MapUIComponent.cs 12 KB

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