using FairyGUI; using FL.Battle; using FL.Map.UI; using FL.Nodes.GameMain; using System.Collections.Generic; using UnityEngine; using XGame; using XGame.Database; using XGame.Framework.Asyncs; using XGame.Framework.Components; using XGame.Framework.Interfaces; using XGame.Framework.Map; using XGame.Framework.Time; namespace FL.Map.TestMainMap { public interface IMapUIContext { IMapAssetModule Asset { get; } ITimeModule Time { get; } TestMainMapVM VM { get; } } /// /// 地图用UI /// public class MapUIComponent : Component, ILateUpdate { private MainMapUIView _uiView; private IAsync _uiViewAsync; /// /// 玩家、怪物血条 /// private Dictionary _entityViewsMap = new Dictionary(); /// /// 飘血计时 /// 同一个Entity的飘血需要有200毫秒间隔 /// private Dictionary _hpAddLastTimeMap = new Dictionary(); private HashSet _hpAddTimers = new(); /// /// TODO /// private Camera UICamera => StageCamera.main; private int _chapterId; private bool _isMasking; protected override void OnEnable(object intent) { EventSingle.Instance.AddListener(EventDefine.GameMainMapRemoveEntity, OnRemoveEntity); EventSingle.Instance.AddListener(EventDefine.GameMainMapStartMask, OnStartMask); EventSingle.Instance.AddListener(EventDefine.GameMainMapEndMask, OnEndMask); EventSingle.Instance.AddListener(EventDefine.GameMainMapHpAdd, OnHpAdd); EventSingle.Instance.AddListener(EventDefine.GameMainMapBossStart, OnBossStart); LoadUIView(); } protected override void OnDisable() { EventSingle.Instance.RemoveListener(EventDefine.GameMainMapRemoveEntity, OnRemoveEntity); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapStartMask, OnStartMask); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapEndMask, OnEndMask); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapHpAdd, OnHpAdd); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapBossStart, OnBossStart); if (_uiView != null) { Context.Asset.Recycle(_uiView); _uiView = null; } if (_uiViewAsync != null) { _uiViewAsync.RemoveAll(); _uiViewAsync = null; } ClearEntities(true); _chapterId = 0; _isMasking = false; } protected override void OnDispose() { } void ILateUpdate.LateUpdate(int millisecond) { if (_entityViewsMap.Count > 0) { var cameraMoving = Context.VM.Camera.IsMoving; foreach (var view in _entityViewsMap.Values) { if ((cameraMoving || view.Master.IsMoving) && view.IsMoveable) { view.LocalPosition = WorldToLocal(view.Master.UIPoint.position, _uiView.PlayerRoot); } view.RefreshBuffs(); } } _uiView?.LateUpdate(millisecond); } /// /// 世界坐标转指定UI节点的相对坐标 /// /// /// /// private Vector2 WorldToLocal(Vector3 position, Transform parent) { var screenPoint = Context.VM.Camera.WorldToScreenPoint(position); RectTransformUtility.ScreenPointToLocalPointInRectangle(parent as RectTransform, screenPoint, UICamera, out var localPoint); return localPoint; } public void AddEntity(ITarget master) { if (_uiView == null) { if (_uiViewAsync != null) { _uiViewAsync.On(_ => { LoadEntityView(master); }); } else { Log.Error($"MainMapUI不能为空."); } } else { LoadEntityView(master); } } public void RefreshEntity(long entityId) { if (_entityViewsMap.TryGetValue(entityId, out var view)) { view.RefreshHp(); } } private void LoadEntityView(ITarget master) { GetEntityAddressable(master, out var addressable, out var parent, out var isBoss); var viewAsync = Context.Asset.LoadEntity(addressable, parent); viewAsync.On(_ => { var view = viewAsync.Result; if (view == null) return; if (master.IsDead) { Log.Debug($"EntityBattleInfoView 加载完成,Entity已经死亡. Id:{master.Entity.EntityId}"); Context.Asset.Recycle(view); return; } view.Master = master; view.IsMoveable = !isBoss; view.LocalPosition = isBoss ? Vector3.zero : WorldToLocal(master.UIPoint.position, _uiView.PlayerRoot); _entityViewsMap.Add(view.Master.Entity.EntityId, view); }); } private void GetEntityAddressable(ITarget master, out string addressableName, out Transform parent, out bool isBoss) { isBoss = false; if (master.Entity.EntityType == EEntityType.Monster) { var table = MonsterTableRepo.Get(master.Entity.TableId); if (table.Type == 2) { addressableName = AddressableDefine.bossbattleinfo; parent = _uiView.BossRoot; isBoss = true; } else { addressableName = AddressableDefine.monsterbattleinfo; parent = _uiView.MonsterRoot; } } else { addressableName = AddressableDefine.playerbattleinfo; parent = _uiView.PlayerRoot; } } private void OnRemoveEntity(int eventId, object args) { var entityId = (long)args; if (_entityViewsMap.TryGetValue(entityId, out var view)) { if (view.Master.Entity.EntityType == EEntityType.Player) { // 玩家的血条不移除 return; } _entityViewsMap.Remove(entityId); Context.Asset.Recycle(view); } _hpAddLastTimeMap.Remove(entityId); } private void OnStartMask(int eventId, object args) { var sign = (EMapCombatSign)args; //0表示战斗胜利,1表示普通战斗失败,2表示boss战斗失败 if ( sign != EMapCombatSign.Success) { // 玩家死亡,失败,移除所有怪物的血条 ClearEntities(false); if (sign is EMapCombatSign.FailedBoss) { _uiView?.SetBossFailed(true); } } _isMasking = true; } private void OnEndMask(int eventId, object args) { //var sign = (EMapCombatSign)args; _isMasking = false; } private void OnHpAdd(int eventId, object args) { PlayHpAdd((HpAddDto)args); } private void OnBossStart(int eventId, object args) { //var chapterId = (int)args; _uiView?.StartBossTime(); } private void ClearEntities(bool includePlayer) { EntityBattleInfoView player = null; foreach (var entity in _entityViewsMap.Values) { if (!includePlayer && entity.Master.Entity.EntityType == EEntityType.Player) { player = entity; continue; } Context.Asset.Recycle(entity); } _entityViewsMap.Clear(); _hpAddLastTimeMap.Clear(); foreach(var timer in _hpAddTimers) { timer.Cancel(); } _hpAddTimers.Clear(); if (player != null) { _entityViewsMap.Add(player.Master.Entity.EntityId, player); } } private void PlayHpAdd(HpAddDto dto) { if (_uiView == null || _isMasking) return; RefreshEntity(dto.entityId); SaveHpAddTime(dto.entityId, out var delay); if (delay > 0) { ITimer timer = null; timer = Context.Time.AddDelayTimer(delay, () => { _hpAddTimers.Remove(timer); LoadHpAddView(dto); }); _hpAddTimers.Add(timer); } else { LoadHpAddView(dto); } } private void LoadHpAddView(HpAddDto dto) { var viewAsync = Context.Asset.LoadEntity(AddressableDefine.uihpadd, _uiView.CombatValRoot); viewAsync.On(_ => { var hpAddView = viewAsync.Result; if (hpAddView == null) return; var localPoint = WorldToLocal(dto.position, _uiView.CombatValRoot); //var screenPoint = Context.VM.Camera.WorldToScreenPoint(dto.position); //RectTransformUtility.ScreenPointToLocalPointInRectangle(_uiView.CombatValRoot as RectTransform, screenPoint, UICamera, out var localPoint); hpAddView?.ShowValue(dto.hpValue, localPoint, dto.elementType, () => { Context.Asset.Recycle(hpAddView); }); }); } /// /// 保存时间 /// /// /// 延迟播放时间,单位:毫秒 private void SaveHpAddTime(long entityId, out int delay) { delay = 0; var curTime = Context.Time.GetNowTime(); if (_hpAddLastTimeMap.TryGetValue(entityId, out var lastTime)) { const int interval = 200; var playTime = lastTime + interval; if (playTime > curTime) { lastTime = playTime; delay = (int)(playTime - curTime); } else { lastTime = curTime; } } else { lastTime = curTime; } _hpAddLastTimeMap[entityId] = lastTime; } private void LoadUIView() { var loadAsync = Context.Asset.LoadEntity(AddressableDefine.mainmapui, Context.VM.UIRoot); _uiViewAsync = loadAsync; loadAsync.On(_ => { _uiViewAsync = null; var uiView = loadAsync.Result; if (uiView == null) return; _uiView = uiView; uiView.RefreshChapter(_chapterId); }); } #region 副本进度相关 public void RefreshChapter(int chapterId) { if (_chapterId == chapterId) return; _chapterId = chapterId; _uiView?.RefreshChapter(chapterId); } #endregion } }