diff --git a/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs b/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs index c88e33f..5080d9b 100644 --- a/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs +++ b/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs @@ -1727,4 +1727,12 @@ } _freezeSprite.Unfreeze(); } + + /// + /// 获取中心点坐标 + /// + public Vector2 GetCenterPosition() + { + return AnimatedSprite.Position + Position; + } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/framework/activity/components/StateBase.cs b/DungeonShooting_Godot/src/framework/activity/components/StateBase.cs index ce71a11..42a8e57 100644 --- a/DungeonShooting_Godot/src/framework/activity/components/StateBase.cs +++ b/DungeonShooting_Godot/src/framework/activity/components/StateBase.cs @@ -49,15 +49,6 @@ } /// - /// 是否允许切换至下一个状态, 该函数由状态机控制器调用, 不需要手动调用 - /// - /// 下一个状态类型 - public virtual bool CanChangeState(S next) - { - return true; - } - - /// /// 从当前状态退出时调用 /// /// 下一个状态类型 diff --git a/DungeonShooting_Godot/src/framework/activity/components/StateController.cs b/DungeonShooting_Godot/src/framework/activity/components/StateController.cs index f1b035c..942dd5f 100644 --- a/DungeonShooting_Godot/src/framework/activity/components/StateController.cs +++ b/DungeonShooting_Godot/src/framework/activity/components/StateController.cs @@ -133,7 +133,7 @@ CurrStateBase = newState; newState.Enter(default, arg); } - else if (CurrStateBase.CanChangeState(next)) + else { // Debug.Log($"changeState: {CurrState} => {next}"); _isChangeState = !late; diff --git a/DungeonShooting_Godot/src/game/activity/bullet/explode/Explode.cs b/DungeonShooting_Godot/src/game/activity/bullet/explode/Explode.cs index d21bca8..50a9e22 100644 --- a/DungeonShooting_Godot/src/game/activity/bullet/explode/Explode.cs +++ b/DungeonShooting_Godot/src/game/activity/bullet/explode/Explode.cs @@ -30,6 +30,10 @@ /// public uint AttackLayer { get; private set; } + /// + /// 产生爆炸的子弹数据 + /// + public BulletData BulletData { get; private set; } private bool _init = false; private float _hitRadius; @@ -51,13 +55,13 @@ /// /// 初始化爆炸数据 /// - /// 爆炸所在区域 + /// 产生爆炸的子弹数据 /// 攻击的层级 /// 伤害半径 /// 造成的伤害 /// 击退半径 /// 最大击退速度 - public void Init(AffiliationArea affiliationArea, uint attackLayer, float hitRadius, int harm, float repelledRadius, float maxRepelled) + public void Init(BulletData bulletData, uint attackLayer, float hitRadius, int harm, float repelledRadius, float maxRepelled) { if (!_init) { @@ -68,7 +72,8 @@ AnimationPlayer.AnimationFinished += OnAnimationFinish; BodyEntered += OnBodyEntered; } - + + BulletData = bulletData; AttackLayer = attackLayer; _hitRadius = hitRadius; _harm = harm; @@ -78,6 +83,7 @@ CircleShape.Radius = Mathf.Max(hitRadius, maxRepelled); //冲击波 + var affiliationArea = bulletData.TriggerRole?.AffiliationArea; if (affiliationArea != null) { ShockWave(affiliationArea); @@ -138,7 +144,7 @@ { if (o is Role role) //是角色 { - role.CallDeferred(nameof(role.Hurt), _harm, angle); + role.CallDeferred(nameof(role.Hurt), BulletData.TriggerRole, _harm, angle); } else if (o is Bullet bullet) //是子弹 { diff --git a/DungeonShooting_Godot/src/game/activity/bullet/laser/Laser.cs b/DungeonShooting_Godot/src/game/activity/bullet/laser/Laser.cs index 5a62788..5722fce 100644 --- a/DungeonShooting_Godot/src/game/activity/bullet/laser/Laser.cs +++ b/DungeonShooting_Godot/src/game/activity/bullet/laser/Laser.cs @@ -145,7 +145,7 @@ role.MoveController.AddForce(Vector2.FromAngle(Rotation) * BulletData.Repel); } //造成伤害 - role.CallDeferred(nameof(Role.Hurt), BulletData.Harm, Rotation); + role.CallDeferred(nameof(Role.Hurt), BulletData.TriggerRole, BulletData.Harm, Rotation); } } diff --git a/DungeonShooting_Godot/src/game/activity/bullet/normal/BoomBullet.cs b/DungeonShooting_Godot/src/game/activity/bullet/normal/BoomBullet.cs index 447f5a7..065162f 100644 --- a/DungeonShooting_Godot/src/game/activity/bullet/normal/BoomBullet.cs +++ b/DungeonShooting_Godot/src/game/activity/bullet/normal/BoomBullet.cs @@ -71,7 +71,7 @@ explode.Position = pos; explode.RotationDegrees = Utils.Random.RandomRangeInt(0, 360); explode.AddToActivityRootDeferred(RoomLayerEnum.YSortLayer); - explode.Init(BulletData.TriggerRole?.AffiliationArea, AttackLayer, 25, BulletData.Harm, 50, BulletData.Repel); + explode.Init(BulletData, AttackLayer, 25, BulletData.Harm, 50, BulletData.Repel); explode.RunPlay(BulletData.TriggerRole); if (AffiliationArea != null) { diff --git a/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs b/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs index a3d59c1..407c461 100644 --- a/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs +++ b/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs @@ -146,7 +146,7 @@ } //造成伤害 - role.CallDeferred(nameof(AdvancedRole.Hurt), BulletData.Harm, Rotation); + role.CallDeferred(nameof(AdvancedRole.Hurt), BulletData.TriggerRole, BulletData.Harm, Rotation); //穿透次数 CurrentPenetration++; diff --git a/DungeonShooting_Godot/src/game/activity/role/AdvancedRole.cs b/DungeonShooting_Godot/src/game/activity/role/AdvancedRole.cs index c3dfc4b..c1350c1 100644 --- a/DungeonShooting_Godot/src/game/activity/role/AdvancedRole.cs +++ b/DungeonShooting_Godot/src/game/activity/role/AdvancedRole.cs @@ -147,11 +147,6 @@ }); } - public override Vector2 GetCenterPosition() - { - return MountPoint.GlobalPosition; - } - public override void LookTargetPosition(Vector2 pos) { LookPosition = pos; @@ -385,7 +380,7 @@ role.MoveController.AddForce(v2); } - role.CallDeferred(nameof(Hurt), damage, (role.GetCenterPosition() - GlobalPosition).Angle()); + role.CallDeferred(nameof(Hurt), this, damage, (role.GetCenterPosition() - GlobalPosition).Angle()); } else if (activityObject is Bullet bullet) //攻击子弹 { diff --git a/DungeonShooting_Godot/src/game/activity/role/Role.cs b/DungeonShooting_Godot/src/game/activity/role/Role.cs index 1634e09..ee675a6 100644 --- a/DungeonShooting_Godot/src/game/activity/role/Role.cs +++ b/DungeonShooting_Godot/src/game/activity/role/Role.cs @@ -275,9 +275,10 @@ /// /// 当受伤时调用 /// + /// 谁触发的伤害 /// 受到的伤害 /// 是否受到真实伤害, 如果为false, 则表示该伤害被互动格挡掉了 - protected virtual void OnHit(int damage, bool realHarm) + protected virtual void OnHit(ActivityObject target, int damage, bool realHarm) { } @@ -503,14 +504,6 @@ } /// - /// 获取当前角色的中心点坐标 - /// - public virtual Vector2 GetCenterPosition() - { - return AnimatedSprite.GlobalPosition; - } - - /// /// 使角色看向指定的坐标的方向 /// public virtual void LookTargetPosition(Vector2 pos) @@ -675,9 +668,10 @@ /// /// 受到伤害, 如果是在碰撞信号处理函数中调用该函数, 请使用 CallDeferred 来延时调用, 否则很有可能导致报错 /// + /// 谁触发的伤害 /// 伤害的量 /// 伤害角度(弧度制) - public virtual void Hurt(int damage, float angle) + public virtual void Hurt(ActivityObject target, int damage, float angle) { //受伤闪烁, 无敌状态 if (Invincible) @@ -706,7 +700,7 @@ // blood.Rotation = angle; // GameApplication.Instance.Node3D.GetRoot().AddChild(blood); } - OnHit(damage, !flag); + OnHit(target, damage, !flag); //受伤特效 PlayHitAnimation(); diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/AdvancedEnemy.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/AdvancedEnemy.cs index ea5c57b..5052136 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/AdvancedEnemy.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/AdvancedEnemy.cs @@ -73,7 +73,7 @@ /// 锁定目标已经走过的时间 /// public float LockTargetTime { get; set; } = 0; - + public override void OnInit() { base.OnInit(); @@ -100,6 +100,8 @@ StateController.Register(new AiSurroundState()); StateController.Register(new AiFindAmmoState()); StateController.Register(new AiAttackState()); + StateController.Register(new AiAstonishedState()); + StateController.Register(new AiNotifyState()); //默认状态 StateController.ChangeStateInstant(AIAdvancedStateEnum.AiNormal); @@ -179,12 +181,13 @@ EnemyPickUpWeapon(); } - protected override void OnHit(int damage, bool realHarm) + protected override void OnHit(ActivityObject target, int damage, bool realHarm) { //受到伤害 var state = StateController.CurrState; if (state == AIAdvancedStateEnum.AiNormal || state == AIAdvancedStateEnum.AiLeaveFor) //|| state == AiStateEnum.AiProbe { + LookTarget = target; StateController.ChangeState(AIAdvancedStateEnum.AiTailAfter); } } @@ -281,26 +284,6 @@ } /// - /// 检查是否能切换到 AiStateEnum.AiLeaveFor 状态 - /// - public bool CanChangeLeaveFor() - { - if (!World.Enemy_IsFindTarget) - { - return false; - } - - var currState = StateController.CurrState; - if (currState == AIAdvancedStateEnum.AiNormal)// || currState == AiStateEnum.AiProbe) - { - //判断是否在同一个房间内 - return World.Enemy_FindTargetAffiliationSet.Contains(AffiliationArea); - } - - return false; - } - - /// /// 获取武器攻击范围 (最大距离值与最小距离的中间值) /// /// 从最小到最大距离的过渡量, 0 - 1, 默认 0.5 diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs index b6f69a0..d97e5f9 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs @@ -179,13 +179,14 @@ } } - protected override void OnHit(int damage, bool realHarm) + protected override void OnHit(ActivityObject target, int damage, bool realHarm) { //受到伤害 var state = StateController.CurrState; if (state == AINormalStateEnum.AiNormal || state == AINormalStateEnum.AiLeaveFor) //|| state == AiStateEnum.AiProbe { - StateController.ChangeState(AINormalStateEnum.AiTailAfter); + LookTarget = target; + StateController.ChangeState(AINormalStateEnum.AiTailAfter, target); } } @@ -213,26 +214,6 @@ } /// - /// 检查是否能切换到 AiStateEnum.AiLeaveFor 状态 - /// - public bool CanChangeLeaveFor() - { - if (!World.Enemy_IsFindTarget) - { - return false; - } - - var currState = StateController.CurrState; - if (currState == AINormalStateEnum.AiNormal)// || currState == AiStateEnum.AiProbe) - { - //判断是否在同一个房间内 - return World.Enemy_FindTargetAffiliationSet.Contains(AffiliationArea); - } - - return false; - } - - /// /// 返回目标点是否在视野范围内 /// public bool IsInViewRange(Vector2 target) diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AIAdvancedStateEnum.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AIAdvancedStateEnum.cs index a9c963d..c02b0d2 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AIAdvancedStateEnum.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AIAdvancedStateEnum.cs @@ -8,7 +8,7 @@ /// /// 找到玩家,准备通知其他敌人 /// - AiFind, + AiNotify, /// /// 惊讶状态 /// diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAstonishedState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAstonishedState.cs new file mode 100644 index 0000000..bc07574 --- /dev/null +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAstonishedState.cs @@ -0,0 +1,64 @@ +using Godot; + +namespace AdvancedState; + +/// +/// 发现目标时的惊讶状态 +/// +public class AiAstonishedState : StateBase +{ + /// + /// 下一个状态 + /// + public AIAdvancedStateEnum NextState; + + private object[] _args; + private float _timer; + + public AiAstonishedState() : base(AIAdvancedStateEnum.AiAstonished) + { + } + + public override void Enter(AIAdvancedStateEnum prev, params object[] args) + { + if (args.Length == 0) + { + Debug.Log("进入 AINormalStateEnum.AiAstonished 状态必传入下一个状态做完参数!"); + ChangeState(prev); + return; + } + + NextState = (AIAdvancedStateEnum)args[0]; + _args = args; + _timer = 0.6f; + + if (NextState == AIAdvancedStateEnum.AiLeaveFor) + { + var target = (ActivityObject)args[1]; + Master.LookTargetPosition(target.GetCenterPosition()); + } + } + + public override void Process(float delta) + { + Master.DoIdle(); + //播放惊讶表情 + + _timer -= delta; + if (_timer <= 0) + { + if (_args.Length == 1) + { + ChangeState(NextState); + } + else if (_args.Length == 2) + { + ChangeState(NextState, _args[1]); + } + else if (_args.Length == 3) + { + ChangeState(NextState, _args[1], _args[2]); + } + } + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAttackState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAttackState.cs index f4f690d..7d8b30d 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAttackState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAttackState.cs @@ -1,4 +1,5 @@ -using Godot; +using System; +using Godot; namespace AdvancedState; @@ -38,19 +39,21 @@ public override void Enter(AIAdvancedStateEnum prev, params object[] args) { + if (Master.LookTarget == null) + { + throw new Exception("进入 AIAdvancedStateEnum.AiAttack 状态时角色没有攻击目标!"); + } + var weapon = Master.WeaponPack.ActiveItem; if (weapon == null) { - Debug.LogError("进入 AIAdvancedStateEnum.AiAttack 状态时角色没有武器!"); - ChangeState(prev); - return; + throw new Exception("进入 AIAdvancedStateEnum.AiAttack 状态时角色没有武器!"); + } if (!weapon.TriggerIsReady()) { - Debug.LogError("进入 AIAdvancedStateEnum.AiAttack 状态时角色武器还玩法触动扳机!"); - ChangeState(prev); - return; + throw new Exception("进入 AIAdvancedStateEnum.AiAttack 状态时角色武器还玩法触动扳机!"); } Master.BasisVelocity = Vector2.Zero; @@ -67,13 +70,10 @@ { Master.MountLookTarget = true; Master.LockTargetTime = 0; - Master.LookTarget = null; } public override void Process(float delta) { - Master.LookTarget = Player.Current; - var weapon = Master.WeaponPack.ActiveItem; if (weapon == null) { @@ -84,6 +84,7 @@ { if (weapon.GetAttackTimer() <= 0) //攻击冷却完成 { + Master.MountLookTarget = true; //这里要做换弹判断, 还有上膛判断 if (weapon.CurrAmmo <= 0) //换弹判断 { @@ -194,7 +195,7 @@ } else if (_isMoveOver) //移动已经完成 { - RunOver(Player.Current.Position); + RunOver(Master.LookTarget.Position); _isMoveOver = false; } else @@ -202,7 +203,7 @@ var masterPosition = Master.Position; if (_lockTimer >= 1) //卡在一个点超过一秒 { - RunOver(Player.Current.Position); + RunOver(Master.LookTarget.Position); _isMoveOver = false; _lockTimer = 0; } diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFindAmmoState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFindAmmoState.cs index 0019e00..3857cea 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFindAmmoState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFindAmmoState.cs @@ -1,4 +1,5 @@ +using System; using Godot; namespace AdvancedState; @@ -24,11 +25,14 @@ public override void Enter(AIAdvancedStateEnum prev, params object[] args) { + if (Master.LookTarget == null) + { + throw new Exception("进入 AIAdvancedStateEnum.AiFindAmmo 状态时角色没有攻击目标!"); + } + if (args.Length == 0) { - Debug.LogError("进入 AiStateEnum.AiFindAmmo 状态必须要把目标武器当成参数传过来"); - ChangeState(prev); - return; + throw new Exception("进入 AiStateEnum.AiFindAmmo 状态必须要把目标武器当成参数传过来"); } SetTargetWeapon((Weapon)args[0]); @@ -40,11 +44,6 @@ _target.SetSign(SignNames.AiFindWeaponSign, Master); } - public override void Exit(AIAdvancedStateEnum next) - { - Master.LookTarget = null; - } - public override void Process(float delta) { if (!Master.IsAllWeaponTotalAmmoEmpty()) //已经有弹药了 @@ -65,9 +64,6 @@ { _navigationUpdateTimer -= delta; } - - //枪口指向玩家 - Master.LookTarget = Player.Current; if (_target.IsDestroyed || _target.IsTotalAmmoEmpty()) //已经被销毁, 或者弹药已经被其他角色捡走 { @@ -96,7 +92,7 @@ else { //检测目标没有超出跟随视野距离 - _isInTailAfterRange = Master.IsInTailAfterViewRange(Player.Current.GetCenterPosition()); + _isInTailAfterRange = Master.IsInTailAfterViewRange(Master.LookTarget.GetCenterPosition()); if (_isInTailAfterRange) { _tailAfterTimer = 0; @@ -143,11 +139,11 @@ if (_tailAfterTimer <= 0) { - Master.DrawLine(Vector2.Zero, Master.ToLocal(Player.Current.GetCenterPosition()), Colors.Orange); + Master.DrawLine(Vector2.Zero, Master.ToLocal(Master.LookTarget.GetCenterPosition()), Colors.Orange); } else if (_tailAfterTimer <= 10) { - Master.DrawLine(Vector2.Zero, Master.ToLocal(Player.Current.GetCenterPosition()), Colors.Blue); + Master.DrawLine(Vector2.Zero, Master.ToLocal(Master.LookTarget.GetCenterPosition()), Colors.Blue); } } diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFollowUpState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFollowUpState.cs index 30b04ff..6d7f141 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFollowUpState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFollowUpState.cs @@ -1,4 +1,5 @@ +using System; using Godot; namespace AdvancedState; @@ -18,15 +19,15 @@ public override void Enter(AIAdvancedStateEnum prev, params object[] args) { + if (Master.LookTarget == null) + { + throw new Exception("进入 AIAdvancedStateEnum.AiFollowUp 状态时角色没有攻击目标!"); + } + _navigationUpdateTimer = 0; Master.TargetInView = true; } - public override void Exit(AIAdvancedStateEnum next) - { - Master.LookTarget = null; - } - public override void Process(float delta) { //先检查弹药是否打光 @@ -46,7 +47,7 @@ } } - var playerPos = Player.Current.GetCenterPosition(); + var playerPos = Master.LookTarget.GetCenterPosition(); //更新玩家位置 if (_navigationUpdateTimer <= 0) @@ -69,9 +70,6 @@ { inAttackRange = distanceSquared <= Mathf.Pow(Master.GetWeaponRange(0.7f), 2); } - - //枪口指向玩家 - Master.LookTarget = Player.Current; if (!Master.NavigationAgent2D.IsNavigationFinished()) { @@ -121,7 +119,7 @@ public override void DebugDraw() { - var playerPos = Player.Current.GetCenterPosition(); + var playerPos = Master.LookTarget.GetCenterPosition(); Master.DrawLine(new Vector2(0, -8), Master.ToLocal(playerPos), Colors.Red); } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiLeaveForState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiLeaveForState.cs index 9bdea9c..3383060 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiLeaveForState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiLeaveForState.cs @@ -1,4 +1,5 @@ +using System; using Godot; namespace AdvancedState; @@ -12,22 +13,26 @@ private float _navigationUpdateTimer = 0; private float _navigationInterval = 0.3f; + //目标 + private ActivityObject _target; + //目标点 + private Vector2 _targetPosition; + public AiLeaveForState() : base(AIAdvancedStateEnum.AiLeaveFor) { } public override void Enter(AIAdvancedStateEnum prev, params object[] args) { - if (Master.World.Enemy_IsFindTarget) + if (args.Length == 0) { - Master.NavigationAgent2D.TargetPosition = Master.World.Enemy_FindTargetPosition; - } - else - { - ChangeState(prev); - return; + throw new Exception("进入 AINormalStateEnum.AiLeaveFor 状态必须带上目标对象"); } + _target = (ActivityObject)args[0]; + _targetPosition = _target.GetCenterPosition(); + Master.LookTargetPosition(_targetPosition); + //先检查弹药是否打光 if (Master.IsAllWeaponTotalAmmoEmpty()) { @@ -40,6 +45,16 @@ } } + /// + /// 设置移动目标位置 + /// + public void SetTargetPosition(Vector2 target) + { + _targetPosition = target; + _navigationUpdateTimer = _navigationInterval; + Master.NavigationAgent2D.TargetPosition = target; + } + public override void Process(float delta) { //这个状态下不会有攻击事件, 所以没必要每一帧检查是否弹药耗尽 @@ -49,7 +64,7 @@ { //每隔一段时间秒更改目标位置 _navigationUpdateTimer = _navigationInterval; - Master.NavigationAgent2D.TargetPosition = Master.World.Enemy_FindTargetPosition; + Master.NavigationAgent2D.TargetPosition = _targetPosition; } else { @@ -58,7 +73,7 @@ if (!Master.NavigationAgent2D.IsNavigationFinished()) { - Master.LookTargetPosition(Master.World.Enemy_FindTargetPosition); + Master.LookTargetPosition(_targetPosition); //移动 Master.DoMove(); } @@ -77,6 +92,7 @@ //关闭射线检测 Master.TestViewRayCastOver(); //切换成发现目标状态 + Master.LookTarget = Player.Current; ChangeState(AIAdvancedStateEnum.AiFollowUp); return; } diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNormalState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNormalState.cs index bc57b33..8a4858b 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNormalState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNormalState.cs @@ -8,9 +8,6 @@ /// public class AiNormalState : StateBase { - //是否发现玩家 - private bool _isFindPlayer; - //下一个运动的坐标 private Vector2 _nextPos; @@ -38,106 +35,94 @@ public override void Enter(AIAdvancedStateEnum prev, params object[] args) { - _isFindPlayer = false; _isMoveOver = true; _againstWall = false; _againstWallNormalAngle = 0; _pauseTimer = 0; _moveFlag = false; + Master.LookTarget = null; } public override void Process(float delta) { - //其他敌人发现玩家 - if (Master.CanChangeLeaveFor()) + //检测玩家 + var player = Player.Current; + //玩家中心点坐标 + var playerPos = player.GetCenterPosition(); + + if (Master.IsInViewRange(playerPos) && !Master.TestViewRayCast(playerPos)) //发现玩家 { - ChangeState(AIAdvancedStateEnum.AiLeaveFor); + //发现玩家 + Master.LookTarget = player; + //进入惊讶状态, 然后再进入通知状态 + ChangeState(AIAdvancedStateEnum.AiAstonished, AIAdvancedStateEnum.AiNotify); return; } - - if (_isFindPlayer) //已经找到玩家了 + else if (_pauseTimer >= 0) { - //现临时处理, 直接切换状态 - ChangeState(AIAdvancedStateEnum.AiTailAfter); + Master.AnimatedSprite.Play(AnimatorNames.Idle); + _pauseTimer -= delta; } - else //没有找到玩家 + else if (_isMoveOver) //没发现玩家, 且已经移动完成 { - //检测玩家 - var player = Player.Current; - //玩家中心点坐标 - var playerPos = player.GetCenterPosition(); - - if (Master.IsInViewRange(playerPos) && !Master.TestViewRayCast(playerPos)) //发现玩家 - { - //发现玩家 - _isFindPlayer = true; - } - else if (_pauseTimer >= 0) - { - Master.AnimatedSprite.Play(AnimatorNames.Idle); - _pauseTimer -= delta; - } - else if (_isMoveOver) //没发现玩家, 且已经移动完成 + RunOver(); + _isMoveOver = false; + } + else //移动中 + { + if (_lockTimer >= 1) //卡在一个点超过一秒 { RunOver(); _isMoveOver = false; + _lockTimer = 0; } - else //移动中 + else if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点 { - if (_lockTimer >= 1) //卡在一个点超过一秒 + _pauseTimer = Utils.Random.RandomRangeFloat(0.3f, 2f); + _isMoveOver = true; + _moveFlag = false; + //站立 + Master.DoIdle(); + } + else if (!_moveFlag) + { + _moveFlag = true; + var pos = Master.Position; + //移动 + Master.DoMove(); + _prevPos = pos; + } + else + { + var pos = Master.Position; + var lastSlideCollision = Master.GetLastSlideCollision(); + if (lastSlideCollision != null && lastSlideCollision.GetCollider() is AdvancedRole) //碰到其他角色 { - RunOver(); - _isMoveOver = false; - _lockTimer = 0; - } - else if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点 - { - _pauseTimer = Utils.Random.RandomRangeFloat(0.3f, 2f); + _pauseTimer = Utils.Random.RandomRangeFloat(0.1f, 0.5f); _isMoveOver = true; _moveFlag = false; //站立 Master.DoIdle(); } - else if (!_moveFlag) + else { - _moveFlag = true; - var pos = Master.Position; //移动 Master.DoMove(); - _prevPos = pos; + } + + if (_prevPos.DistanceSquaredTo(pos) <= 0.01f) + { + _lockTimer += delta; } else { - var pos = Master.Position; - var lastSlideCollision = Master.GetLastSlideCollision(); - if (lastSlideCollision != null && lastSlideCollision.GetCollider() is AdvancedRole) //碰到其他角色 - { - _pauseTimer = Utils.Random.RandomRangeFloat(0.1f, 0.5f); - _isMoveOver = true; - _moveFlag = false; - //站立 - Master.DoIdle(); - } - else - { - //移动 - Master.DoMove(); - } - - if (_prevPos.DistanceSquaredTo(pos) <= 0.01f) - { - _lockTimer += delta; - } - else - { - _prevPos = pos; - } + _prevPos = pos; } } - - //关闭射线检测 - Master.TestViewRayCastOver(); } + + //关闭射线检测 + Master.TestViewRayCastOver(); } //移动结束 diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNotifyState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNotifyState.cs new file mode 100644 index 0000000..b8fe1fa --- /dev/null +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNotifyState.cs @@ -0,0 +1,36 @@ +using System; + +namespace AdvancedState; + +/// +/// 发现目标, 通知其它敌人 +/// +public class AiNotifyState : StateBase +{ + private float _timer; + + public AiNotifyState() : base(AIAdvancedStateEnum.AiNotify) + { + } + + public override void Enter(AIAdvancedStateEnum prev, params object[] args) + { + if (Master.LookTarget == null) + { + throw new Exception("进入 AIAdvancedStateEnum.AiNotify 没有攻击目标!"); + } + _timer = 0.6f; + //通知其它角色 + Master.World.NotifyEnemyTarget(Master, Master.LookTarget); + } + + public override void Process(float delta) + { + Master.DoIdle(); + _timer -= delta; + if (_timer <= 0) + { + ChangeState(AIAdvancedStateEnum.AiTailAfter); + } + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiSurroundState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiSurroundState.cs index 2b063e8..6faee7a 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiSurroundState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiSurroundState.cs @@ -1,4 +1,5 @@ +using System; using Godot; namespace AdvancedState; @@ -29,17 +30,17 @@ public override void Enter(AIAdvancedStateEnum prev, params object[] args) { + if (Master.LookTarget == null) + { + throw new Exception("进入 AIAdvancedStateEnum.AiSurround 状态时角色没有攻击目标!"); + } + Master.TargetInView = true; _isMoveOver = true; _pauseTimer = 0; _moveFlag = false; } - public override void Exit(AIAdvancedStateEnum next) - { - Master.LookTarget = null; - } - public override void Process(float delta) { //先检查弹药是否打光 @@ -54,12 +55,9 @@ } } - var playerPos = Player.Current.GetCenterPosition(); + var playerPos = Master.LookTarget.GetCenterPosition(); var weapon = Master.WeaponPack.ActiveItem; - //枪口指向玩家 - Master.LookTarget = Player.Current; - //检测玩家是否在视野内 if (Master.IsInTailAfterViewRange(playerPos)) { diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiTailAfterState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiTailAfterState.cs index 1146b13..a4bd0f6 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiTailAfterState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiTailAfterState.cs @@ -1,10 +1,11 @@ +using System; using Godot; namespace AdvancedState; /// -/// AI 发现玩家, 跟随玩家 +/// AI 发现玩家, 跟随玩家, 但是不在视野范围内 /// public class AiTailAfterState : StateBase { @@ -26,6 +27,11 @@ public override void Enter(AIAdvancedStateEnum prev, params object[] args) { + if (Master.LookTarget == null) + { + throw new Exception("进入 AIAdvancedStateEnum.AiTailAfter 状态时角色没有攻击目标!"); + } + _isInViewRange = true; _navigationUpdateTimer = 0; _viewTimer = 0; @@ -42,16 +48,11 @@ } } - public override void Exit(AIAdvancedStateEnum next) - { - Master.LookTarget = null; - } - public override void Process(float delta) { //这个状态下不会有攻击事件, 所以没必要每一帧检查是否弹药耗尽 - var playerPos = Player.Current.GetCenterPosition(); + var playerPos = Master.LookTarget.GetCenterPosition(); //更新玩家位置 if (_navigationUpdateTimer <= 0) @@ -65,9 +66,6 @@ _navigationUpdateTimer -= delta; } - //枪口指向玩家 - Master.LookTarget = Player.Current; - if (!Master.NavigationAgent2D.IsNavigationFinished()) { //移动 @@ -117,7 +115,7 @@ public override void DebugDraw() { - var playerPos = Player.Current.GetCenterPosition(); + var playerPos = Master.LookTarget.GetCenterPosition(); if (_isInViewRange) { Master.DrawLine(new Vector2(0, -8), Master.ToLocal(playerPos), Colors.Orange); diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AINormalStateEnum.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AINormalStateEnum.cs index 9b8fe24..35e56dd 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AINormalStateEnum.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AINormalStateEnum.cs @@ -9,7 +9,11 @@ /// /// 找到玩家,准备通知其他敌人 /// - AiFind, + AiNotify, + /// + /// 发现目标, 惊讶状态 + /// + AiAstonished, /// /// 收到其他敌人通知, 前往发现目标的位置 /// diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiAstonishedState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiAstonishedState.cs new file mode 100644 index 0000000..74fd553 --- /dev/null +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiAstonishedState.cs @@ -0,0 +1,41 @@ +namespace NnormalState; + +/// +/// 发现目标时的惊讶状态 +/// +public class AiAstonishedState : StateBase +{ + /// + /// 下一个状态 + /// + public AINormalStateEnum NextState; + + private float _timer; + + public AiAstonishedState() : base(AINormalStateEnum.AiAstonished) + { + } + + public override void Enter(AINormalStateEnum prev, params object[] args) + { + if (args.Length == 0) + { + Debug.Log("进入 AINormalStateEnum.AiAstonished 状态必传入下一个状态做完参数!"); + ChangeState(prev); + return; + } + + NextState = (AINormalStateEnum)args[0]; + _timer = 1.5f; + } + + public override void Process(float delta) + { + Master.DoIdle(); + _timer -= delta; + if (_timer <= 0) + { + ChangeState(NextState); + } + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiLeaveForState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiLeaveForState.cs index 9cd1c49..98163be 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiLeaveForState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiLeaveForState.cs @@ -18,68 +18,68 @@ public override void Enter(AINormalStateEnum prev, params object[] args) { - if (Master.World.Enemy_IsFindTarget) - { - Master.NavigationAgent2D.TargetPosition = Master.World.Enemy_FindTargetPosition; - } - else - { - ChangeState(prev); - } + // if (Master.World.Enemy_IsFindTarget) + // { + // Master.NavigationAgent2D.TargetPosition = Master.World.Enemy_FindTargetPosition; + // } + // else + // { + // ChangeState(prev); + // } } public override void Process(float delta) { - //这个状态下不会有攻击事件, 所以没必要每一帧检查是否弹药耗尽 - - //更新玩家位置 - if (_navigationUpdateTimer <= 0) - { - //每隔一段时间秒更改目标位置 - _navigationUpdateTimer = _navigationInterval; - Master.NavigationAgent2D.TargetPosition = Master.World.Enemy_FindTargetPosition; - } - else - { - _navigationUpdateTimer -= delta; - } - - if (!Master.NavigationAgent2D.IsNavigationFinished()) - { - Master.LookTargetPosition(Master.World.Enemy_FindTargetPosition); - //移动 - Master.DoMove(); - } - else - { - //站立 - Master.DoIdle(); - } - - var playerPos = Player.Current.GetCenterPosition(); - //检测玩家是否在视野内, 如果在, 则切换到 AiTargetInView 状态 - if (Master.IsInTailAfterViewRange(playerPos)) - { - if (!Master.TestViewRayCast(playerPos)) //看到玩家 - { - //关闭射线检测 - Master.TestViewRayCastOver(); - //切换成发现目标状态 - ChangeState(AINormalStateEnum.AiFollowUp); - return; - } - else - { - //关闭射线检测 - Master.TestViewRayCastOver(); - } - } - - //移动到目标掉了, 还没发现目标 - if (Master.NavigationAgent2D.IsNavigationFinished()) - { - ChangeState(AINormalStateEnum.AiNormal); - } + // //这个状态下不会有攻击事件, 所以没必要每一帧检查是否弹药耗尽 + // + // //更新玩家位置 + // if (_navigationUpdateTimer <= 0) + // { + // //每隔一段时间秒更改目标位置 + // _navigationUpdateTimer = _navigationInterval; + // Master.NavigationAgent2D.TargetPosition = Master.World.Enemy_FindTargetPosition; + // } + // else + // { + // _navigationUpdateTimer -= delta; + // } + // + // if (!Master.NavigationAgent2D.IsNavigationFinished()) + // { + // Master.LookTargetPosition(Master.World.Enemy_FindTargetPosition); + // //移动 + // Master.DoMove(); + // } + // else + // { + // //站立 + // Master.DoIdle(); + // } + // + // var playerPos = Player.Current.GetCenterPosition(); + // //检测玩家是否在视野内, 如果在, 则切换到 AiTargetInView 状态 + // if (Master.IsInTailAfterViewRange(playerPos)) + // { + // if (!Master.TestViewRayCast(playerPos)) //看到玩家 + // { + // //关闭射线检测 + // Master.TestViewRayCastOver(); + // //切换成发现目标状态 + // ChangeState(AINormalStateEnum.AiFollowUp); + // return; + // } + // else + // { + // //关闭射线检测 + // Master.TestViewRayCastOver(); + // } + // } + // + // //移动到目标掉了, 还没发现目标 + // if (Master.NavigationAgent2D.IsNavigationFinished()) + // { + // ChangeState(AINormalStateEnum.AiNormal); + // } } public override void DebugDraw() diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNormalState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNormalState.cs index c2f2a32..61469b8 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNormalState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNormalState.cs @@ -47,13 +47,6 @@ public override void Process(float delta) { - //其他敌人发现玩家 - if (Master.CanChangeLeaveFor()) - { - ChangeState(AINormalStateEnum.AiLeaveFor); - return; - } - if (_isFindPlayer) //已经找到玩家了 { //现临时处理, 直接切换状态 diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNotifyState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNotifyState.cs new file mode 100644 index 0000000..76e46c4 --- /dev/null +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNotifyState.cs @@ -0,0 +1,30 @@ +namespace NnormalState; + +/// +/// 发现目标, 通知其它敌人 +/// +public class AiNotifyState : StateBase +{ + + private float _timer; + + public AiNotifyState() : base(AINormalStateEnum.AiNotify) + { + } + + public override void Enter(AINormalStateEnum prev, params object[] args) + { + _timer = 1.5f; + //通知其它角色 + Master.World.NotifyEnemyTarget(Master, Player.Current); + } + + public override void Process(float delta) + { + _timer -= delta; + if (_timer <= 0) + { + ChangeState(AINormalStateEnum.AiTailAfter); + } + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/player/Player.cs b/DungeonShooting_Godot/src/game/activity/role/player/Player.cs index d92e804..40e84e3 100644 --- a/DungeonShooting_Godot/src/game/activity/role/player/Player.cs +++ b/DungeonShooting_Godot/src/game/activity/role/player/Player.cs @@ -183,14 +183,14 @@ { //Hurt(1000, 0); Hp = 0; - Hurt(1000, 0); + Hurt(this, 1000, 0); } else if (Input.IsKeyPressed(Key.O)) //测试用, 消灭房间内所有敌人 { var enemyList = AffiliationArea.FindIncludeItems(o => o.CollisionWithMask(PhysicsLayer.Enemy)); foreach (var enemy in enemyList) { - ((AdvancedEnemy)enemy).Hurt(1000, 0); + ((AdvancedEnemy)enemy).Hurt(this, 1000, 0); } } // //测试用 @@ -224,7 +224,7 @@ return 1; } - protected override void OnHit(int damage, bool realHarm) + protected override void OnHit(ActivityObject target, int damage, bool realHarm) { //进入无敌状态 if (realHarm) //真实伤害 diff --git a/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs b/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs index de74723..b5db0d6 100644 --- a/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs +++ b/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs @@ -159,7 +159,7 @@ } //造成伤害 - role.CallDeferred(nameof(AdvancedRole.Hurt), damage, (role.GetCenterPosition() - GlobalPosition).Angle()); + role.CallDeferred(nameof(AdvancedRole.Hurt), TriggerRole, damage, (role.GetCenterPosition() - GlobalPosition).Angle()); } else if (activityObject is Bullet bullet) //攻击子弹 { diff --git a/DungeonShooting_Godot/src/game/room/World.cs b/DungeonShooting_Godot/src/game/room/World.cs index 27d4805..580c809 100644 --- a/DungeonShooting_Godot/src/game/room/World.cs +++ b/DungeonShooting_Godot/src/game/room/World.cs @@ -65,21 +65,6 @@ /// public List Enemy_InstanceList { get; } = new List(); - // /// - // /// 公共属性, 敌人是否找到目标, 如果找到目标, 则与目标同房间的所有敌人都会知道目标的位置 - // /// - // public bool Enemy_IsFindTarget { get; set; } - // - // /// - // /// 公共属性, 敌人在哪个区域找到的目标, 所有该区域下的敌人都会知道目标的位置 - // /// - // public HashSet Enemy_FindTargetAffiliationSet { get; } = new HashSet(); - // - // /// - // /// 公共属性, 敌人找到的目标的位置, 如果目标在视野内, 则一直更新 - // /// - // public Vector2 Enemy_FindTargetPosition { get; set; } - private bool _pause = false; private List _coroutineList; @@ -131,9 +116,10 @@ if (role is AdvancedEnemy advancedEnemy) { //将未发现目标的敌人状态置为惊讶状态 - if (advancedEnemy.StateController.CurrState == AIAdvancedStateEnum.AiNormal) + var controller = advancedEnemy.StateController; + if (controller.CurrState == AIAdvancedStateEnum.AiNormal) { - advancedEnemy.StateController.ChangeState(AIAdvancedStateEnum.AiAstonished); + controller.ChangeState(AIAdvancedStateEnum.AiAstonished, AIAdvancedStateEnum.AiLeaveFor, target); } } }