Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / activity / role / ai / AiRole.cs
  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. /// 获取攻击的目标对象, 该函数不能返回 null
  95. /// </summary>
  96. public virtual Role GetAttackTarget()
  97. {
  98. return World.Player;
  99. }
  100. /// <summary>
  101. /// 返回地上的武器是否有可以拾取的, 也包含没有被其他敌人标记的武器
  102. /// </summary>
  103. public bool CheckUsableWeaponInUnclaimed()
  104. {
  105. foreach (var unclaimedWeapon in World.Weapon_UnclaimedWeapons)
  106. {
  107. //判断是否能拾起武器, 条件: 相同的房间
  108. if (unclaimedWeapon.AffiliationArea == AffiliationArea)
  109. {
  110. if (!unclaimedWeapon.IsTotalAmmoEmpty())
  111. {
  112. if (!unclaimedWeapon.HasSign(SignNames.AiFindWeaponSign))
  113. {
  114. return true;
  115. }
  116. else
  117. {
  118. //判断是否可以移除该标记
  119. var enemy = unclaimedWeapon.GetSign<Enemy>(SignNames.AiFindWeaponSign);
  120. if (enemy == null || enemy.IsDestroyed) //标记当前武器的敌人已经被销毁
  121. {
  122. unclaimedWeapon.RemoveSign(SignNames.AiFindWeaponSign);
  123. return true;
  124. }
  125. else if (!enemy.IsAllWeaponTotalAmmoEmpty()) //标记当前武器的敌人已经有新的武器了
  126. {
  127. unclaimedWeapon.RemoveSign(SignNames.AiFindWeaponSign);
  128. return true;
  129. }
  130. }
  131. }
  132. }
  133. }
  134.  
  135. return false;
  136. }
  137. /// <summary>
  138. /// 寻找可用的武器
  139. /// </summary>
  140. public Weapon FindTargetWeapon()
  141. {
  142. Weapon target = null;
  143. var position = Position;
  144. foreach (var weapon in World.Weapon_UnclaimedWeapons)
  145. {
  146. //判断是否能拾起武器, 条件: 相同的房间, 或者当前房间目前没有战斗, 或者不在战斗房间
  147. if (weapon.AffiliationArea == AffiliationArea)
  148. {
  149. //还有弹药
  150. if (!weapon.IsTotalAmmoEmpty())
  151. {
  152. //查询是否有其他敌人标记要拾起该武器
  153. if (weapon.HasSign(SignNames.AiFindWeaponSign))
  154. {
  155. var enemy = weapon.GetSign<Enemy>(SignNames.AiFindWeaponSign);
  156. if (enemy == this) //就是自己标记的
  157. {
  158.  
  159. }
  160. else if (enemy == null || enemy.IsDestroyed) //标记当前武器的敌人已经被销毁
  161. {
  162. weapon.RemoveSign(SignNames.AiFindWeaponSign);
  163. }
  164. else if (!enemy.IsAllWeaponTotalAmmoEmpty()) //标记当前武器的敌人已经有新的武器了
  165. {
  166. weapon.RemoveSign(SignNames.AiFindWeaponSign);
  167. }
  168. else //放弃这把武器
  169. {
  170. continue;
  171. }
  172. }
  173.  
  174. if (target == null) //第一把武器
  175. {
  176. target = weapon;
  177. }
  178. else if (target.Position.DistanceSquaredTo(position) >
  179. weapon.Position.DistanceSquaredTo(position)) //距离更近
  180. {
  181. target = weapon;
  182. }
  183. }
  184. }
  185. }
  186.  
  187. return target;
  188. }
  189.  
  190. /// <summary>
  191. /// 获取武器攻击范围 (最大距离值与最小距离的中间值)
  192. /// </summary>
  193. /// <param name="weight">从最小到最大距离的过渡量, 0 - 1, 默认 0.5</param>
  194. public float GetWeaponRange(float weight = 0.5f)
  195. {
  196. if (WeaponPack.ActiveItem != null)
  197. {
  198. var attribute = WeaponPack.ActiveItem.Attribute;
  199. return Mathf.Lerp(Utils.GetConfigRangeStart(attribute.Bullet.DistanceRange), Utils.GetConfigRangeEnd(attribute.Bullet.DistanceRange), weight);
  200. }
  201.  
  202. return 0;
  203. }
  204.  
  205. /// <summary>
  206. /// 返回目标点是否在视野范围内
  207. /// </summary>
  208. public virtual bool IsInViewRange(Vector2 target)
  209. {
  210. var isForward = IsPositionInForward(target);
  211. if (isForward)
  212. {
  213. if (GlobalPosition.DistanceSquaredTo(target) <= ViewRange * ViewRange) //没有超出视野半径
  214. {
  215. return true;
  216. }
  217. }
  218.  
  219. return false;
  220. }
  221.  
  222. /// <summary>
  223. /// 返回目标点是否在跟随状态下的视野半径内
  224. /// </summary>
  225. public virtual bool IsInTailAfterViewRange(Vector2 target)
  226. {
  227. var isForward = IsPositionInForward(target);
  228. if (isForward)
  229. {
  230. if (GlobalPosition.DistanceSquaredTo(target) <= TailAfterViewRange * TailAfterViewRange) //没有超出视野半径
  231. {
  232. return true;
  233. }
  234. }
  235.  
  236. return false;
  237. }
  238.  
  239. /// <summary>
  240. /// 调用视野检测, 如果被墙壁和其它物体遮挡, 则返回true
  241. /// </summary>
  242. public bool TestViewRayCast(Vector2 target)
  243. {
  244. ViewRay.Enabled = true;
  245. ViewRay.TargetPosition = ViewRay.ToLocal(target);
  246. ViewRay.ForceRaycastUpdate();
  247. return ViewRay.IsColliding();
  248. }
  249.  
  250. /// <summary>
  251. /// 调用视野检测完毕后, 需要调用 TestViewRayCastOver() 来关闭视野检测射线
  252. /// </summary>
  253. public void TestViewRayCastOver()
  254. {
  255. ViewRay.Enabled = false;
  256. }
  257.  
  258. /// <summary>
  259. /// AI 拾起武器操作
  260. /// </summary>
  261. public void DoPickUpWeapon()
  262. {
  263. //这几个状态不需要主动拾起武器操作
  264. var state = StateController.CurrState;
  265. if (state == AIStateEnum.AiNormal || state == AIStateEnum.AiNotify || state == AIStateEnum.AiAstonished || state == AIStateEnum.AiAttack)
  266. {
  267. return;
  268. }
  269. //拾起地上的武器
  270. if (InteractiveItem is Weapon weapon)
  271. {
  272. if (WeaponPack.ActiveItem == null) //手上没有武器, 无论如何也要拾起
  273. {
  274. TriggerInteractive();
  275. return;
  276. }
  277.  
  278. //没弹药了
  279. if (weapon.IsTotalAmmoEmpty())
  280. {
  281. return;
  282. }
  283. var index = WeaponPack.FindIndex((we, i) => we.ActivityBase.Id == weapon.ActivityBase.Id);
  284. if (index != -1) //与武器背包中武器类型相同, 补充子弹
  285. {
  286. if (!WeaponPack.GetItem(index).IsAmmoFull())
  287. {
  288. TriggerInteractive();
  289. }
  290.  
  291. return;
  292. }
  293.  
  294. // var index2 = Holster.FindWeapon((we, i) =>
  295. // we.Attribute.WeightType == weapon.Attribute.WeightType && we.IsTotalAmmoEmpty());
  296. var index2 = WeaponPack.FindIndex((we, i) => we.IsTotalAmmoEmpty());
  297. if (index2 != -1) //扔掉没子弹的武器
  298. {
  299. ThrowWeapon(index2);
  300. TriggerInteractive();
  301. return;
  302. }
  303. // if (Holster.HasVacancy()) //有空位, 拾起武器
  304. // {
  305. // TriggerInteractive();
  306. // return;
  307. // }
  308. }
  309. }
  310. /// <summary>
  311. /// 获取锁定目标的剩余时间
  312. /// </summary>
  313. public float GetLockRemainderTime()
  314. {
  315. var weapon = WeaponPack.ActiveItem;
  316. if (weapon == null)
  317. {
  318. return LockingTime - LockTargetTime;
  319. }
  320. return weapon.Attribute.AiAttackAttr.LockingTime - LockTargetTime;
  321. }
  322.  
  323. public override void LookTargetPosition(Vector2 pos)
  324. {
  325. LookTarget = null;
  326. base.LookTargetPosition(pos);
  327. }
  328. /// <summary>
  329. /// 执行移动操作
  330. /// </summary>
  331. public void DoMove()
  332. {
  333. // //计算移动
  334. // NavigationAgent2D.MaxSpeed = EnemyRoleState.MoveSpeed;
  335. // var nextPos = NavigationAgent2D.GetNextPathPosition();
  336. // NavigationAgent2D.Velocity = (nextPos - Position - NavigationPoint.Position).Normalized() * RoleState.MoveSpeed;
  337. AnimatedSprite.Play(AnimatorNames.Run);
  338. //计算移动
  339. var nextPos = NavigationAgent2D.GetNextPathPosition();
  340. BasisVelocity = (nextPos - Position - NavigationPoint.Position).Normalized() * RoleState.MoveSpeed;
  341. }
  342.  
  343. /// <summary>
  344. /// 执行站立操作
  345. /// </summary>
  346. public void DoIdle()
  347. {
  348. AnimatedSprite.Play(AnimatorNames.Idle);
  349. BasisVelocity = Vector2.Zero;
  350. }
  351. /// <summary>
  352. /// 更新房间中标记的目标位置
  353. /// </summary>
  354. public void UpdateMarkTargetPosition()
  355. {
  356. if (LookTarget != null)
  357. {
  358. AffiliationArea.RoomInfo.MarkTargetPosition[LookTarget.Id] = LookTarget.Position;
  359. }
  360. }
  361.  
  362. protected override void OnDie()
  363. {
  364. //扔掉所有武器
  365. ThrowAllWeapon();
  366. //创建金币
  367. Gold.CreateGold(Position, RoleState.Gold);
  368. //销毁
  369. Destroy();
  370. }
  371.  
  372. // private void OnVelocityComputed(Vector2 velocity)
  373. // {
  374. // if (Mathf.Abs(velocity.X) >= 0.01f && Mathf.Abs(velocity.Y) >= 0.01f)
  375. // {
  376. // AnimatedSprite.Play(AnimatorNames.Run);
  377. // BasisVelocity = velocity;
  378. // }
  379. // }
  380. }