diff --git a/DungeonShooting_Godot/src/game/activity/role/Role.cs b/DungeonShooting_Godot/src/game/activity/role/Role.cs index dcaf930..1d2acd1 100644 --- a/DungeonShooting_Godot/src/game/activity/role/Role.cs +++ b/DungeonShooting_Godot/src/game/activity/role/Role.cs @@ -941,7 +941,7 @@ _meleeAttackTimer = RoleState.MeleeAttackTime; MountLookTarget = false; - WeaponPack.ActiveItem.TriggerMeleeAttack(this); + //WeaponPack.ActiveItem.TriggerMeleeAttack(this); //播放近战动画 PlayAnimation_MeleeAttack(() => { diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/AiAttackEnum.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/AiAttackEnum.cs new file mode 100644 index 0000000..f8e8b89 --- /dev/null +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/AiAttackEnum.cs @@ -0,0 +1,43 @@ + +/// +/// 调用 Enemy.EnemyAttack() 函数返回的结果 +/// +public enum AiAttackEnum +{ + /// + /// 未触发 EnemyAttack() + /// + None, + /// + /// 触发切换武器 + /// + ExchangeWeapon, + /// + /// 没有弹药了 + /// + NoAmmo, + /// + /// 换弹中 + /// + Reloading, + /// + /// 触发换弹 + /// + TriggerReload, + /// + /// 没有武器 + /// + NoWeapon, + /// + /// 正在锁定目标中 + /// + LockingTime, + /// + /// 攻击间隙时间 + /// + AttackInterval, + /// + /// 成功触发攻击 + /// + Attack, +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs index 0ff19a3..1e4fa21 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs @@ -53,11 +53,9 @@ /// 导航代理中点 /// public Marker2D NavigationPoint { get; private set; } - - //开火间隙时间 - private float _enemyAttackTimer = 0; - //目标在视野内的时间 - private float _targetInViewTime = 0; + + //锁定目标时间 + private float _lockTargetTime = 0; public override void OnInit() { @@ -141,17 +139,31 @@ protected override void Process(float delta) { base.Process(delta); - _enemyAttackTimer -= delta; //目标在视野内的时间 var currState = StateController.CurrState; if (currState == AiStateEnum.AiSurround || currState == AiStateEnum.AiFollowUp) { - _targetInViewTime += delta; + var weapon = WeaponPack.ActiveItem; + if (weapon != null) + { + if (!weapon.IsAttackIntervalTime()) //必须在可以开火时记录时间 + { + _lockTargetTime += delta; + } + else + { + _lockTargetTime = 0; + } + } + else + { + _lockTargetTime = 0; + } } else { - _targetInViewTime = 0; + _lockTargetTime = 0; } EnemyPickUpWeapon(); @@ -280,57 +292,22 @@ } /// - /// Ai触发的攻击 + /// Ai触发的攻击, 返回是否成功触发 Attack() 函数 /// - public void EnemyAttack(float delta) + public AiAttackEnum EnemyAttack(float delta) { + AiAttackEnum flag; var weapon = WeaponPack.ActiveItem; if (weapon != null) { - if (weapon.IsTotalAmmoEmpty()) //当前武器弹药打空 - { - //切换到有子弹的武器 - var index = WeaponPack.FindIndex((we, i) => !we.IsTotalAmmoEmpty()); - if (index != -1) - { - WeaponPack.ExchangeByIndex(index); - } - else //所有子弹打光 - { - - } - } - else if (weapon.Reloading) //换弹中 - { - - } - else if (weapon.IsAmmoEmpty()) //弹夹已经打空 - { - Reload(); - } - else if (_targetInViewTime >= weapon.Attribute.AiTargetLockingTime) //正常射击 - { - if (weapon.GetDelayedAttackTime() > 0) - { - Attack(); - } - else - { - if (weapon.Attribute.ContinuousShoot) //连发 - { - Attack(); - } - else //单发 - { - if (_enemyAttackTimer <= 0) - { - _enemyAttackTimer = 60f / weapon.Attribute.StartFiringSpeed; - Attack(); - } - } - } - } + flag = weapon.AiTriggerAttack(); } + else //没有武器 + { + flag = AiAttackEnum.NoWeapon; + } + + return flag; } /// @@ -457,4 +434,20 @@ } } + /// + /// 获取锁定目标的时间 + /// + public float GetLockTargetTime() + { + return _lockTargetTime; + } + + /// + /// 强制设置锁定目标时间 + /// + public void SetLockTargetTime(float time) + { + _lockTargetTime = time; + } + } diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFollowUpState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFollowUpState.cs index e0953eb..424de97 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFollowUpState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFollowUpState.cs @@ -15,6 +15,8 @@ //导航目标点刷新计时器 private float _navigationUpdateTimer = 0; private float _navigationInterval = 0.3f; + //Ai攻击状态 + private AiAttackEnum _attackEnum; public AiFollowUpState() : base(AiStateEnum.AiFollowUp) { @@ -75,11 +77,19 @@ if (!Master.NavigationAgent2D.IsNavigationFinished()) { - //计算移动 - var nextPos = Master.NavigationAgent2D.GetNextPathPosition(); - Master.AnimatedSprite.Play(AnimatorNames.Run); - Master.BasisVelocity = (nextPos - masterPosition - Master.NavigationPoint.Position).Normalized() * - Master.RoleState.MoveSpeed; + if (_attackEnum != AiAttackEnum.LockingTime && _attackEnum != AiAttackEnum.Attack) + { + //计算移动 + var nextPos = Master.NavigationAgent2D.GetNextPathPosition(); + Master.AnimatedSprite.Play(AnimatorNames.Run); + Master.BasisVelocity = (nextPos - masterPosition - Master.NavigationPoint.Position).Normalized() * + Master.RoleState.MoveSpeed; + } + else + { + Master.AnimatedSprite.Play(AnimatorNames.Idle); + Master.BasisVelocity = Vector2.Zero; + } } else { @@ -103,7 +113,7 @@ if (inAttackRange) //在攻击范围内 { //发起攻击 - Master.EnemyAttack(delta); + _attackEnum = Master.EnemyAttack(delta); //距离够近, 可以切换到环绕模式 if (Master.GlobalPosition.DistanceSquaredTo(playerPos) <= Mathf.Pow(Utils.GetConfigRangeStart(weapon.Attribute.BulletDistanceRange), 2) * 0.7f) diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiSurroundState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiSurroundState.cs index 1e7f4c4..9b50208 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiSurroundState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiSurroundState.cs @@ -25,6 +25,8 @@ private Vector2 _prevPos; //卡在一个位置的时间 private float _lockTimer; + //Ai攻击状态 + private AiAttackEnum _attackEnum; public AiSurroundState() : base(AiStateEnum.AiSurround) { @@ -36,6 +38,7 @@ _isMoveOver = true; _pauseTimer = 0; _moveFlag = false; + _attackEnum = AiAttackEnum.None; } public override void Process(float delta) @@ -120,11 +123,19 @@ } else { - //计算移动 - var nextPos = Master.NavigationAgent2D.GetNextPathPosition(); - Master.AnimatedSprite.Play(AnimatorNames.Run); - Master.BasisVelocity = (nextPos - pos - Master.NavigationPoint.Position).Normalized() * - Master.RoleState.MoveSpeed; + if (_attackEnum != AiAttackEnum.LockingTime && _attackEnum != AiAttackEnum.Attack) + { + //计算移动 + var nextPos = Master.NavigationAgent2D.GetNextPathPosition(); + Master.AnimatedSprite.Play(AnimatorNames.Run); + Master.BasisVelocity = (nextPos - pos - Master.NavigationPoint.Position).Normalized() * + Master.RoleState.MoveSpeed; + } + else + { + Master.AnimatedSprite.Play(AnimatorNames.Idle); + Master.BasisVelocity = Vector2.Zero; + } } if (_prevPos.DistanceSquaredTo(pos) <= 0.01f) @@ -147,7 +158,7 @@ else { //发起攻击 - Master.EnemyAttack(delta); + _attackEnum = Master.EnemyAttack(delta); } } } diff --git a/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs index 24bacb0..1cc974a 100644 --- a/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs +++ b/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs @@ -190,7 +190,7 @@ private float _upTimer = 0; //连发次数 - private float _continuousCount = 0; + private int _continuousCount = 0; //连发状态记录 private bool _continuousShootFlag = false; @@ -682,12 +682,12 @@ { return Master == null && GetParent() == GameApplication.Instance.World.NormalLayer; } - + /// /// 扳机函数, 调用即视为按下扳机 /// - /// 按下扳机的角色, 如果传 null, 则视为走火 - public void Trigger(Role trigger) + /// 按下扳机的角色, 如果传 null, 则视为走火 + public void Trigger(Role triggerRole) { //不能触发扳机 if (!NoMasterCanTrigger && Master == null) return; @@ -698,10 +698,10 @@ //更新武器属性信息 _triggerFlag = true; _triggerRoleFlag = true; - if (trigger != null) + if (triggerRole != null) { - TriggerRoleIsAi = trigger.IsAi; - TriggerRoleAttackLayer = trigger.AttackLayer; + TriggerRoleIsAi = triggerRole.IsAi; + TriggerRoleAttackLayer = triggerRole.AttackLayer; _weaponAttribute = TriggerRoleIsAi ? _aiWeaponAttribute : _playerWeaponAttribute; } else if (Master != null) @@ -713,10 +713,9 @@ //是否第一帧按下 var justDown = _downTimer == 0; - if (_beLoadedState == 0 || _beLoadedState == -1) //需要执行上膛操作 { - if (justDown && !Reloading && trigger != null) + if (justDown && !Reloading && triggerRole != null) { if (CurrAmmo <= 0) { @@ -786,7 +785,7 @@ else if (CurrAmmo <= 0) //子弹不够 { fireFlag = false; - if (justDown && trigger != null) + if (justDown && triggerRole != null) { //第一帧按下, 触发换弹 Reload(); @@ -831,10 +830,7 @@ } } } - - _attackFlag = true; } - } } } @@ -871,6 +867,22 @@ { return _delayedTime; } + + /// + /// 获取当前需要连发弹药的数量, 配置了 ContinuousCountRange 时生效 + /// + public int GetContinuousCount() + { + return _continuousCount; + } + + /// + /// 返回是否是攻击间隙时间 + /// + public bool IsAttackIntervalTime() + { + return _attackTimer > 0 || _triggerTimer > 0; + } /// /// 刚按下扳机 @@ -920,6 +932,7 @@ /// private void TriggerFire() { + _attackFlag = true; _noAttackTime = 0; //减子弹数量 @@ -1036,13 +1049,13 @@ } } - /// - /// 触发武器的近战攻击 - /// - public void TriggerMeleeAttack(Role trigger) - { - - } + // /// + // /// 触发武器的近战攻击 + // /// + // public virtual void TriggerMeleeAttack(Role trigger) + // { + // + // } /// /// 获取武器攻击的目标层级 @@ -1892,11 +1905,90 @@ //-------------------------------- Ai相关 ----------------------------- - /// - /// 获取 Ai 对于该武器的评分, 评分越高, 代表 Ai 会越优先选择该武器, 如果为 -1, 则表示 Ai 不会使用该武器 - /// - public float GetAiScore() + public AiAttackEnum AiTriggerAttack() { - return 1; + AiAttackEnum flag; + if (IsTotalAmmoEmpty()) //当前武器弹药打空 + { + //切换到有子弹的武器 + var index = Master.WeaponPack.FindIndex((we, i) => !we.IsTotalAmmoEmpty()); + if (index != -1) + { + flag = AiAttackEnum.ExchangeWeapon; + Master.WeaponPack.ExchangeByIndex(index); + } + else //所有子弹打光 + { + flag = AiAttackEnum.NoAmmo; + } + } + else if (Reloading) //换弹中 + { + flag = AiAttackEnum.TriggerReload; + } + else if (IsAmmoEmpty()) //弹夹已经打空 + { + flag = AiAttackEnum.Reloading; + Reload(); + } + else if (_continuousCount >= 1) //连发中 + { + flag = AiAttackEnum.Attack; + } + else if (IsAttackIntervalTime()) //开火间隙 + { + flag = AiAttackEnum.AttackInterval; + } + else + { + var enemy = (Enemy)Master; + if (enemy.GetLockTargetTime() >= Attribute.AiTargetLockingTime) //正常射击 + { + if (GetDelayedAttackTime() > 0) + { + flag = AiAttackEnum.Attack; + enemy.Attack(); + if (_attackFlag) + { + enemy.SetLockTargetTime(0); + } + } + else + { + if (Attribute.ContinuousShoot) //连发 + { + flag = AiAttackEnum.Attack; + enemy.Attack(); + if (_attackFlag) + { + enemy.SetLockTargetTime(0); + } + } + else //单发 + { + flag = AiAttackEnum.Attack; + enemy.Attack(); + if (_attackFlag) + { + enemy.SetLockTargetTime(0); + } + } + } + } + else //锁定时间没到 + { + flag = AiAttackEnum.LockingTime; + } + } + + return flag; } + + // /// + // /// 获取 Ai 对于该武器的评分, 评分越高, 代表 Ai 会越优先选择该武器, 如果为 -1, 则表示 Ai 不会使用该武器 + // /// + // public float GetAiScore() + // { + // return 1; + // } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/weapon/gun/Gun.cs b/DungeonShooting_Godot/src/game/activity/weapon/gun/Gun.cs index 0d8ba1c..c5c36bd 100644 --- a/DungeonShooting_Godot/src/game/activity/weapon/gun/Gun.cs +++ b/DungeonShooting_Godot/src/game/activity/weapon/gun/Gun.cs @@ -37,6 +37,7 @@ { this.CallDelay(0, () => { + Debug.Log("敌人扔掉武器触发攻击"); Trigger(master); }); }