Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / role / Role.cs
@小李xl 小李xl on 10 May 2023 15 KB 添加敌人死亡特效
  1. using System.Collections.Generic;
  2. using Godot;
  3.  
  4. /// <summary>
  5. /// 角色基类
  6. /// </summary>
  7. public abstract partial class Role : ActivityObject
  8. {
  9. /// <summary>
  10. /// 是否是 Ai
  11. /// </summary>
  12. public bool IsAi { get; protected set; } = false;
  13. /// <summary>
  14. /// 默认攻击对象层级
  15. /// </summary>
  16. public const uint DefaultAttackLayer = PhysicsLayer.Player | PhysicsLayer.Enemy | PhysicsLayer.Wall | PhysicsLayer.Props;
  17. /// <summary>
  18. /// 伤害区域
  19. /// </summary>
  20. public Area2D HurtArea { get; private set; }
  21.  
  22. /// <summary>
  23. /// 移动速度
  24. /// </summary>
  25. public float MoveSpeed = 120f;
  26.  
  27. /// <summary>
  28. /// 所属阵营
  29. /// </summary>
  30. public CampEnum Camp;
  31.  
  32. /// <summary>
  33. /// 攻击目标的碰撞器所属层级, 数据源自于: PhysicsLayer
  34. /// </summary>
  35. public uint AttackLayer { get; set; } = PhysicsLayer.Wall;
  36.  
  37. // /// <summary>
  38. // /// 携带的道具包裹
  39. // /// </summary>
  40. // public List<object> PropsPack { get; } = new List<object>();
  41.  
  42. /// <summary>
  43. /// 角色携带的武器袋
  44. /// </summary>
  45. public Holster Holster { get; private set; }
  46.  
  47. /// <summary>
  48. /// 武器挂载点
  49. /// </summary>
  50. public MountRotation MountPoint { get; private set; }
  51. /// <summary>
  52. /// 背后武器的挂载点
  53. /// </summary>
  54. public Marker2D BackMountPoint { get; private set; }
  55.  
  56. /// <summary>
  57. /// 互动碰撞区域
  58. /// </summary>
  59. public Area2D InteractiveArea { get; private set; }
  60. /// <summary>
  61. /// 脸的朝向
  62. /// </summary>
  63. public FaceDirection Face { get => _face; set => SetFace(value); }
  64. private FaceDirection _face;
  65.  
  66. /// <summary>
  67. /// 是否死亡
  68. /// </summary>
  69. public bool IsDie { get; private set; }
  70. /// <summary>
  71. /// 血量
  72. /// </summary>
  73. public int Hp
  74. {
  75. get => _hp;
  76. protected set
  77. {
  78. int temp = _hp;
  79. _hp = value;
  80. if (temp != _hp) OnChangeHp(_hp);
  81. }
  82. }
  83. private int _hp = 20;
  84.  
  85. /// <summary>
  86. /// 最大血量
  87. /// </summary>
  88. public int MaxHp
  89. {
  90. get => _maxHp;
  91. protected set
  92. {
  93. int temp = _maxHp;
  94. _maxHp = value;
  95. //护盾值改变
  96. if (temp != _maxHp) OnChangeMaxHp(_maxHp);
  97. }
  98. }
  99. private int _maxHp = 20;
  100. /// <summary>
  101. /// 当前护盾值
  102. /// </summary>
  103. public int Shield
  104. {
  105. get => _shield;
  106. protected set
  107. {
  108. int temp = _shield;
  109. _shield = value;
  110. //护盾被破坏
  111. if (temp > 0 && _shield <= 0) OnShieldDamage();
  112. //护盾值改变
  113. if (temp != _shield) OnChangeShield(_shield);
  114. }
  115. }
  116. private int _shield = 0;
  117.  
  118. /// <summary>
  119. /// 最大护盾值
  120. /// </summary>
  121. public int MaxShield
  122. {
  123. get => _maxShield;
  124. protected set
  125. {
  126. int temp = _maxShield;
  127. _maxShield = value;
  128. if (temp != _maxShield) OnChangeMaxShield(_maxShield);
  129. }
  130. }
  131. private int _maxShield = 0;
  132.  
  133. /// <summary>
  134. /// 当前角色所看向的对象, 也就是枪口指向的对象
  135. /// </summary>
  136. public ActivityObject LookTarget { get; set; }
  137.  
  138. //初始缩放
  139. private Vector2 _startScale;
  140. //所有角色碰撞的道具
  141. private readonly List<ActivityObject> _interactiveItemList = new List<ActivityObject>();
  142.  
  143. private CheckInteractiveResult _tempResultData;
  144.  
  145. /// <summary>
  146. /// 可以互动的道具
  147. /// </summary>
  148. public ActivityObject InteractiveItem { get; private set; }
  149.  
  150. /// <summary>
  151. /// 当血量改变时调用
  152. /// </summary>
  153. protected virtual void OnChangeHp(int hp)
  154. {
  155. }
  156.  
  157. /// <summary>
  158. /// 当最大血量改变时调用
  159. /// </summary>
  160. protected virtual void OnChangeMaxHp(int maxHp)
  161. {
  162. }
  163. /// <summary>
  164. /// 护盾值改变时调用
  165. /// </summary>
  166. protected virtual void OnChangeShield(int shield)
  167. {
  168. }
  169.  
  170. /// <summary>
  171. /// 最大护盾值改变时调用
  172. /// </summary>
  173. protected virtual void OnChangeMaxShield(int maxShield)
  174. {
  175. }
  176.  
  177. /// <summary>
  178. /// 当护盾被破坏时调用
  179. /// </summary>
  180. protected virtual void OnShieldDamage()
  181. {
  182. }
  183.  
  184. /// <summary>
  185. /// 当受伤时调用
  186. /// </summary>
  187. /// <param name="damage">受到的伤害</param>
  188. protected virtual void OnHit(int damage)
  189. {
  190. }
  191.  
  192. /// <summary>
  193. /// 当可互动的物体改变时调用, result 参数为 null 表示变为不可互动
  194. /// </summary>
  195. /// <param name="result">检测是否可互动时的返回值</param>
  196. protected virtual void ChangeInteractiveItem(CheckInteractiveResult result)
  197. {
  198. }
  199.  
  200. /// <summary>
  201. /// 死亡时调用
  202. /// </summary>
  203. protected virtual void OnDie()
  204. {
  205. }
  206.  
  207. public override void OnInit()
  208. {
  209. Holster = new Holster(this);
  210. _startScale = Scale;
  211. MountPoint = GetNode<MountRotation>("MountPoint");
  212. MountPoint.Master = this;
  213. BackMountPoint = GetNode<Marker2D>("BackMountPoint");
  214.  
  215. HurtArea = GetNode<Area2D>("HurtArea");
  216. HurtArea.CollisionLayer = CollisionLayer;
  217. HurtArea.CollisionMask = 0;
  218. Face = FaceDirection.Right;
  219.  
  220. //连接互动物体信号
  221. InteractiveArea = GetNode<Area2D>("InteractiveArea");
  222. InteractiveArea.BodyEntered += _OnPropsEnter;
  223. InteractiveArea.BodyExited += _OnPropsExit;
  224. }
  225.  
  226. protected override void Process(float delta)
  227. {
  228. //看向目标
  229. if (LookTarget != null)
  230. {
  231. Vector2 pos = LookTarget.GlobalPosition;
  232. //脸的朝向
  233. var gPos = GlobalPosition;
  234. if (pos.X > gPos.X && Face == FaceDirection.Left)
  235. {
  236. Face = FaceDirection.Right;
  237. }
  238. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  239. {
  240. Face = FaceDirection.Left;
  241. }
  242. //枪口跟随目标
  243. MountPoint.SetLookAt(pos);
  244. }
  245. //检查可互动的道具
  246. bool findFlag = false;
  247. for (int i = 0; i < _interactiveItemList.Count; i++)
  248. {
  249. var item = _interactiveItemList[i];
  250. if (item == null || item.IsDestroyed)
  251. {
  252. _interactiveItemList.RemoveAt(i--);
  253. }
  254. else
  255. {
  256. //找到可互动的道具了
  257. if (!findFlag)
  258. {
  259. var result = item.CheckInteractive(this);
  260. if (result.CanInteractive) //可以互动
  261. {
  262. findFlag = true;
  263. if (InteractiveItem != item) //更改互动物体
  264. {
  265. InteractiveItem = item;
  266. ChangeInteractiveItem(result);
  267. }
  268. else if (result.ShowIcon != _tempResultData.ShowIcon) //切换状态
  269. {
  270. ChangeInteractiveItem(result);
  271. }
  272. }
  273. _tempResultData = result;
  274. }
  275. }
  276. }
  277. //没有可互动的道具
  278. if (!findFlag && InteractiveItem != null)
  279. {
  280. InteractiveItem = null;
  281. ChangeInteractiveItem(null);
  282. }
  283. }
  284.  
  285. /// <summary>
  286. /// 当武器放到后背时调用, 用于设置武器位置和角度
  287. /// </summary>
  288. /// <param name="weapon">武器实例</param>
  289. /// <param name="index">放入武器袋的位置</param>
  290. public virtual void OnPutBackMount(Weapon weapon, int index)
  291. {
  292. if (index < 8)
  293. {
  294. if (index % 2 == 0)
  295. {
  296. weapon.Position = new Vector2(-4, 3);
  297. weapon.RotationDegrees = 90 - (index / 2f) * 20;
  298. weapon.Scale = new Vector2(-1, 1);
  299. }
  300. else
  301. {
  302. weapon.Position = new Vector2(4, 3);
  303. weapon.RotationDegrees = 270 + (index - 1) / 2f * 20;
  304. weapon.Scale = new Vector2(1, 1);
  305. }
  306. }
  307. else
  308. {
  309. weapon.Visible = false;
  310. }
  311. }
  312. protected override void OnAffiliationChange()
  313. {
  314. //身上的武器的所属区域也得跟着变
  315. Holster.ForEach((weapon, i) =>
  316. {
  317. if (Affiliation != null)
  318. {
  319. Affiliation.InsertItem(weapon);
  320. }
  321. else if (weapon.Affiliation != null)
  322. {
  323. weapon.Affiliation.RemoveItem(weapon);
  324. }
  325. });
  326. }
  327.  
  328. /// <summary>
  329. /// 获取当前角色的中心点坐标
  330. /// </summary>
  331. public Vector2 GetCenterPosition()
  332. {
  333. return MountPoint.GlobalPosition;
  334. }
  335.  
  336. /// <summary>
  337. /// 使角色看向指定的坐标,
  338. /// 注意, 调用该函数会清空 LookTarget, 因为拥有 LookTarget 时也会每帧更新玩家视野位置
  339. /// </summary>
  340. /// <param name="pos"></param>
  341. public void LookTargetPosition(Vector2 pos)
  342. {
  343. LookTarget = null;
  344. //脸的朝向
  345. var gPos = GlobalPosition;
  346. if (pos.X > gPos.X && Face == FaceDirection.Left)
  347. {
  348. Face = FaceDirection.Right;
  349. }
  350. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  351. {
  352. Face = FaceDirection.Left;
  353. }
  354. //枪口跟随目标
  355. MountPoint.SetLookAt(pos);
  356. }
  357. /// <summary>
  358. /// 判断指定坐标是否在角色视野方向
  359. /// </summary>
  360. public bool IsPositionInForward(Vector2 pos)
  361. {
  362. var gps = GlobalPosition;
  363. return (Face == FaceDirection.Left && pos.X <= gps.X) ||
  364. (Face == FaceDirection.Right && pos.X >= gps.X);
  365. }
  366.  
  367. /// <summary>
  368. /// 返回所有武器是否弹药都打光了
  369. /// </summary>
  370. public bool IsAllWeaponTotalAmmoEmpty()
  371. {
  372. foreach (var weapon in Holster.Weapons)
  373. {
  374. if (weapon != null && !weapon.IsTotalAmmoEmpty())
  375. {
  376. return false;
  377. }
  378. }
  379.  
  380. return true;
  381. }
  382. /// <summary>
  383. /// 拾起一个武器, 返回是否成功拾取, 如果不想立刻切换到该武器, exchange 请传 false
  384. /// </summary>
  385. /// <param name="weapon">武器对象</param>
  386. /// <param name="exchange">是否立即切换到该武器, 默认 true </param>
  387. public virtual bool PickUpWeapon(Weapon weapon, bool exchange = true)
  388. {
  389. if (Holster.PickupWeapon(weapon, exchange) != -1)
  390. {
  391. //从可互动队列中移除
  392. _interactiveItemList.Remove(weapon);
  393. return true;
  394. }
  395.  
  396. return false;
  397. }
  398.  
  399. /// <summary>
  400. /// 切换到下一个武器
  401. /// </summary>
  402. public virtual void ExchangeNext()
  403. {
  404. Holster.ExchangeNext();
  405. }
  406.  
  407. /// <summary>
  408. /// 切换到上一个武器
  409. /// </summary>
  410. public virtual void ExchangePrev()
  411. {
  412. Holster.ExchangePrev();
  413. }
  414.  
  415. /// <summary>
  416. /// 扔掉当前使用的武器, 切换到上一个武器
  417. /// </summary>
  418. public virtual void ThrowWeapon()
  419. {
  420. ThrowWeapon(Holster.ActiveIndex);
  421. }
  422.  
  423. /// <summary>
  424. /// 扔掉指定位置的武器
  425. /// </summary>
  426. /// <param name="index">武器在武器袋中的位置</param>
  427. public virtual void ThrowWeapon(int index)
  428. {
  429. var weapon = Holster.GetWeapon(index);
  430. if (weapon == null)
  431. {
  432. return;
  433. }
  434.  
  435. var temp = new Vector2(weapon.Attribute.HoldPosition.X, 0);
  436. var pos = weapon.GlobalPosition + temp.Rotated(weapon.GlobalRotation);
  437. Holster.RemoveWeapon(index);
  438. //播放抛出效果
  439. weapon.ThrowWeapon(this, pos);
  440. }
  441.  
  442. /// <summary>
  443. /// 返回是否存在可互动的物体
  444. /// </summary>
  445. public bool HasInteractive()
  446. {
  447. return InteractiveItem != null;
  448. }
  449.  
  450. /// <summary>
  451. /// 触发与碰撞的物体互动, 并返回与其互动的物体
  452. /// </summary>
  453. public ActivityObject TriggerInteractive()
  454. {
  455. if (HasInteractive())
  456. {
  457. var item = InteractiveItem;
  458. item.Interactive(this);
  459. return item;
  460. }
  461.  
  462. return null;
  463. }
  464.  
  465. /// <summary>
  466. /// 触发换弹
  467. /// </summary>
  468. public virtual void Reload()
  469. {
  470. if (Holster.ActiveWeapon != null)
  471. {
  472. Holster.ActiveWeapon.Reload();
  473. }
  474. }
  475.  
  476. /// <summary>
  477. /// 触发攻击
  478. /// </summary>
  479. public virtual void Attack()
  480. {
  481. if (Holster.ActiveWeapon != null)
  482. {
  483. Holster.ActiveWeapon.Trigger();
  484. }
  485. }
  486.  
  487. /// <summary>
  488. /// 受到伤害, 如果是在碰撞信号处理函数中调用该函数, 请使用 CallDeferred 来延时调用, 否则很有可能导致报错
  489. /// </summary>
  490. /// <param name="damage">伤害的量</param>
  491. /// <param name="angle">角度</param>
  492. public virtual void Hurt(int damage, float angle)
  493. {
  494. OnHit(damage);
  495. if (Shield > 0)
  496. {
  497. Shield -= damage;
  498. }
  499. else
  500. {
  501. Hp -= damage;
  502. //播放血液效果
  503. // var packedScene = ResourceManager.Load<PackedScene>(ResourcePath.prefab_effect_Blood_tscn);
  504. // var blood = packedScene.Instance<Blood>();
  505. // blood.GlobalPosition = GlobalPosition;
  506. // blood.Rotation = angle;
  507. // GameApplication.Instance.Node3D.GetRoot().AddChild(blood);
  508. }
  509. //受伤特效
  510. PlayHitAnimation();
  511. //死亡判定
  512. if (Hp <= 0)
  513. {
  514. //死亡
  515. if (!IsDie)
  516. {
  517. IsDie = true;
  518. OnDie();
  519. }
  520. }
  521. }
  522.  
  523. /// <summary>
  524. /// 设置脸的朝向
  525. /// </summary>
  526. private void SetFace(FaceDirection face)
  527. {
  528. if (_face != face)
  529. {
  530. _face = face;
  531. if (face == FaceDirection.Right)
  532. {
  533. RotationDegrees = 0;
  534. Scale = _startScale;
  535. }
  536. else
  537. {
  538. RotationDegrees = 180;
  539. Scale = new Vector2(_startScale.X, -_startScale.Y);
  540. }
  541. }
  542. }
  543. /// <summary>
  544. /// 连接信号: InteractiveArea.BodyEntered
  545. /// 与物体碰撞
  546. /// </summary>
  547. private void _OnPropsEnter(Node2D other)
  548. {
  549. if (other is ActivityObject propObject)
  550. {
  551. if (!_interactiveItemList.Contains(propObject))
  552. {
  553. _interactiveItemList.Add(propObject);
  554. }
  555. }
  556. }
  557.  
  558. /// <summary>
  559. /// 连接信号: InteractiveArea.BodyExited
  560. /// 物体离开碰撞区域
  561. /// </summary>
  562. private void _OnPropsExit(Node2D other)
  563. {
  564. if (other is ActivityObject propObject)
  565. {
  566. if (_interactiveItemList.Contains(propObject))
  567. {
  568. _interactiveItemList.Remove(propObject);
  569. }
  570. if (InteractiveItem == propObject)
  571. {
  572. InteractiveItem = null;
  573. ChangeInteractiveItem(null);
  574. }
  575. }
  576. }
  577. }