Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / activity / role / ai / AiRole.cs
@小李xl 小李xl on 24 Mar 2024 11 KB 抽出AiRole类
  1.  
  2. using AiState;
  3. using Godot;
  4.  
  5. /// <summary>
  6. /// Ai角色
  7. /// </summary>
  8. public abstract partial class AiRole : Role
  9. {
  10. /// <summary>
  11. /// 目标是否在视野内
  12. /// </summary>
  13. public bool TargetInView { get; set; } = true;
  14. /// <summary>
  15. /// 敌人身上的状态机控制器
  16. /// </summary>
  17. public StateController<AiRole, AIStateEnum> StateController { get; private set; }
  18. /// <summary>
  19. /// 视野检测射线, 朝玩家打射线, 检测是否碰到墙
  20. /// </summary>
  21. [Export, ExportFillNode]
  22. public RayCast2D ViewRay { get; set; }
  23.  
  24. /// <summary>
  25. /// 导航代理
  26. /// </summary>
  27. [Export, ExportFillNode]
  28. public NavigationAgent2D NavigationAgent2D { get; set; }
  29.  
  30. /// <summary>
  31. /// 导航代理中点
  32. /// </summary>
  33. [Export, ExportFillNode]
  34. public Marker2D NavigationPoint { get; set; }
  35.  
  36. /// <summary>
  37. /// 不通过武发射子弹的开火点
  38. /// </summary>
  39. [Export, ExportFillNode]
  40. public Marker2D FirePoint { get; set; }
  41. /// <summary>
  42. /// 当前敌人所看向的对象, 也就是枪口指向的对象
  43. /// </summary>
  44. public ActivityObject LookTarget { get; set; }
  45.  
  46. /// <summary>
  47. /// 攻击锁定目标时间
  48. /// </summary>
  49. public float LockingTime { get; set; } = 1f;
  50. /// <summary>
  51. /// 锁定目标已经走过的时间
  52. /// </summary>
  53. public float LockTargetTime { get; set; } = 0;
  54. /// <summary>
  55. /// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙
  56. /// </summary>
  57. public float ViewRange { get; set; } = 250;
  58.  
  59. /// <summary>
  60. /// 发现玩家后跟随玩家的视野半径
  61. /// </summary>
  62. public float TailAfterViewRange { get; set; } = 400;
  63.  
  64. /// <summary>
  65. /// 背后的视野半径, 单位像素
  66. /// </summary>
  67. public float BackViewRange { get; set; } = 50;
  68. /// <summary>
  69. /// 攻击间隔时间, 秒
  70. /// </summary>
  71. public float AttackInterval { get; set; } = 0;
  72.  
  73. public override void OnInit()
  74. {
  75. base.OnInit();
  76. IsAi = true;
  77. StateController = AddComponent<StateController<AiRole, AIStateEnum>>();
  78. //注册Ai状态机
  79. StateController.Register(new AiNormalState());
  80. StateController.Register(new AiTailAfterState());
  81. StateController.Register(new AiFollowUpState());
  82. StateController.Register(new AiLeaveForState());
  83. StateController.Register(new AiSurroundState());
  84. StateController.Register(new AiFindAmmoState());
  85. StateController.Register(new AiAttackState());
  86. StateController.Register(new AiAstonishedState());
  87. StateController.Register(new AiNotifyState());
  88. //默认状态
  89. StateController.ChangeStateInstant(AIStateEnum.AiNormal);
  90.  
  91. //NavigationAgent2D.VelocityComputed += OnVelocityComputed;
  92. }
  93. /// <summary>
  94. /// 返回地上的武器是否有可以拾取的, 也包含没有被其他敌人标记的武器
  95. /// </summary>
  96. public bool CheckUsableWeaponInUnclaimed()
  97. {
  98. foreach (var unclaimedWeapon in World.Weapon_UnclaimedWeapons)
  99. {
  100. //判断是否能拾起武器, 条件: 相同的房间
  101. if (unclaimedWeapon.AffiliationArea == AffiliationArea)
  102. {
  103. if (!unclaimedWeapon.IsTotalAmmoEmpty())
  104. {
  105. if (!unclaimedWeapon.HasSign(SignNames.AiFindWeaponSign))
  106. {
  107. return true;
  108. }
  109. else
  110. {
  111. //判断是否可以移除该标记
  112. var enemy = unclaimedWeapon.GetSign<Enemy>(SignNames.AiFindWeaponSign);
  113. if (enemy == null || enemy.IsDestroyed) //标记当前武器的敌人已经被销毁
  114. {
  115. unclaimedWeapon.RemoveSign(SignNames.AiFindWeaponSign);
  116. return true;
  117. }
  118. else if (!enemy.IsAllWeaponTotalAmmoEmpty()) //标记当前武器的敌人已经有新的武器了
  119. {
  120. unclaimedWeapon.RemoveSign(SignNames.AiFindWeaponSign);
  121. return true;
  122. }
  123. }
  124. }
  125. }
  126. }
  127.  
  128. return false;
  129. }
  130. /// <summary>
  131. /// 寻找可用的武器
  132. /// </summary>
  133. public Weapon FindTargetWeapon()
  134. {
  135. Weapon target = null;
  136. var position = Position;
  137. foreach (var weapon in World.Weapon_UnclaimedWeapons)
  138. {
  139. //判断是否能拾起武器, 条件: 相同的房间, 或者当前房间目前没有战斗, 或者不在战斗房间
  140. if (weapon.AffiliationArea == AffiliationArea)
  141. {
  142. //还有弹药
  143. if (!weapon.IsTotalAmmoEmpty())
  144. {
  145. //查询是否有其他敌人标记要拾起该武器
  146. if (weapon.HasSign(SignNames.AiFindWeaponSign))
  147. {
  148. var enemy = weapon.GetSign<Enemy>(SignNames.AiFindWeaponSign);
  149. if (enemy == this) //就是自己标记的
  150. {
  151.  
  152. }
  153. else if (enemy == null || enemy.IsDestroyed) //标记当前武器的敌人已经被销毁
  154. {
  155. weapon.RemoveSign(SignNames.AiFindWeaponSign);
  156. }
  157. else if (!enemy.IsAllWeaponTotalAmmoEmpty()) //标记当前武器的敌人已经有新的武器了
  158. {
  159. weapon.RemoveSign(SignNames.AiFindWeaponSign);
  160. }
  161. else //放弃这把武器
  162. {
  163. continue;
  164. }
  165. }
  166.  
  167. if (target == null) //第一把武器
  168. {
  169. target = weapon;
  170. }
  171. else if (target.Position.DistanceSquaredTo(position) >
  172. weapon.Position.DistanceSquaredTo(position)) //距离更近
  173. {
  174. target = weapon;
  175. }
  176. }
  177. }
  178. }
  179.  
  180. return target;
  181. }
  182.  
  183. /// <summary>
  184. /// 获取武器攻击范围 (最大距离值与最小距离的中间值)
  185. /// </summary>
  186. /// <param name="weight">从最小到最大距离的过渡量, 0 - 1, 默认 0.5</param>
  187. public float GetWeaponRange(float weight = 0.5f)
  188. {
  189. if (WeaponPack.ActiveItem != null)
  190. {
  191. var attribute = WeaponPack.ActiveItem.Attribute;
  192. return Mathf.Lerp(Utils.GetConfigRangeStart(attribute.Bullet.DistanceRange), Utils.GetConfigRangeEnd(attribute.Bullet.DistanceRange), weight);
  193. }
  194.  
  195. return 0;
  196. }
  197.  
  198. /// <summary>
  199. /// 返回目标点是否在视野范围内
  200. /// </summary>
  201. public virtual bool IsInViewRange(Vector2 target)
  202. {
  203. var isForward = IsPositionInForward(target);
  204. if (isForward)
  205. {
  206. if (GlobalPosition.DistanceSquaredTo(target) <= ViewRange * ViewRange) //没有超出视野半径
  207. {
  208. return true;
  209. }
  210. }
  211.  
  212. return false;
  213. }
  214.  
  215. /// <summary>
  216. /// 返回目标点是否在跟随状态下的视野半径内
  217. /// </summary>
  218. public virtual bool IsInTailAfterViewRange(Vector2 target)
  219. {
  220. var isForward = IsPositionInForward(target);
  221. if (isForward)
  222. {
  223. if (GlobalPosition.DistanceSquaredTo(target) <= TailAfterViewRange * TailAfterViewRange) //没有超出视野半径
  224. {
  225. return true;
  226. }
  227. }
  228.  
  229. return false;
  230. }
  231.  
  232. /// <summary>
  233. /// 调用视野检测, 如果被墙壁和其它物体遮挡, 则返回true
  234. /// </summary>
  235. public bool TestViewRayCast(Vector2 target)
  236. {
  237. ViewRay.Enabled = true;
  238. ViewRay.TargetPosition = ViewRay.ToLocal(target);
  239. ViewRay.ForceRaycastUpdate();
  240. return ViewRay.IsColliding();
  241. }
  242.  
  243. /// <summary>
  244. /// 调用视野检测完毕后, 需要调用 TestViewRayCastOver() 来关闭视野检测射线
  245. /// </summary>
  246. public void TestViewRayCastOver()
  247. {
  248. ViewRay.Enabled = false;
  249. }
  250.  
  251. /// <summary>
  252. /// AI 拾起武器操作
  253. /// </summary>
  254. public void DoPickUpWeapon()
  255. {
  256. //这几个状态不需要主动拾起武器操作
  257. var state = StateController.CurrState;
  258. if (state == AIStateEnum.AiNormal || state == AIStateEnum.AiNotify || state == AIStateEnum.AiAstonished || state == AIStateEnum.AiAttack)
  259. {
  260. return;
  261. }
  262. //拾起地上的武器
  263. if (InteractiveItem is Weapon weapon)
  264. {
  265. if (WeaponPack.ActiveItem == null) //手上没有武器, 无论如何也要拾起
  266. {
  267. TriggerInteractive();
  268. return;
  269. }
  270.  
  271. //没弹药了
  272. if (weapon.IsTotalAmmoEmpty())
  273. {
  274. return;
  275. }
  276. var index = WeaponPack.FindIndex((we, i) => we.ActivityBase.Id == weapon.ActivityBase.Id);
  277. if (index != -1) //与武器背包中武器类型相同, 补充子弹
  278. {
  279. if (!WeaponPack.GetItem(index).IsAmmoFull())
  280. {
  281. TriggerInteractive();
  282. }
  283.  
  284. return;
  285. }
  286.  
  287. // var index2 = Holster.FindWeapon((we, i) =>
  288. // we.Attribute.WeightType == weapon.Attribute.WeightType && we.IsTotalAmmoEmpty());
  289. var index2 = WeaponPack.FindIndex((we, i) => we.IsTotalAmmoEmpty());
  290. if (index2 != -1) //扔掉没子弹的武器
  291. {
  292. ThrowWeapon(index2);
  293. TriggerInteractive();
  294. return;
  295. }
  296. // if (Holster.HasVacancy()) //有空位, 拾起武器
  297. // {
  298. // TriggerInteractive();
  299. // return;
  300. // }
  301. }
  302. }
  303. /// <summary>
  304. /// 获取锁定目标的剩余时间
  305. /// </summary>
  306. public float GetLockRemainderTime()
  307. {
  308. var weapon = WeaponPack.ActiveItem;
  309. if (weapon == null)
  310. {
  311. return LockingTime - LockTargetTime;
  312. }
  313. return weapon.Attribute.AiAttackAttr.LockingTime - LockTargetTime;
  314. }
  315.  
  316. public override void LookTargetPosition(Vector2 pos)
  317. {
  318. LookTarget = null;
  319. base.LookTargetPosition(pos);
  320. }
  321. /// <summary>
  322. /// 执行移动操作
  323. /// </summary>
  324. public void DoMove()
  325. {
  326. // //计算移动
  327. // NavigationAgent2D.MaxSpeed = EnemyRoleState.MoveSpeed;
  328. // var nextPos = NavigationAgent2D.GetNextPathPosition();
  329. // NavigationAgent2D.Velocity = (nextPos - Position - NavigationPoint.Position).Normalized() * RoleState.MoveSpeed;
  330. AnimatedSprite.Play(AnimatorNames.Run);
  331. //计算移动
  332. var nextPos = NavigationAgent2D.GetNextPathPosition();
  333. BasisVelocity = (nextPos - Position - NavigationPoint.Position).Normalized() * RoleState.MoveSpeed;
  334. }
  335.  
  336. /// <summary>
  337. /// 执行站立操作
  338. /// </summary>
  339. public void DoIdle()
  340. {
  341. AnimatedSprite.Play(AnimatorNames.Idle);
  342. BasisVelocity = Vector2.Zero;
  343. }
  344. /// <summary>
  345. /// 更新房间中标记的目标位置
  346. /// </summary>
  347. public void UpdateMarkTargetPosition()
  348. {
  349. if (LookTarget != null)
  350. {
  351. AffiliationArea.RoomInfo.MarkTargetPosition[LookTarget.Id] = LookTarget.Position;
  352. }
  353. }
  354. // private void OnVelocityComputed(Vector2 velocity)
  355. // {
  356. // if (Mathf.Abs(velocity.X) >= 0.01f && Mathf.Abs(velocity.Y) >= 0.01f)
  357. // {
  358. // AnimatedSprite.Play(AnimatorNames.Run);
  359. // BasisVelocity = velocity;
  360. // }
  361. // }
  362. }