MapUIComponent.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. using FairyGUI;
  2. using FL.Battle;
  3. using FL.Map.UI;
  4. using FL.Nodes.GameMain;
  5. using System.Collections.Generic;
  6. using UnityEngine;
  7. using XGame;
  8. using XGame.Database;
  9. using XGame.Framework;
  10. using XGame.Framework.Asyncs;
  11. using XGame.Framework.Components;
  12. using XGame.Framework.Interfaces;
  13. using XGame.Framework.Map;
  14. using XGame.Framework.Time;
  15. namespace FL.Map.TestMainMap
  16. {
  17. public interface IMapUIContext
  18. {
  19. IMapAssetModule Asset { get; }
  20. ITimeModule Time { get; }
  21. TestMainMapVM VM { get; }
  22. }
  23. /// <summary>
  24. /// 地图用UI
  25. /// </summary>
  26. public class MapUIComponent : Component<IMapUIContext>, ILateUpdate
  27. {
  28. private MainMapUIView _uiView;
  29. private IAsync _uiViewAsync;
  30. /// <summary>
  31. /// 玩家、怪物血条
  32. /// </summary>
  33. private Dictionary<long, IEntityBattleInfoView> _entityViewsMap = new();
  34. /// <summary>
  35. /// 飘血计时
  36. /// 同一个Entity的飘血需要有200毫秒间隔
  37. /// </summary>
  38. private Dictionary<long, long> _hpAddLastTimeMap = new Dictionary<long, long>();
  39. private HashSet<ITimer> _hpAddTimers = new();
  40. /// <summary>
  41. /// TODO
  42. /// </summary>
  43. private Camera UICamera => StageCamera.main;
  44. private int _chapterId;
  45. private bool _isMasking;
  46. protected override void OnEnable(object intent)
  47. {
  48. EventSingle.Instance.AddListener(EventDefine.GameMainMapRemoveEntity, OnRemoveEntity);
  49. EventSingle.Instance.AddListener(EventDefine.GameMainMapStartMask, OnStartMask);
  50. EventSingle.Instance.AddListener(EventDefine.GameMainMapEndMask, OnEndMask);
  51. EventSingle.Instance.AddListener(EventDefine.GameMainMapHpAdd, OnHpAdd);
  52. LoadUIView();
  53. }
  54. protected override void OnDisable()
  55. {
  56. EventSingle.Instance.RemoveListener(EventDefine.GameMainMapRemoveEntity, OnRemoveEntity);
  57. EventSingle.Instance.RemoveListener(EventDefine.GameMainMapStartMask, OnStartMask);
  58. EventSingle.Instance.RemoveListener(EventDefine.GameMainMapEndMask, OnEndMask);
  59. EventSingle.Instance.RemoveListener(EventDefine.GameMainMapHpAdd, OnHpAdd);
  60. if (_uiView != null)
  61. {
  62. Context.Asset.Recycle(_uiView);
  63. _uiView = null;
  64. }
  65. if (_uiViewAsync != null)
  66. {
  67. _uiViewAsync.RemoveAll();
  68. _uiViewAsync = null;
  69. }
  70. ClearEntities(true);
  71. _chapterId = 0;
  72. _isMasking = false;
  73. }
  74. protected override void OnDispose()
  75. {
  76. }
  77. void ILateUpdate.LateUpdate(int millisecond)
  78. {
  79. if (_entityViewsMap.Count > 0)
  80. {
  81. var cameraMoving = Context.VM.Camera.IsMoving;
  82. foreach (var view in _entityViewsMap.Values)
  83. {
  84. if ((cameraMoving || view.Master.IsMoving) && view.IsMoveable)
  85. {
  86. view.LocalPosition = WorldToLocal(view.Master.UIPoint.position, _uiView.PlayerRoot);
  87. }
  88. view.RefreshBuffs();
  89. }
  90. }
  91. }
  92. /// <summary>
  93. /// 世界坐标转指定UI节点的相对坐标
  94. /// </summary>
  95. /// <param name="position"></param>
  96. /// <param name="parent"></param>
  97. /// <returns></returns>
  98. private Vector2 WorldToLocal(Vector3 position, Transform parent)
  99. {
  100. var screenPoint = Context.VM.Camera.WorldToScreenPoint(position);
  101. RectTransformUtility.ScreenPointToLocalPointInRectangle(parent as RectTransform, screenPoint, UICamera, out var localPoint);
  102. return localPoint;
  103. }
  104. public void AddEntity(ITarget master)
  105. {
  106. if (_uiView == null)
  107. {
  108. if (_uiViewAsync != null)
  109. {
  110. _uiViewAsync.On(_ =>
  111. {
  112. LoadEntityView(master);
  113. });
  114. }
  115. else
  116. {
  117. Log.Error($"MainMapUI不能为空.");
  118. }
  119. }
  120. else
  121. {
  122. LoadEntityView(master);
  123. }
  124. }
  125. public void AddBoss(ITarget master, int chapterId)
  126. {
  127. Assert.IsNotNull(_uiView, $"MainMapUI不能为空.");
  128. LoadEntityView(master, chapterId);
  129. }
  130. public void RefreshEntity(long entityId)
  131. {
  132. if (_entityViewsMap.TryGetValue(entityId, out var view))
  133. {
  134. view.RefreshHp();
  135. }
  136. }
  137. private void LoadEntityView(ITarget master, int chapterId = 0)
  138. {
  139. if (master.Entity.EntityType == EEntityType.Monster)
  140. {
  141. var table = MonsterTableRepo.Get(master.Entity.TableId);
  142. if (table.Type == 2)
  143. {
  144. var viewAsync = Context.Asset.LoadEntity<BossBattleInfoView>(AddressableDefine.bossbattleinfo, _uiView.BossRoot);
  145. viewAsync.On(_ =>
  146. {
  147. var view = viewAsync.Result;
  148. if (view == null)
  149. return;
  150. OnLoaded(master, view);
  151. view.IsMoveable = false;
  152. view.LocalPosition = Vector3.zero;
  153. view.RefreshTimeLimit(chapterId);
  154. });
  155. }
  156. else
  157. {
  158. var viewAsync = Context.Asset.LoadEntity<MonsterBattleInfoView>(AddressableDefine.monsterbattleinfo, _uiView.MonsterRoot);
  159. viewAsync.On(_ =>
  160. {
  161. var view = viewAsync.Result;
  162. if (view == null)
  163. return;
  164. OnLoaded(master, view);
  165. });
  166. }
  167. }
  168. else
  169. {
  170. var viewAsync = Context.Asset.LoadEntity<PlayerBattleInfoView>(AddressableDefine.playerbattleinfo, _uiView.PlayerRoot);
  171. viewAsync.On(_ =>
  172. {
  173. var view = viewAsync.Result;
  174. if (view == null)
  175. return;
  176. OnLoaded(master, view);
  177. });
  178. }
  179. }
  180. void OnLoaded(ITarget master, IEntityBattleInfoView view)
  181. {
  182. if (master.IsDead)
  183. {
  184. Log.Debug($"EntityBattleInfoView 加载完成,Entity已经死亡. Id:{master.Entity.EntityId}");
  185. Context.Asset.Recycle(view);
  186. return;
  187. }
  188. view.Master = master;
  189. view.IsMoveable = true;
  190. view.LocalPosition = WorldToLocal(master.UIPoint.position, _uiView.PlayerRoot);
  191. _entityViewsMap.Add(view.Master.Entity.EntityId, view);
  192. }
  193. private void OnRemoveEntity(int eventId, object args)
  194. {
  195. var entityId = (long)args;
  196. if (_entityViewsMap.TryGetValue(entityId, out var view))
  197. {
  198. if (view.Master.Entity.EntityType == EEntityType.Player)
  199. { // 玩家的血条不移除
  200. return;
  201. }
  202. _entityViewsMap.Remove(entityId);
  203. Context.Asset.Recycle(view);
  204. }
  205. _hpAddLastTimeMap.Remove(entityId);
  206. }
  207. private void OnStartMask(int eventId, object args)
  208. {
  209. var sign = (EMapCombatSign)args; //0表示战斗胜利,1表示普通战斗失败,2表示boss战斗失败
  210. if ( sign != EMapCombatSign.Success)
  211. { // 玩家死亡,失败,移除所有怪物的血条
  212. ClearEntities(false);
  213. if (sign is EMapCombatSign.FailedBoss)
  214. {
  215. _uiView?.SetBossFailed(true);
  216. }
  217. }
  218. _isMasking = true;
  219. }
  220. private void OnEndMask(int eventId, object args)
  221. {
  222. //var sign = (EMapCombatSign)args;
  223. _isMasking = false;
  224. }
  225. private void OnHpAdd(int eventId, object args)
  226. {
  227. PlayHpAdd((HpAddDto)args);
  228. }
  229. private void ClearEntities(bool includePlayer)
  230. {
  231. IEntityBattleInfoView player = null;
  232. foreach (var entity in _entityViewsMap.Values)
  233. {
  234. if (!includePlayer && entity.Master.Entity.EntityType == EEntityType.Player)
  235. {
  236. player = entity;
  237. continue;
  238. }
  239. Context.Asset.Recycle(entity);
  240. }
  241. _entityViewsMap.Clear();
  242. _hpAddLastTimeMap.Clear();
  243. foreach(var timer in _hpAddTimers)
  244. {
  245. timer.Cancel();
  246. }
  247. _hpAddTimers.Clear();
  248. if (player != null)
  249. {
  250. _entityViewsMap.Add(player.Master.Entity.EntityId, player);
  251. }
  252. }
  253. private void PlayHpAdd(HpAddDto dto)
  254. {
  255. if (_uiView == null || _isMasking)
  256. return;
  257. RefreshEntity(dto.entityId);
  258. SaveHpAddTime(dto.entityId, out var delay);
  259. if (delay > 0)
  260. {
  261. ITimer timer = null;
  262. timer = Context.Time.AddDelayTimer(delay, () =>
  263. {
  264. _hpAddTimers.Remove(timer);
  265. LoadHpAddView(dto);
  266. });
  267. _hpAddTimers.Add(timer);
  268. }
  269. else
  270. {
  271. LoadHpAddView(dto);
  272. }
  273. }
  274. private void LoadHpAddView(HpAddDto dto)
  275. {
  276. var viewAsync = Context.Asset.LoadEntity<HpAddEntityView>(AddressableDefine.uihpadd, _uiView.CombatValRoot);
  277. viewAsync.On(_ =>
  278. {
  279. var hpAddView = viewAsync.Result;
  280. if (hpAddView == null)
  281. return;
  282. var localPoint = WorldToLocal(dto.position, _uiView.CombatValRoot);
  283. //var screenPoint = Context.VM.Camera.WorldToScreenPoint(dto.position);
  284. //RectTransformUtility.ScreenPointToLocalPointInRectangle(_uiView.CombatValRoot as RectTransform, screenPoint, UICamera, out var localPoint);
  285. hpAddView?.ShowValue(dto.hpValue, localPoint, dto.elementType, () =>
  286. {
  287. Context.Asset.Recycle(hpAddView);
  288. });
  289. });
  290. }
  291. /// <summary>
  292. /// 保存时间
  293. /// </summary>
  294. /// <param name="entityId"></param>
  295. /// <param name="delay">延迟播放时间,单位:毫秒</param>
  296. private void SaveHpAddTime(long entityId, out int delay)
  297. {
  298. delay = 0;
  299. var curTime = Context.Time.GetNowTime();
  300. if (_hpAddLastTimeMap.TryGetValue(entityId, out var lastTime))
  301. {
  302. const int interval = 200;
  303. var playTime = lastTime + interval;
  304. if (playTime > curTime)
  305. {
  306. lastTime = playTime;
  307. delay = (int)(playTime - curTime);
  308. }
  309. else
  310. {
  311. lastTime = curTime;
  312. }
  313. }
  314. else
  315. {
  316. lastTime = curTime;
  317. }
  318. _hpAddLastTimeMap[entityId] = lastTime;
  319. }
  320. private void LoadUIView()
  321. {
  322. var loadAsync = Context.Asset.LoadEntity<MainMapUIView>(AddressableDefine.mainmapui, Context.VM.UIRoot);
  323. _uiViewAsync = loadAsync;
  324. loadAsync.On(_ =>
  325. {
  326. _uiViewAsync = null;
  327. var uiView = loadAsync.Result;
  328. if (uiView == null)
  329. return;
  330. _uiView = uiView;
  331. uiView.RefreshChapter(_chapterId);
  332. });
  333. }
  334. #region 副本进度相关
  335. public void RefreshChapter(int chapterId)
  336. {
  337. if (_chapterId == chapterId) return;
  338. _chapterId = chapterId;
  339. _uiView?.RefreshChapter(chapterId);
  340. }
  341. #endregion
  342. }
  343. }