FguiModule.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. using FairyGUI;
  2. using Spine.Unity;
  3. using System;
  4. using System.Collections.Generic;
  5. using UnityEngine;
  6. using XGame.Framework.Asset;
  7. using XGame.Framework.Asyncs;
  8. using XGame.Framework.Interfaces;
  9. using XGame.Framework.UI;
  10. namespace XGame.Framework.FGUI
  11. {
  12. internal partial class FguiModule : IUIModule, IUpdate, ILateUpdate, IUIContextSetter, IDisposable, IUIAssetModuleCreator, IPackageHandle
  13. {
  14. /// <summary>
  15. /// bitmapFont的shader名字
  16. /// </summary>
  17. private const string bitmap_font_shader = "XGame/Text Shader";
  18. private UIContext _context;
  19. private IUITree _uitree;
  20. private IAssetModule _assetModule;
  21. /// <summary>
  22. /// 加载中的
  23. /// </summary>
  24. private Dictionary<string, IAsync> _loadingMap;
  25. /// <summary>
  26. /// 已打开的UI
  27. /// </summary>
  28. private Dictionary<string, IUIView> _openedMap;
  29. /// <summary>
  30. /// 已关闭的UI
  31. /// </summary>
  32. private Dictionary<string, IUIView> _closedMap;
  33. /// <summary>
  34. /// Update 和 LateUpdate用
  35. /// </summary>
  36. private List<IUIView> _updates;
  37. /// <summary>
  38. /// 已加载的字体
  39. /// </summary>
  40. private HashSet<string> _fontNames;
  41. public FguiModule(IAssetModule assetModule, IUITree uitree)
  42. {
  43. _assetModule = assetModule;
  44. _uitree = uitree;
  45. _loadingMap = new Dictionary<string, IAsync>();
  46. _openedMap = new Dictionary<string, IUIView>();
  47. _closedMap = new Dictionary<string, IUIView>();
  48. _updates = new List<IUIView>();
  49. _fontNames = new HashSet<string>();
  50. //Gtween的默认曲线
  51. GTweener.defaultEaseType = EaseType.Linear;
  52. #region 只对PackageItem有效
  53. NTexture.CustomDestroyMethod += RecycleTexture;
  54. NAudioClip.CustomDestroyMethod += RecycleAudio;
  55. #if FAIRYGUI_SPINE
  56. GLoader3D.CustomLoadSpine += OnLoadSpine;
  57. GLoader3D.CustomSpineDestroyMethod += RecycleSpine;
  58. #endif
  59. #endregion
  60. UIObjectFactory.SetLoaderExtension(CreatCustomLoader);
  61. }
  62. public UIContext Context { get => _context; set => _context = value; }
  63. #region 接口实现
  64. Camera IUIModule.Camera => _uitree.Camera;
  65. Canvas IUIModule.GetCanvas(UILayer layer)
  66. {
  67. return _uitree.GetCanvas(layer);
  68. }
  69. IAsync IUIModule.OpenAsync(UIKey uikey, object intent)
  70. {
  71. string key = uikey;
  72. if (_loadingMap.ContainsKey(key))
  73. {
  74. return _loadingMap[key];
  75. }
  76. var asyncGroup = new AsyncGroup();
  77. if (_openedMap.ContainsKey(key))
  78. {
  79. Log.Warn($"重复开启UI:{key}");
  80. }
  81. else if (_closedMap.TryGetValue(key, out var closedView))
  82. { // 已关闭的UI
  83. asyncGroup.On(_ =>
  84. {
  85. _closedMap.Remove(key);
  86. _uitree.SetAsLastSibling(closedView.Panel);
  87. closedView.Enable(intent);
  88. _openedMap.Add(key, closedView);
  89. FrameworkEvent.Instance.Notify(EventDefine.UI_OPENED, uikey);
  90. });
  91. }
  92. else
  93. {
  94. _loadingMap.Add(key, asyncGroup);
  95. var objLoadAsync = new GObjectFromPackageAsync(uikey.PackageName, uikey.PanelName);
  96. objLoadAsync.Join(asyncGroup);
  97. objLoadAsync.On(_ =>
  98. {
  99. _loadingMap.Remove(key);
  100. var panelObj = objLoadAsync.Result as GComponent;
  101. if (panelObj == null)
  102. {
  103. Log.Error($"UI加载结果为空. UIKey:{uikey}");
  104. return;
  105. }
  106. var panel = new FguiPanel(panelObj, GetLayer(uikey.UIViewType));
  107. var view = Activator.CreateInstance(uikey.UIViewType) as IUIView;
  108. var context = _context.Clone();
  109. (context as IUIViewAdapter).Key = uikey;
  110. (view as UIView).Init(context, panel);
  111. _uitree.AddPanel(panel);
  112. view.Enable(intent);
  113. _openedMap.Add(key, view);
  114. FrameworkEvent.Instance.Notify(EventDefine.UI_OPENED, uikey);
  115. });
  116. var pkgLoadAsync = LoadPackageAsync(uikey);
  117. if (pkgLoadAsync != null)
  118. {
  119. pkgLoadAsync.Join(asyncGroup);
  120. pkgLoadAsync.On(_ =>
  121. {
  122. objLoadAsync.Start();
  123. });
  124. }
  125. else
  126. {
  127. objLoadAsync.Start();
  128. }
  129. }
  130. asyncGroup.End();
  131. return asyncGroup;
  132. }
  133. void IUIModule.Close(UIKey uikey, bool isDestroy)
  134. {
  135. var key = uikey.Key;
  136. if (_loadingMap.TryGetValue(key, out var async))
  137. {
  138. _loadingMap.Remove(key);
  139. async.RemoveAll();
  140. return;
  141. }
  142. if (_openedMap.TryGetValue(key, out var uiView))
  143. {
  144. _openedMap.Remove(key);
  145. uiView.Disable();
  146. _uitree.RemovePanel(uiView.Panel);
  147. if (isDestroy)
  148. {
  149. DestroyView(uiView);
  150. }
  151. else
  152. {
  153. _closedMap.Add(key, uiView);
  154. }
  155. FrameworkEvent.Instance.Notify(EventDefine.UI_CLOSED, uikey);
  156. return;
  157. }
  158. if (isDestroy && _closedMap.TryGetValue(key, out uiView))
  159. {
  160. _closedMap.Remove(key);
  161. DestroyView(uiView);
  162. }
  163. }
  164. IAsync IUIModule.Preload(UIKey uikey)
  165. {
  166. var loadAsync = LoadPackageAsync(uikey);
  167. return loadAsync;
  168. }
  169. bool IUIModule.IsOpened(UIKey uikey)
  170. {
  171. return _openedMap.ContainsKey(uikey);
  172. }
  173. void IUpdate.Update(int millisecond)
  174. {
  175. if (_openedMap.Count == 0)
  176. return;
  177. _updates.Clear();
  178. _updates.AddRange(_openedMap.Values);
  179. for (var i = 0; i < _updates.Count;)
  180. {
  181. var view = _updates[i];
  182. view.Update(millisecond);
  183. if (view.Active)
  184. {
  185. i++;
  186. }
  187. else
  188. {
  189. _updates.RemoveAt(i);
  190. }
  191. }
  192. }
  193. void ILateUpdate.LateUpdate(int millisecond)
  194. {
  195. if (_updates.Count == 0)
  196. return;
  197. foreach (var uiview in _updates)
  198. {
  199. uiview.LateUpdate(millisecond);
  200. }
  201. _updates.Clear();
  202. }
  203. void IDisposable.Dispose()
  204. {
  205. _updates.Clear();
  206. foreach (var async in _loadingMap.Values)
  207. {
  208. async.RemoveAll();
  209. }
  210. _loadingMap.Clear();
  211. foreach (var uiview in _closedMap.Values)
  212. {
  213. DestroyView(uiview);
  214. }
  215. _closedMap.Clear();
  216. foreach (var uiview in _openedMap.Values)
  217. {
  218. DestroyView(uiview);
  219. }
  220. _openedMap.Clear();
  221. foreach (var fontName in _fontNames)
  222. {
  223. var font = FontManager.UnregisterFont(fontName);
  224. if (font is DynamicFont dynFont)
  225. {
  226. _assetModule.Recycle(dynFont.nativeFont);
  227. }
  228. font?.Dispose();
  229. }
  230. _fontNames.Clear();
  231. #region 清理FGUI緩存
  232. StageEngine.beingQuit = true;
  233. UIPackage.RemoveAllPackages();
  234. Stage.inst.Dispose();
  235. NTexture.CustomDestroyMethod -= RecycleTexture;
  236. NAudioClip.CustomDestroyMethod -= RecycleAudio;
  237. #if FAIRYGUI_SPINE
  238. GLoader3D.CustomLoadSpine -= OnLoadSpine;
  239. GLoader3D.CustomSpineDestroyMethod -= RecycleSpine;
  240. #endif
  241. UIObjectFactory.Clear();
  242. ClearGloaderAssets();
  243. #endregion
  244. (_uitree as IDisposable)?.Dispose();
  245. (_assetModule as IDisposable)?.Dispose();
  246. _assetModule = null;
  247. _context = null;
  248. }
  249. #endregion
  250. #region IPackageHandle 接口实现
  251. public IGObjectLoadAsync LoadGobjectAsync(UIKey uikey)
  252. {
  253. var objLoadAsync = new GObjectFromPackageAsync(uikey.PackageName, uikey.PanelName);
  254. var pkgLoadAsync = LoadPackageAsync(uikey);
  255. if (pkgLoadAsync != null)
  256. {
  257. pkgLoadAsync.On(_ =>
  258. {
  259. objLoadAsync.Start();
  260. });
  261. }
  262. else
  263. {
  264. objLoadAsync.Start();
  265. }
  266. return objLoadAsync;
  267. }
  268. /// <summary>
  269. /// 加载UIPackage
  270. /// </summary>
  271. /// <param name="uikey"></param>
  272. /// <returns></returns>
  273. public IAsync LoadPackageAsync(UIKey uikey)
  274. {
  275. if (UIPackage.GetByName(uikey.PackageName) == null)
  276. {
  277. var pkgAddressable = FguiUtils.ToDescFileName(uikey.PackageName);
  278. if (_loadingMap.ContainsKey(pkgAddressable))
  279. {
  280. return _loadingMap[pkgAddressable];
  281. }
  282. var pkgLoadAsync = _assetModule.LoadAsync<TextAsset>(pkgAddressable);
  283. _loadingMap.Add(pkgAddressable, pkgLoadAsync);
  284. pkgLoadAsync.On(_ =>
  285. {
  286. _loadingMap.Remove(pkgAddressable);
  287. var pkgAsset = pkgLoadAsync.Result;
  288. var descData = pkgAsset?.bytes;
  289. _assetModule.Recycle(pkgAsset);
  290. if (descData == null || descData.Length == 0)
  291. {
  292. Log.Error($"UIPackage 加载失败. UIKey:{uikey}");
  293. return;
  294. }
  295. if (UIPackage.GetByName(uikey.PackageName) == null)
  296. {
  297. UIPackage.AddPackage(descData, string.Empty, LoadPackageItemAsync);
  298. //var pkg = UIPackage.GetByName(uikey.PackageName);
  299. //if (pkg != null && pkg.dependencies != null)
  300. //{
  301. // Log.Debug($"{uikey.PackageName} dependencies:{pkg.dependencies.Length}");
  302. // foreach (var dependency in pkg.dependencies)
  303. // {
  304. // foreach(var item in dependency)
  305. // {
  306. // Log.Debug($"dependency key:{item.Key} value:{item.Value}");
  307. // }
  308. // }
  309. //}
  310. }
  311. });
  312. return pkgLoadAsync;
  313. }
  314. else
  315. {
  316. Log.Info($"UIPackage已加载. UIKey:{uikey}");
  317. return null;
  318. }
  319. }
  320. public IAsync LoadFontAsync(string fontName)
  321. {
  322. if (_fontNames.Contains(fontName))
  323. {
  324. return null;
  325. }
  326. if (_loadingMap.TryGetValue(fontName, out var loading))
  327. {
  328. return loading;
  329. }
  330. var async = _assetModule.LoadAsync<Font>(fontName);
  331. _loadingMap.Add(fontName, async);
  332. async.On(_ =>
  333. {
  334. _loadingMap.Remove(fontName);
  335. var result = async.Result;
  336. if (result == null)
  337. return;
  338. _fontNames.Add(fontName);
  339. var fguiFont = new DynamicFont()
  340. {
  341. name = fontName,
  342. nativeFont = result
  343. };
  344. if (result.material != null && result.material.shader.name == bitmap_font_shader)
  345. { // bitmapfont字体使用自带的shader
  346. fguiFont.shader = bitmap_font_shader;
  347. }
  348. FontManager.RegisterFont(fguiFont);
  349. });
  350. return async;
  351. }
  352. #endregion
  353. IUIAssetModule IUIAssetModuleCreator.Create(UIContext context)
  354. {
  355. return new FguiAssetModule(context, this);
  356. }
  357. #region 内部函数
  358. /// <summary>
  359. /// 异步加载PackageItem
  360. /// UIPackage的Texture、AudioClip等资源
  361. /// </summary>
  362. /// <param name="name"></param>
  363. /// <param name="extension"></param>
  364. /// <param name="type"></param>
  365. /// <param name="item"></param>
  366. private void LoadPackageItemAsync(string name, string extension, System.Type type, PackageItem item)
  367. {
  368. Log.Info($"LoadPackageItemAsync name:{name} extension:{extension} type:{type} itemName:{item.name}");
  369. var addressable = $"{item.owner.name}_{name}";
  370. var loadAsync = _assetModule.LoadAsync<UnityEngine.Object>(addressable);
  371. loadAsync.On(_ =>
  372. {
  373. var asset = loadAsync.Result;
  374. item.owner.SetItemAsset(item, Convert(asset, type), DestroyMethod.Custom);
  375. });
  376. }
  377. private object Convert(UnityEngine.Object value, Type type)
  378. {
  379. if (value == null)
  380. return default;
  381. if (type.IsInstanceOfType(value))
  382. {
  383. return value;
  384. }
  385. if (type.IsClass)
  386. {
  387. if (type.IsSubclassOf(typeof(Component)))
  388. {
  389. var go = value as GameObject;
  390. if (go != null)
  391. {
  392. return go.GetComponent(type);
  393. }
  394. var component = value as Component;
  395. if (component != null)
  396. {
  397. return component.gameObject.GetComponent(type);
  398. }
  399. }
  400. else if (type == typeof(Sprite) && value is Texture2D texture)
  401. {
  402. object sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
  403. return sprite;
  404. }
  405. }
  406. Log.Error($"UIModuleFGUI object conver type Error. source type:{value.GetType()} name:{value} target:{type}");
  407. return value;
  408. }
  409. private void DestroyView(IUIView view)
  410. {
  411. var uiPanel = (view.Panel as FguiPanel).Panel;
  412. (view as IDisposable).Dispose();
  413. uiPanel.Dispose();
  414. }
  415. /// <summary>
  416. /// 只对LoadPackageItemAsync加载的资源有效
  417. /// </summary>
  418. /// <param name="texture"></param>
  419. private void RecycleTexture(Texture texture)
  420. {
  421. Log.Info($"RecycleTexture Texture:{texture.name}");
  422. _assetModule.Recycle(texture);
  423. }
  424. /// <summary>
  425. /// 只对LoadPackageItemAsync加载的资源有效
  426. /// </summary>
  427. /// <param name="audioClip"></param>
  428. private void RecycleAudio(AudioClip audioClip)
  429. {
  430. Log.Info($"RecycleAudio AudioClip:{audioClip.name}");
  431. _assetModule?.Recycle(audioClip);
  432. }
  433. private GLoader CreatCustomLoader()
  434. {
  435. return new CustomLoader(this);
  436. }
  437. private void OnLoadSpine(string addressable, Action<SkeletonDataAsset> action)
  438. {
  439. var loadAsync = _assetModule.LoadAsync<SkeletonDataAsset>(addressable);
  440. loadAsync.On(_ =>
  441. {
  442. var data = loadAsync.Result;
  443. if (data == null)
  444. {
  445. return;
  446. }
  447. action(data);
  448. });
  449. }
  450. private void RecycleSpine(SkeletonDataAsset asset)
  451. {
  452. _assetModule.Recycle(asset);
  453. }
  454. private UILayer GetLayer(Type uiviewType)
  455. {
  456. var layer = UILayer.Normal;
  457. var property = uiviewType.GetProperty("Layer", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.IgnoreCase);
  458. if (property != null)
  459. {
  460. layer = (UILayer)property.GetValue(null);
  461. }
  462. return layer;
  463. }
  464. #endregion
  465. }
  466. }