Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / role / enemy / Enemy.cs
  1. #region 基础敌人设计思路
  2. /*
  3. 敌人有三种状态:
  4. 状态1: 未发现玩家, 视野不可穿墙, 该状态下敌人移动比较规律, 移动速度较慢, 一旦玩家进入视野或者听到玩家枪声, 立刻切换至状态3, 该房间的敌人不能再回到状态1
  5. 状态2: 发现有玩家, 但不知道在哪, 视野不可穿墙, 该情况下敌人移动速度明显加快, 移动不规律, 一旦玩家进入视野或者听到玩家枪声, 立刻切换至状态3
  6. 状态3: 明确知道玩家的位置, 视野允许穿墙, 移动速度与状态2一致, 进入该状态时, 敌人之间会相互告知玩家所在位置, 并朝着玩家位置开火,
  7. 如果有墙格挡, 则有一定概率继续开火, 一旦玩家立刻敌人视野超哥一段时间, 敌人自动切换为状态2
  8.  
  9. 敌人状态1只存在于少数房间内, 比如特殊房间, 大部分情况下敌人应该是状态2, 或者玩家进入房间时就被敌人发现
  10. */
  11. #endregion
  12.  
  13.  
  14. using System.Collections.Generic;
  15. using Godot;
  16.  
  17. /// <summary>
  18. /// 基础敌人
  19. /// </summary>
  20. public class Enemy : Role
  21. {
  22. /// <summary>
  23. /// 公共属性, 是否找到玩家, 如果找到玩家, 则所有敌人都会知道玩家的位置
  24. /// </summary>
  25. public static bool IsFindPlayer { get; set; }
  26.  
  27. private static readonly List<Enemy> _enemies = new List<Enemy>();
  28.  
  29. /// <summary>
  30. /// 敌人身上的状态机控制器
  31. /// </summary>
  32. public StateController<Enemy, AIStateEnum> StateController { get; }
  33.  
  34. /// <summary>
  35. /// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙
  36. /// </summary>
  37. public float ViewRange { get; set; } = 250;
  38.  
  39. /// <summary>
  40. /// 发现玩家后的视野半径
  41. /// </summary>
  42. public float TailAfterViewRange { get; set; } = 400;
  43.  
  44. /// <summary>
  45. /// 背后的视野半径, 单位像素
  46. /// </summary>
  47. public float BackViewRange { get; set; } = 50;
  48.  
  49. /// <summary>
  50. /// 视野检测射线, 朝玩家打射线, 检测是否碰到墙
  51. /// </summary>
  52. public RayCast2D ViewRay { get; }
  53. /// <summary>
  54. /// 导航代理
  55. /// </summary>
  56. public NavigationAgent2D NavigationAgent2D { get; }
  57.  
  58. /// <summary>
  59. /// 导航代理中点
  60. /// </summary>
  61. public Position2D NavigationPoint { get; }
  62.  
  63. private float _enemyAttackTimer = 0;
  64. public Enemy() : base(ResourcePath.prefab_role_Enemy_tscn)
  65. {
  66. StateController = new StateController<Enemy, AIStateEnum>();
  67. AddComponent(StateController);
  68.  
  69. AttackLayer = PhysicsLayer.Wall | PhysicsLayer.Props | PhysicsLayer.Player;
  70. Camp = CampEnum.Camp2;
  71.  
  72. MoveSpeed = 30;
  73.  
  74. Holster.SlotList[2].Enable = true;
  75. Holster.SlotList[3].Enable = true;
  76.  
  77. //视野射线
  78. ViewRay = GetNode<RayCast2D>("ViewRay");
  79. NavigationPoint = GetNode<Position2D>("NavigationPoint");
  80. NavigationAgent2D = NavigationPoint.GetNode<NavigationAgent2D>("NavigationAgent2D");
  81.  
  82. //PathSign = new PathSign(this, PathSignLength, GameApplication.Instance.Room.Player);
  83.  
  84. //注册Ai状态机
  85. StateController.Register(new AiNormalState());
  86. StateController.Register(new AiProbeState());
  87. StateController.Register(new AiTailAfterState());
  88. StateController.Register(new AIAttackState());
  89. }
  90.  
  91. public override void _Ready()
  92. {
  93. base._Ready();
  94. //默认状态
  95. StateController.ChangeState(AIStateEnum.AINormal);
  96.  
  97. NavigationAgent2D.SetTargetLocation(GameApplication.Instance.Room.Player.GlobalPosition);
  98. }
  99.  
  100. public override void _EnterTree()
  101. {
  102. if (!_enemies.Contains(this))
  103. {
  104. _enemies.Add(this);
  105. }
  106. }
  107.  
  108. public override void _ExitTree()
  109. {
  110. _enemies.Remove(this);
  111. }
  112.  
  113. public override void _PhysicsProcess(float delta)
  114. {
  115. base._PhysicsProcess(delta);
  116.  
  117. _enemyAttackTimer -= delta;
  118. }
  119.  
  120. /// <summary>
  121. /// 更新敌人视野
  122. /// </summary>
  123. public static void UpdateEnemiesView()
  124. {
  125. for (var i = 0; i < _enemies.Count; i++)
  126. {
  127. var enemy = _enemies[i];
  128. //enemy.StateController.GetState()
  129. }
  130. }
  131.  
  132. /// <summary>
  133. /// Ai触发的攻击
  134. /// </summary>
  135. public void EnemyAttack()
  136. {
  137. var weapon = Holster.ActiveWeapon;
  138. if (weapon != null)
  139. {
  140. if (weapon.Attribute.ContinuousShoot) //连发
  141. {
  142. Attack();
  143. }
  144. else //单发
  145. {
  146. if (_enemyAttackTimer <= 0)
  147. {
  148. _enemyAttackTimer = 60f / weapon.Attribute.StartFiringSpeed;
  149. Attack();
  150. }
  151. }
  152. }
  153. }
  154.  
  155. /// <summary>
  156. /// 返回目标点是否在视野范围内
  157. /// </summary>
  158. public bool IsInViewRange(Vector2 target)
  159. {
  160. var isForward = IsPositionInForward(target);
  161. if (isForward)
  162. {
  163. if (GlobalPosition.DistanceSquaredTo(target) <= ViewRange * ViewRange) //没有超出视野半径
  164. {
  165. return true;
  166. }
  167. }
  168.  
  169. return false;
  170. }
  171.  
  172. /// <summary>
  173. /// 调用视野检测, 如果被墙壁和其它物体遮挡, 则返回被挡住视野的物体对象, 视野无阻则返回 null
  174. /// </summary>
  175. public bool TestViewRayCast(Vector2 target)
  176. {
  177. ViewRay.Enabled = true;
  178. ViewRay.CastTo = ViewRay.ToLocal(target);
  179. ViewRay.ForceRaycastUpdate();
  180. return ViewRay.IsColliding();
  181. }
  182.  
  183. /// <summary>
  184. /// 调用视野检测完毕后, 需要调用 TestViewRayCastOver() 来关闭视野检测射线
  185. /// </summary>
  186. public void TestViewRayCastOver()
  187. {
  188. ViewRay.Enabled = false;
  189. }
  190. }