Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / role / Role.cs
  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. protected override void OnAffiliationChange()
  286. {
  287. //身上的武器的所属区域也得跟着变
  288. Holster.ForEach((weapon, i) =>
  289. {
  290. if (Affiliation != null)
  291. {
  292. Affiliation.InsertItem(weapon);
  293. }
  294. else if (weapon.Affiliation != null)
  295. {
  296. weapon.Affiliation.RemoveItem(weapon);
  297. }
  298. });
  299. }
  300.  
  301. /// <summary>
  302. /// 获取当前角色的中心点坐标
  303. /// </summary>
  304. public Vector2 GetCenterPosition()
  305. {
  306. return MountPoint.GlobalPosition;
  307. }
  308.  
  309. /// <summary>
  310. /// 使角色看向指定的坐标,
  311. /// 注意, 调用该函数会清空 LookTarget, 因为拥有 LookTarget 时也会每帧更新玩家视野位置
  312. /// </summary>
  313. /// <param name="pos"></param>
  314. public void LookTargetPosition(Vector2 pos)
  315. {
  316. LookTarget = null;
  317. //脸的朝向
  318. var gPos = GlobalPosition;
  319. if (pos.X > gPos.X && Face == FaceDirection.Left)
  320. {
  321. Face = FaceDirection.Right;
  322. }
  323. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  324. {
  325. Face = FaceDirection.Left;
  326. }
  327. //枪口跟随目标
  328. MountPoint.SetLookAt(pos);
  329. }
  330. /// <summary>
  331. /// 判断指定坐标是否在角色视野方向
  332. /// </summary>
  333. public bool IsPositionInForward(Vector2 pos)
  334. {
  335. var gps = GlobalPosition;
  336. return (Face == FaceDirection.Left && pos.X <= gps.X) ||
  337. (Face == FaceDirection.Right && pos.X >= gps.X);
  338. }
  339.  
  340. /// <summary>
  341. /// 返回所有武器是否弹药都打光了
  342. /// </summary>
  343. public bool IsAllWeaponTotalAmmoEmpty()
  344. {
  345. foreach (var weaponSlot in Holster.SlotList)
  346. {
  347. if (weaponSlot.Weapon != null && !weaponSlot.Weapon.IsTotalAmmoEmpty())
  348. {
  349. return false;
  350. }
  351. }
  352.  
  353. return true;
  354. }
  355. /// <summary>
  356. /// 拾起一个武器, 返回是否成功拾取, 如果不想立刻切换到该武器, exchange 请传 false
  357. /// </summary>
  358. /// <param name="weapon">武器对象</param>
  359. /// <param name="exchange">是否立即切换到该武器, 默认 true </param>
  360. public virtual bool PickUpWeapon(Weapon weapon, bool exchange = true)
  361. {
  362. if (Holster.PickupWeapon(weapon, exchange) != -1)
  363. {
  364. //从可互动队列中移除
  365. _interactiveItemList.Remove(weapon);
  366. return true;
  367. }
  368.  
  369. return false;
  370. }
  371.  
  372. /// <summary>
  373. /// 切换到下一个武器
  374. /// </summary>
  375. public virtual void ExchangeNext()
  376. {
  377. Holster.ExchangeNext();
  378. }
  379.  
  380. /// <summary>
  381. /// 切换到上一个武器
  382. /// </summary>
  383. public virtual void ExchangePrev()
  384. {
  385. Holster.ExchangePrev();
  386. }
  387.  
  388. /// <summary>
  389. /// 扔掉当前使用的武器, 切换到上一个武器
  390. /// </summary>
  391. public virtual void ThrowWeapon()
  392. {
  393. ThrowWeapon(Holster.ActiveIndex);
  394. }
  395.  
  396. /// <summary>
  397. /// 扔掉指定位置的武器
  398. /// </summary>
  399. /// <param name="index">武器在武器袋中的位置</param>
  400. public virtual void ThrowWeapon(int index)
  401. {
  402. var weapon = Holster.GetWeapon(index);
  403. if (weapon == null)
  404. {
  405. return;
  406. }
  407.  
  408. var temp = new Vector2(weapon.Attribute.HoldPosition.X, 0);
  409. var pos = weapon.GlobalPosition + temp.Rotated(weapon.GlobalRotation);
  410. Holster.RemoveWeapon(index);
  411. //播放抛出效果
  412. weapon.ThrowWeapon(this, pos);
  413. }
  414.  
  415. /// <summary>
  416. /// 返回是否存在可互动的物体
  417. /// </summary>
  418. public bool HasInteractive()
  419. {
  420. return InteractiveItem != null;
  421. }
  422.  
  423. /// <summary>
  424. /// 触发与碰撞的物体互动, 并返回与其互动的物体
  425. /// </summary>
  426. public ActivityObject TriggerInteractive()
  427. {
  428. if (HasInteractive())
  429. {
  430. var item = InteractiveItem;
  431. item.Interactive(this);
  432. return item;
  433. }
  434.  
  435. return null;
  436. }
  437.  
  438. /// <summary>
  439. /// 触发换弹
  440. /// </summary>
  441. public virtual void Reload()
  442. {
  443. if (Holster.ActiveWeapon != null)
  444. {
  445. Holster.ActiveWeapon.Reload();
  446. }
  447. }
  448.  
  449. /// <summary>
  450. /// 触发攻击
  451. /// </summary>
  452. public virtual void Attack()
  453. {
  454. if (Holster.ActiveWeapon != null)
  455. {
  456. Holster.ActiveWeapon.Trigger();
  457. }
  458. }
  459.  
  460. /// <summary>
  461. /// 受到伤害, 如果是在碰撞信号处理函数中调用该函数, 请使用 CallDeferred 来延时调用, 否则很有可能导致报错
  462. /// </summary>
  463. /// <param name="damage">伤害的量</param>
  464. /// <param name="angle">角度</param>
  465. public virtual void Hurt(int damage, float angle)
  466. {
  467. OnHit(damage);
  468. if (Shield > 0)
  469. {
  470. Shield -= damage;
  471. }
  472. else
  473. {
  474. Hp -= damage;
  475. //播放血液效果
  476. // var packedScene = ResourceManager.Load<PackedScene>(ResourcePath.prefab_effect_Blood_tscn);
  477. // var blood = packedScene.Instance<Blood>();
  478. // blood.GlobalPosition = GlobalPosition;
  479. // blood.Rotation = angle;
  480. // GameApplication.Instance.Node3D.GetRoot().AddChild(blood);
  481. }
  482. //受伤特效
  483. PlayHitAnimation();
  484. //死亡判定
  485. if (Hp <= 0)
  486. {
  487. //死亡
  488. if (!IsDie)
  489. {
  490. IsDie = true;
  491. OnDie();
  492. }
  493. }
  494. }
  495.  
  496. /// <summary>
  497. /// 设置脸的朝向
  498. /// </summary>
  499. private void SetFace(FaceDirection face)
  500. {
  501. if (_face != face)
  502. {
  503. _face = face;
  504. if (face == FaceDirection.Right)
  505. {
  506. RotationDegrees = 0;
  507. Scale = _startScale;
  508. }
  509. else
  510. {
  511. RotationDegrees = 180;
  512. Scale = new Vector2(_startScale.X, -_startScale.Y);
  513. }
  514. }
  515. }
  516. /// <summary>
  517. /// 连接信号: InteractiveArea.BodyEntered
  518. /// 与物体碰撞
  519. /// </summary>
  520. private void _OnPropsEnter(Node2D other)
  521. {
  522. if (other is ActivityObject propObject)
  523. {
  524. if (!_interactiveItemList.Contains(propObject))
  525. {
  526. _interactiveItemList.Add(propObject);
  527. }
  528. }
  529. }
  530.  
  531. /// <summary>
  532. /// 连接信号: InteractiveArea.BodyExited
  533. /// 物体离开碰撞区域
  534. /// </summary>
  535. private void _OnPropsExit(Node2D other)
  536. {
  537. if (other is ActivityObject propObject)
  538. {
  539. if (_interactiveItemList.Contains(propObject))
  540. {
  541. _interactiveItemList.Remove(propObject);
  542. }
  543. if (InteractiveItem == propObject)
  544. {
  545. InteractiveItem = null;
  546. ChangeInteractiveItem(null);
  547. }
  548. }
  549. }
  550. }