using FL.Battle; using FL.Battle.Components; using FL.Battle.Skills; using FL.Data; using FL.Nodes.GameMain; using System; using System.Collections.Generic; using UnityEngine; using XGame; using XGame.Database; using XGame.Framework.Asyncs; using XGame.Framework.Components; using XGame.Framework.Map; using XGame.Framework.Time; namespace FL.Map.TestMainMap { public interface IEntitiesContext { IMapAssetModule Asset { get; } ITimeModule Time { get; } TestMainMapVM VM { get; } VfxComponent Vfx { get; } MapUIComponent MapUI { get; } } public partial class EntitiesComponent : Component { /// /// 怪物的位置相对于玩家战斗位置的偏移值 /// private const float MONSTER_POSITION_OFFSET = 6; private PlayerEntityView _player; private Dictionary _partnerMap = new(); private Dictionary _monsterMap = new(); /// /// 地图实例对象,怪物、陷阱、召唤兽等 /// key:UID /// private Dictionary _entityViewsMap = new(); private TargetSelector _selector; private ITimer _selectorTimer; protected override void OnEnable(object intent) { InitSelector(); EventSingle.Instance.AddListener(EventDefine.GameMainMapLoadEntities, OnLoadEntities); EventSingle.Instance.AddListener(EventDefine.GameMainMapRemoveEntity, OnRemoveEntity); EventSingle.Instance.AddListener(EventDefine.GameMainMapRemoveSimpleEntity, OnRemoveSimpleEntity); EventSingle.Instance.AddListener(EventDefine.GameMainMapStartMask, OnStartMask); EventSingle.Instance.AddListener(EventDefine.GameMainMapGenBullet, OnGenBullet); EventSingle.Instance.AddListener(EventDefine.GameMainMapGenCommons, OnGenCommons); EventSingle.Instance.AddListener(EventDefine.GameMainMapAddBuff, OnAddBuff); EventSingle.Instance.AddListener(EventDefine.GameMainMapRemoveBuff, OnRemoveBuff); EventSingle.Instance.AddListener(EventDefine.GameMainMapReqPreviewBuffs, OnRequestBuffList); } protected override void OnDisable() { EventSingle.Instance.RemoveListener(EventDefine.GameMainMapLoadEntities, OnLoadEntities); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapRemoveEntity, OnRemoveEntity); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapRemoveSimpleEntity, OnRemoveSimpleEntity); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapStartMask, OnStartMask); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapGenBullet, OnGenBullet); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapGenCommons, OnGenCommons); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapAddBuff, OnAddBuff); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapRemoveBuff, OnRemoveBuff); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapReqPreviewBuffs, OnRequestBuffList); ClearSelector(); Context.VM.Camera.Target = null; Context.Asset.Recycle(_player); foreach (var partner in _partnerMap.Values) { Context.Asset.Recycle(partner); } _partnerMap.Clear(); foreach (var monster in _monsterMap.Values) { Context.Asset.Recycle(monster); } _monsterMap.Clear(); foreach (var entity in _entityViewsMap.Values) { Context.Asset.Recycle(entity); } _entityViewsMap.Clear(); ClearElementBuffs(); } protected override void OnDispose() { } #region 事件监听 private void OnLoadEntities(int eventId, object args) { var dto = (LoadEntitiesDto)args; if (dto.isPlayerMove) { ClearVfxs(); } Context.MapUI.RefreshChapter(dto.chapterId); var asyncGroup = new AsyncGroup(); if (dto.player != null) { // 第一波怪物,玩家不存在时 var playerAsync = Context.Asset.LoadEntity(AddressableDefine.Player, Context.VM.PlayerRoot); playerAsync.On(_ => { var entityView = playerAsync.Result; if (entityView == null) { return; } entityView.Entity = dto.player; Context.VM.Camera.Target = entityView.Move;// behaviour.transform; _player = entityView; _selector.Register(entityView); entityView.Selector = _selector; Context.MapUI.AddEntity(entityView); }); playerAsync.Join(asyncGroup); } else if (dto.isChapterChanged) { Context.VM.Reset(); _player.Reset(); Context.MapUI.RefreshEntity(_player.Entity.EntityId); foreach (var item in _partnerMap) { item.Value.Reset(); } } else if (dto.isBossPart) { // 重置玩家的血量 _player.Reset(true); Context.MapUI.RefreshEntity(_player.Entity.EntityId); } var partnerCount = dto.partners?.Length ?? 0; if (partnerCount > 0) { // 创建伙伴实例 for (var i = 0; i < partnerCount; i++) { var partner = dto.partners[i]; var table = PartnerTableRepo.Get(partner.TableId); var loadAsync = Context.Asset.LoadEntity(table.Model, Context.VM.PlayerRoot); loadAsync.On(_ => { var entityView = loadAsync.Result; if (entityView == null) { return; } entityView.Entity = partner; entityView.BodyMirror(); _partnerMap.Add(partner.EntityId, entityView); _selector.Register(entityView); entityView.Selector = _selector; }); loadAsync.Join(asyncGroup); } } var monsterCount = dto.monsters?.Length ?? 0; if (monsterCount > 0) { // 创建怪物实例 for (var i = 0; i < monsterCount; i++) { var monster = dto.monsters[i]; var table = MonsterTableRepo.Get(monster.TableId); var loadAsync = Context.Asset.LoadEntity(table.Model, Context.VM.EnemyRoot); loadAsync.On(_ => { var entityView = loadAsync.Result; if (entityView == null) { return; } entityView.Entity = monster; entityView.Entity = monster; _monsterMap.Add(monster.EntityId, entityView); _selector.Register(entityView); entityView.Selector = _selector; if (table.Type == 1) { //boss的血条等boss移动到战斗位置再加载 Context.MapUI.AddEntity(entityView); } }); loadAsync.Join(asyncGroup); } } asyncGroup.On(_ => { //Entities加载完成,开始移动 if (dto.isPlayerMove) { _player.Reset(true); var moveArgs = new MoveArgs() { target = Context.VM.PlayerRoot.TransformPoint(dto.battlePosition) }; _player.CommonAI.MoveToBattle(moveArgs, () => { if (dto.isChapterChanged) { Context.VM.SetFocusPosition(_player.Position); } }); foreach (var item in _partnerMap) { var entityView = item.Value; var entity = entityView.Entity; var tarPosition = dto.battlePosition; tarPosition += PartnersData.Instance.GetPartnerOffset(entity.Attr.UID); // 本地坐标转世界坐标 tarPosition = Context.VM.PlayerRoot.TransformPoint(tarPosition); entityView.Reset(true); moveArgs = new MoveArgs() { target = tarPosition, }; entityView.CommonAI.MoveToBattle(moveArgs); } } else if (partnerCount > 0) { for (var pIdx = 0; pIdx < partnerCount; pIdx++) { var entity = dto.partners[pIdx]; var entityView = _partnerMap[entity.EntityId]; entityView.CommonAI.PrepareBattle(); } } //var monsterCount = dto.monsters?.Length ?? 0; for (var mIdx = 0; mIdx < monsterCount; mIdx++) { var entity = dto.monsters[mIdx]; //var monsterTable = MonsterTableRepo.Get(entity.TableId); var tarPosition = entity.BirthPosition; tarPosition.y += (-dto.monsterMoveDistance + MONSTER_POSITION_OFFSET); tarPosition = Context.VM.EnemyRoot.TransformPoint(tarPosition); var entityView = _monsterMap[entity.EntityId]; var moveArgs = new MoveArgs() { target = tarPosition, delay = entity.BirthDelay, }; if (entity.BirthCurve > 0) { moveArgs.moveType = EMoveType.Curve; moveArgs.curve = Context.VM.MapPathCurve.GetCurve(entity.BirthCurve - 1); moveArgs.curveOffset = new Vector3(3, 0, 0); } entityView.CommonAI.MoveToBattle(moveArgs, () => { if (entity.IsBoss) { Context.MapUI.AddBoss(entityView, dto.chapterId); } }); } }); asyncGroup.End(); } private void OnRemoveEntity(int eventId, object args) { var entityId = (long)args; if (_monsterMap.TryGetValue(entityId, out var monster)) { _monsterMap.Remove(entityId); _selector?.Unregister(monster); Context.Asset.Recycle(monster); RemoveElementBuff(entityId); return; } if (_partnerMap.TryGetValue(entityId, out var partner)) { _partnerMap.Remove(entityId); _selector?.Unregister(partner); Context.Asset.Recycle(partner); return; } //if (entityId == _player?.Entity.EntityId) //{ // 玩家死亡只设置状态,不移除 // _selector?.Unregister(_player); // Context.Asset.Recycle(_player); // _player = null; // Context.VM.Camera.Target = null; //} } private void OnRemoveSimpleEntity(int eventId, object args) { var entityId = (long)args; if (_entityViewsMap.TryGetValue(entityId, out var entityView)) { _entityViewsMap.Remove(entityId); Context.Asset.Recycle(entityView); } } #endregion #region 目标选择 private void InitSelector() { if (_selector == null) { _selector = new TargetSelector(); } _selectorTimer = Context.Time.AddLooperTimer(100, _selector.OnUpdate); } private void ClearSelector() { _selector?.Clear(); _selectorTimer?.Cancel(); _selectorTimer = null; } #endregion #region 黑屏 private void OnStartMask(int eventId, object args) { var sign = (EMapCombatSign)args; var duration = 2; ClearVfxs(); if (sign is EMapCombatSign.Success) { PlayerMoveToCenter(duration); } else { // 玩家死亡,失败, 重置随从 _player?.Reset();// foreach (var entity in _partnerMap.Values) { entity.Reset(); } // 清空怪物 foreach (var item in _monsterMap) { var entityId = item.Key; var monster = item.Value; //_monsterMap.Remove(entityId); _selector?.Unregister(monster); Context.Asset.Recycle(monster); RemoveElementBuff(entityId); } _monsterMap.Clear(); } Context.VM.ShowBlackMask(duration, () => { EventSingle.Instance.Notify(EventDefine.GameMainMapEndMask, sign); }); } /// /// 没有怪物了,直接清空所有特效 /// private void ClearVfxs() { foreach (var item in _entityViewsMap) { Context.Asset.Recycle(item.Value); } _entityViewsMap.Clear(); Context.Vfx.Stop(); } /// /// 玩家移动到屏幕中心 /// /// 单位:秒 private void PlayerMoveToCenter(float duration) { var centerPosition = Context.VM.Camera.TopCenterToWorldPosition(); var distance = centerPosition.y - _player.Position.y; var speed = distance / duration; // 使用玩家世界坐标的z值 centerPosition.z = _player.Position.z; //var targetPosition = Context.VM.PlayerRoot.InverseTransformPoint(centerPosition); //Log.Debug($"PlayerMoveToCenter Player World:{_player.Position} Local:{_player.LocalPosition} Center World:{centerPosition}"); _player.CommonAI.MoveTo(new MoveArgs { target = centerPosition, speed = speed, timeScale = 1, }); foreach (var item in _partnerMap) { var entityView = item.Value; var entity = entityView.Entity; var tarPosition = centerPosition; tarPosition += PartnersData.Instance.GetPartnerOffset(entity.Attr.UID); entityView.CommonAI.MoveTo(new MoveArgs { target = tarPosition, speed = speed, timeScale = 1, }); } } #endregion #region 子弹 private void OnGenBullet(int eventId, object args) { var dto = (GenBulletDto)args; var targetType = dto.masterType == EEntityType.Monster ? EEntityType.Player : EEntityType.Monster; var skill = SkillTableRepo.Get(dto.tableId); var skillVfx = SkillVfxsTableRepo.Get(dto.tableId); var loadAsync = Context.Asset.LoadEntity(skillVfx.SkillVfx, Context.VM.BulletRoot); var startTime = Context.Time.GetNowTime(); loadAsync.On(_ => { var delay = dto.delay > 0 ? Mathf.Max(dto.delay - (int)(Context.Time.GetNowTime() - startTime), 0) : 0; var view = loadAsync.Result; if (view == null) return; //var tarEntity = _selector.GetTarget((int)dto.targetId); //if (tarEntity == null || tarEntity.Entity.State == EEntityState.Dead) //{ // Context.Asset.Recycle(view); // Log.Debug($"子弹加载成功,但是目标已死亡。Master:{dto.masterId} Target:{dto.targetId} Bullet:{dto.tableId}"); // return; //} view.UID = UIDDefine.New(); view.MasterId = dto.masterId; view.TableId = dto.tableId; view.Selector = _selector; view.Position = dto.from; _entityViewsMap.Add(view.UID, view); //var tarPosition = dto.to; //if (skill.SkillVfxType == (int)ESkillVfxType.BulletMortar) //{ // 目标位置随机,以本节战斗位置为中心偏移 // tarPosition = _player.Position; //玩家的坐标和本节战斗的位置重叠 // if (dto.masterType != EEntityType.Monster) // {// 施法者是玩家或者随从 // tarPosition += skillVfx.SkillVfxPositions[1].ToVector3(); // } // tarPosition.x += UnityEngine.Random.Range(-4, 5); // tarPosition.y += UnityEngine.Random.Range(-2, 2); //} view.AI.Start(dto.targetId, dto.to, targetType, delay); }); } #endregion #region 召唤物 private void OnGenCommons(int eventId, object args) { var dto = (GenSummonsDto)args; var skill = SkillTableRepo.Get(dto.tableId); var skillVfxs = SkillVfxsTableRepo.Get(dto.tableId); //var targetType = dto.masterType == EEntityType.Monster ? EEntityType.Player : EEntityType.Monster; var battlePosition = _player.Position;//玩家的坐标和本节战斗的位置重叠 for (var i = 0; i < dto.count; i++) { switch ((ESkillVfxType)skill.SkillVfxType) { case ESkillVfxType.Trap: { var to = _selector.RandomPosition(dto.masterType == EEntityType.Monster ? EEntityType.Player : EEntityType.Monster, i == 0); //if (dto.masterType != EEntityType.Monster) //{ // 施法者是玩家或者随从 // to += skillVfxs.SkillVfxPositions[1].ToVector3(); //} //// 陷阱位置随机 //to.x += UnityEngine.Random.Range(-4, 5); //to.y += UnityEngine.Random.Range(-2, 2); LoadTrap(dto, skillVfxs.SkillVfx, to); } break; case ESkillVfxType.AOE: { if (skillVfxs.VfxFollowType == (int)EVfxFollowType.World) { // 坐标修改为本次战斗的世界坐标 dto.from = battlePosition; } var aoeFrom = skillVfxs.SkillVfxPositions[0].ToVector3(); var aoeTo = skillVfxs.SkillVfxPositions[1].ToVector3(); if (dto.masterType == EEntityType.Monster) { // 怪物在上,玩家在下 aoeFrom.y *= -1; aoeTo.y *= -1; } aoeTo += dto.from; dto.from += aoeFrom; LoadAOEEntity(dto, skillVfxs.SkillVfx, aoeTo); } break; case ESkillVfxType.AOENoMove: { var to = battlePosition; to += skillVfxs.SkillVfxPositions[0].ToVector3(); dto.from = to; LoadAOEEntity(dto, skillVfxs.SkillVfx, to); } break; case ESkillVfxType.AOETarget: { var to = dto.from; LoadAOEEntity(dto, skillVfxs.SkillVfx, to); } break; default: throw new NotImplementedException(); } } } private void LoadTrap(GenSummonsDto dto, string vfxName, Vector3 tarPosition) { var loadAsync = Context.Asset.LoadEntity(vfxName, Context.VM.BulletRoot); loadAsync.On(_ => { var view = loadAsync.Result; if (view == null) return; view.UID = UIDDefine.New(); view.MasterId = dto.masterId; view.TableId = dto.tableId; view.Selector = _selector; view.Position = dto.from; _entityViewsMap.Add(view.UID, view); view.AI.Start(tarPosition, dto.targetType); }); } private void LoadAOEEntity(GenSummonsDto dto, string vfxName, Vector3 tarPosition) { var loadAsync = Context.Asset.LoadEntity(vfxName, Context.VM.BulletRoot); loadAsync.On(_ => { var view = loadAsync.Result; if (view == null) return; view.UID = UIDDefine.New(); view.MasterId = dto.masterId; view.TableId = dto.tableId; view.Selector = _selector; view.Position = dto.from; _entityViewsMap.Add(view.UID, view); view.AI.Start(tarPosition, dto.targetType); }); } #endregion } }