diff --git a/DungeonShooting_Godot/prefab/role/Player.tscn b/DungeonShooting_Godot/prefab/role/Player.tscn
index 7bfb950..5786938 100644
--- a/DungeonShooting_Godot/prefab/role/Player.tscn
+++ b/DungeonShooting_Godot/prefab/role/Player.tscn
@@ -24,4 +24,4 @@
[node name="AnimatedSprite" parent="." index="2"]
material = SubResource( 2 )
-frame = 3
+frame = 2
diff --git a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs
index 98940e9..9016553 100644
--- a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs
+++ b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs
@@ -18,20 +18,26 @@
///
public class Enemy : Role
{
+
+ ///
+ /// 公共属性, 是否找到玩家, 如果找到玩家, 则所有敌人都会知道玩家的位置
+ ///
+ public static bool IsFindPlayer { get; set; }
+
///
/// 敌人身上的状态机控制器
///
public StateController StateController { get; }
///
- /// 视野半径, 单位像素
+ /// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙
///
- public float ViewRange { get; set; } = 200;
+ public float ViewRange { get; set; } = 250;
///
/// 发现玩家后的视野半径
///
- public float TailAfterViewRange { get; set; } = 250;
+ public float TailAfterViewRange { get; set; } = 400;
///
/// 背后的视野半径, 单位像素
@@ -53,6 +59,8 @@
///
public Position2D NavigationPoint { get; }
+ private float _enemyAttackTimer = 0;
+
public Enemy() : base(ResourcePath.prefab_role_Enemy_tscn)
{
StateController = new StateController();
@@ -88,6 +96,36 @@
NavigationAgent2D.SetTargetLocation(GameApplication.Instance.Room.Player.GlobalPosition);
}
+ public override void _PhysicsProcess(float delta)
+ {
+ base._PhysicsProcess(delta);
+
+ _enemyAttackTimer -= delta;
+ }
+
+ ///
+ /// Ai触发的攻击
+ ///
+ public void EnemyAttack()
+ {
+ var weapon = Holster.ActiveWeapon;
+ if (weapon != null)
+ {
+ if (weapon.Attribute.ContinuousShoot) //连发
+ {
+ Attack();
+ }
+ else //单发
+ {
+ if (_enemyAttackTimer <= 0)
+ {
+ _enemyAttackTimer = 60f / weapon.Attribute.StartFiringSpeed;
+ Attack();
+ }
+ }
+ }
+ }
+
///
/// 返回目标点是否在视野范围内
///
diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs
index 03a0d35..35e44bd 100644
--- a/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs
+++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs
@@ -8,11 +8,22 @@
{
//是否发现玩家
private bool _isFindPlayer;
+
//下一个运动的角度
private Vector2 _nextPos;
+
//是否移动结束
private bool _isMoveOver;
+
+ //上一次移动是否撞墙
+ private bool _againstWall;
+ //撞墙法线角度
+ private float _againstWallNormalAngle;
+
+ //移动停顿计时器
+ private float _pauseTimer;
+
public AiNormalState() : base(AIStateEnum.AINormal)
{
}
@@ -21,6 +32,9 @@
{
_isFindPlayer = false;
_isMoveOver = true;
+ _againstWall = false;
+ _againstWallNormalAngle = 0;
+ _pauseTimer = 0;
}
public override void PhysicsProcess(float delta)
@@ -43,37 +57,67 @@
//发现玩家
_isFindPlayer = true;
}
+ else if (_pauseTimer >= 0)
+ {
+ _pauseTimer -= delta;
+ }
else if (_isMoveOver) //没发现玩家, 且已经移动完成
{
- var angle = Utils.RandRange(0, Mathf.Pi * 2f);
- var len = Utils.RandRangeInt(50, 500);
- _nextPos = new Vector2(len, 0).Rotated(angle);
- //获取射线碰到的坐标
- if (Master.TestViewRayCast(_nextPos)) //碰到墙壁
- {
- _nextPos = Master.ViewRay.GetCollisionPoint();
- }
- Master.NavigationAgent2D.SetTargetLocation(_nextPos);
+ RunOver();
_isMoveOver = false;
}
else //移动中
{
//计算移动
var nextPos = Master.NavigationAgent2D.GetNextLocation();
- Master.LookTargetPosition(_nextPos);
Master.AnimatedSprite.Animation = AnimatorNames.Run;
- Master.Velocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() * Master.MoveSpeed;
+ Master.Velocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() *
+ Master.MoveSpeed;
Master.CalcMove(delta);
- if (Master.NavigationAgent2D.IsNavigationFinished())
+ if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点
{
+ _pauseTimer = 1;
_isMoveOver = true;
}
}
+
Master.TestViewRayCastOver();
}
}
+ //移动结束
+ private void RunOver()
+ {
+ float angle;
+ if (_againstWall)
+ {
+ angle = Utils.RandRange(_againstWallNormalAngle - Mathf.Pi * 0.5f,
+ _againstWallNormalAngle + Mathf.Pi * 0.5f);
+ }
+ else
+ {
+ angle = Utils.RandRange(0, Mathf.Pi * 2f);
+ }
+
+ var len = Utils.RandRangeInt(30, 200);
+ _nextPos = new Vector2(len, 0).Rotated(angle) + Master.GlobalPosition;
+ //获取射线碰到的坐标
+ if (Master.TestViewRayCast(_nextPos)) //碰到墙壁
+ {
+ _nextPos = Master.ViewRay.GetCollisionPoint();
+ _againstWall = true;
+ _againstWallNormalAngle = Master.ViewRay.GetCollisionNormal().Angle();
+ }
+ else
+ {
+ _againstWall = false;
+ }
+
+ Master.NavigationAgent2D.SetTargetLocation(_nextPos);
+ Master.LookTargetPosition(_nextPos);
+ }
+
public override void DebugDraw()
{
Master.DrawLine(Vector2.Zero, Master.ToLocal(_nextPos), Colors.Green);
diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs
index 5ff99ac..909715f 100644
--- a/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs
+++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs
@@ -10,9 +10,14 @@
private float _navigationUpdateTimer = 0;
private float _navigationInterval = 0.3f;
- private bool _isInView = true;
+ //目标是否在视野半径内
+ private bool _isInViewRange;
- private float _viewTimer = 0;
+ //是否在视野内
+ private bool _isInView;
+
+ //目标从视野消失时已经过去的时间
+ private float _viewTimer;
public AiTailAfterState() : base(AIStateEnum.AITailAfter)
{
@@ -20,16 +25,14 @@
public override void Enter(AIStateEnum prev, params object[] args)
{
+ _isInViewRange = true;
_isInView = true;
_navigationUpdateTimer = 0;
+ _viewTimer = 0;
}
public override void PhysicsProcess(float delta)
{
- if (Master.NavigationAgent2D.IsNavigationFinished())
- {
- return;
- }
var masterPos = Master.GlobalPosition;
var playerPos = GameApplication.Instance.Room.Player.GlobalPosition;
@@ -56,16 +59,40 @@
Master.CalcMove(delta);
//检测玩家是否在视野内, 此时视野可穿墙, 直接检测距离即可
- _isInView = masterPos.DistanceSquaredTo(playerPos) <= Master.TailAfterViewRange * Master.TailAfterViewRange;
- if (_isInView)
+
+ if (masterPos.DistanceSquaredTo(playerPos) <= Master.TailAfterViewRange * Master.TailAfterViewRange)
+ {
+ _isInView = !Master.TestViewRayCast(playerPos);
+ if (_isInView)
+ {
+ _isInViewRange = true;
+ }
+ else
+ {
+ _isInViewRange = masterPos.DistanceSquaredTo(playerPos) <= Master.ViewRange * Master.ViewRange;
+ }
+ }
+ else
+ {
+ _isInViewRange = masterPos.DistanceSquaredTo(playerPos) <= Master.ViewRange * Master.ViewRange;
+ _isInView = false;
+ }
+
+ if (_isInViewRange)
{
_viewTimer = 0;
+
+ if (_isInView)
+ {
+ //攻击
+ Master.EnemyAttack();
+ }
}
else //超出视野
{
if (_viewTimer > 10) //10秒
{
- ChangeStateLate(AIStateEnum.AIProbe);
+ ChangeStateLate(AIStateEnum.AINormal);
}
else
{
@@ -81,6 +108,10 @@
{
Master.DrawLine(Vector2.Zero, Master.ToLocal(playerPos), Colors.Red);
}
+ else if (_isInViewRange)
+ {
+ Master.DrawLine(Vector2.Zero, Master.ToLocal(playerPos), Colors.Orange);
+ }
else
{
Master.DrawLine(Vector2.Zero, Master.ToLocal(playerPos), Colors.Blue);
diff --git a/DungeonShooting_Godot/src/game/room/RoomManager.cs b/DungeonShooting_Godot/src/game/room/RoomManager.cs
index 9a39e41..9643006 100644
--- a/DungeonShooting_Godot/src/game/room/RoomManager.cs
+++ b/DungeonShooting_Godot/src/game/room/RoomManager.cs
@@ -91,6 +91,12 @@
enemy2.PickUpWeapon(WeaponManager.GetGun("1002"));
enemy2.PickUpWeapon(WeaponManager.GetGun("1004"));
enemy2.PickUpWeapon(WeaponManager.GetGun("1003"));
+
+ var enemy3 = new Enemy();
+ enemy3.Name = "Enemy3";
+ enemy3.PutDown(new Vector2(540, 300));
+ enemy3.PickUpWeapon(WeaponManager.GetGun("1003"));
+ enemy3.PickUpWeapon(WeaponManager.GetGun("1002"));
WeaponManager.GetGun("1001").PutDown(new Vector2(80, 100));
WeaponManager.GetGun("1001").PutDown(new Vector2(80, 80));