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