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; }
  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. public Role() : this(ResourcePath.prefab_role_Role_tscn)
  207. {
  208. }
  209. public Role(string scenePath) : base(scenePath)
  210. {
  211. Holster = new Holster(this);
  212. }
  213. public override void _Ready()
  214. {
  215. base._Ready();
  216. _startScale = Scale;
  217. MountPoint = GetNode<MountRotation>("MountPoint");
  218. MountPoint.Master = this;
  219. BackMountPoint = GetNode<Marker2D>("BackMountPoint");
  220.  
  221. HurtArea = GetNode<Area2D>("HurtArea");
  222. HurtArea.CollisionLayer = CollisionLayer;
  223. HurtArea.CollisionMask = 0;
  224. Face = FaceDirection.Right;
  225.  
  226. //连接互动物体信号
  227. InteractiveArea = GetNode<Area2D>("InteractiveArea");
  228. InteractiveArea.AreaEntered += _OnPropsEnter;
  229. InteractiveArea.AreaExited += _OnPropsExit;
  230. }
  231.  
  232. protected override void Process(float delta)
  233. {
  234. //看向目标
  235. if (LookTarget != null)
  236. {
  237. Vector2 pos = LookTarget.GlobalPosition;
  238. //脸的朝向
  239. var gPos = GlobalPosition;
  240. if (pos.X > gPos.X && Face == FaceDirection.Left)
  241. {
  242. Face = FaceDirection.Right;
  243. }
  244. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  245. {
  246. Face = FaceDirection.Left;
  247. }
  248. //枪口跟随目标
  249. MountPoint.SetLookAt(pos);
  250. }
  251. //检查可互动的道具
  252. bool findFlag = false;
  253. for (int i = 0; i < _interactiveItemList.Count; i++)
  254. {
  255. var item = _interactiveItemList[i];
  256. if (item == null)
  257. {
  258. _interactiveItemList.RemoveAt(i--);
  259. }
  260. else
  261. {
  262. //找到可互动的道具了
  263. if (!findFlag)
  264. {
  265. var result = item.CheckInteractive(this);
  266. if (result.CanInteractive) //可以互动
  267. {
  268. findFlag = true;
  269. if (InteractiveItem != item) //更改互动物体
  270. {
  271. InteractiveItem = item;
  272. ChangeInteractiveItem(result);
  273. }
  274. else if (result.ShowIcon != _tempResultData.ShowIcon) //切换状态
  275. {
  276. ChangeInteractiveItem(result);
  277. }
  278. }
  279. _tempResultData = result;
  280. }
  281. }
  282. }
  283. //没有可互动的道具
  284. if (!findFlag && InteractiveItem != null)
  285. {
  286. InteractiveItem = null;
  287. ChangeInteractiveItem(null);
  288. }
  289. }
  290.  
  291. /// <summary>
  292. /// 获取当前角色的中心点坐标
  293. /// </summary>
  294. public Vector2 GetCenterPosition()
  295. {
  296. return MountPoint.GlobalPosition;
  297. }
  298.  
  299. /// <summary>
  300. /// 使角色看向指定的坐标,
  301. /// 注意, 调用该函数会清空 LookTarget, 因为拥有 LookTarget 时也会每帧更新玩家视野位置
  302. /// </summary>
  303. /// <param name="pos"></param>
  304. public void LookTargetPosition(Vector2 pos)
  305. {
  306. LookTarget = null;
  307. //脸的朝向
  308. var gPos = GlobalPosition;
  309. if (pos.X > gPos.X && Face == FaceDirection.Left)
  310. {
  311. Face = FaceDirection.Right;
  312. }
  313. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  314. {
  315. Face = FaceDirection.Left;
  316. }
  317. //枪口跟随目标
  318. MountPoint.SetLookAt(pos);
  319. }
  320. /// <summary>
  321. /// 判断指定坐标是否在角色视野方向
  322. /// </summary>
  323. public bool IsPositionInForward(Vector2 pos)
  324. {
  325. var gps = GlobalPosition;
  326. return (Face == FaceDirection.Left && pos.X <= gps.X) ||
  327. (Face == FaceDirection.Right && pos.X >= gps.X);
  328. }
  329.  
  330. /// <summary>
  331. /// 返回所有武器是否弹药都打光了
  332. /// </summary>
  333. public bool IsAllWeaponTotalAmmoEmpty()
  334. {
  335. foreach (var weaponSlot in Holster.SlotList)
  336. {
  337. if (weaponSlot.Weapon != null && !weaponSlot.Weapon.IsTotalAmmoEmpty())
  338. {
  339. return false;
  340. }
  341. }
  342.  
  343. return true;
  344. }
  345. /// <summary>
  346. /// 拾起一个武器, 返回是否成功拾取, 如果不想立刻切换到该武器, exchange 请传 false
  347. /// </summary>
  348. /// <param name="weapon">武器对象</param>
  349. /// <param name="exchange">是否立即切换到该武器, 默认 true </param>
  350. public virtual bool PickUpWeapon(Weapon weapon, bool exchange = true)
  351. {
  352. if (Holster.PickupWeapon(weapon, exchange) != -1)
  353. {
  354. //从可互动队列中移除
  355. _interactiveItemList.Remove(weapon);
  356. return true;
  357. }
  358.  
  359. return false;
  360. }
  361.  
  362. /// <summary>
  363. /// 切换到下一个武器
  364. /// </summary>
  365. public virtual void ExchangeNext()
  366. {
  367. Holster.ExchangeNext();
  368. }
  369.  
  370. /// <summary>
  371. /// 切换到上一个武器
  372. /// </summary>
  373. public virtual void ExchangePrev()
  374. {
  375. Holster.ExchangePrev();
  376. }
  377.  
  378. /// <summary>
  379. /// 扔掉当前使用的武器, 切换到上一个武器
  380. /// </summary>
  381. public virtual void ThrowWeapon()
  382. {
  383. ThrowWeapon(Holster.ActiveIndex);
  384. }
  385.  
  386. /// <summary>
  387. /// 扔掉指定位置的武器
  388. /// </summary>
  389. /// <param name="index">武器在武器袋中的位置</param>
  390. public virtual void ThrowWeapon(int index)
  391. {
  392. var weapon = Holster.GetWeapon(index);
  393. if (weapon == null)
  394. {
  395. return;
  396. }
  397.  
  398. var temp = new Vector2(weapon.Attribute.HoldPosition.X, 0);
  399. var pos = weapon.GlobalPosition + temp.Rotated(weapon.GlobalRotation);
  400. Holster.RemoveWeapon(index);
  401. //播放抛出效果
  402. weapon.ThrowWeapon(this, pos);
  403. }
  404.  
  405. /// <summary>
  406. /// 返回是否存在可互动的物体
  407. /// </summary>
  408. public bool HasInteractive()
  409. {
  410. return InteractiveItem != null;
  411. }
  412.  
  413. /// <summary>
  414. /// 触发与碰撞的物体互动, 并返回与其互动的物体
  415. /// </summary>
  416. public ActivityObject TriggerInteractive()
  417. {
  418. if (HasInteractive())
  419. {
  420. var item = InteractiveItem;
  421. item.Interactive(this);
  422. return item;
  423. }
  424.  
  425. return null;
  426. }
  427.  
  428. /// <summary>
  429. /// 触发换弹
  430. /// </summary>
  431. public virtual void Reload()
  432. {
  433. if (Holster.ActiveWeapon != null)
  434. {
  435. Holster.ActiveWeapon.Reload();
  436. }
  437. }
  438.  
  439. /// <summary>
  440. /// 触发攻击
  441. /// </summary>
  442. public virtual void Attack()
  443. {
  444. if (Holster.ActiveWeapon != null)
  445. {
  446. Holster.ActiveWeapon.Trigger();
  447. }
  448. }
  449.  
  450. /// <summary>
  451. /// 受到伤害, 如果是在碰撞信号处理函数中调用该函数, 请使用 CallDeferred 来延时调用, 否则很有可能导致报错
  452. /// </summary>
  453. /// <param name="damage">伤害的量</param>
  454. /// <param name="angle">角度</param>
  455. public virtual void Hurt(int damage, float angle)
  456. {
  457. OnHit(damage);
  458. if (Shield > 0)
  459. {
  460. Shield -= damage;
  461. }
  462. else
  463. {
  464. Hp -= damage;
  465. //播放血液效果
  466. // var packedScene = ResourceManager.Load<PackedScene>(ResourcePath.prefab_effect_Blood_tscn);
  467. // var blood = packedScene.Instance<Blood>();
  468. // blood.GlobalPosition = GlobalPosition;
  469. // blood.Rotation = angle;
  470. // GameApplication.Instance.Node3D.GetRoot().AddChild(blood);
  471. }
  472. //受伤特效
  473. PlayHitAnimation();
  474. //死亡判定
  475. if (Hp <= 0)
  476. {
  477. //死亡
  478. if (!IsDie)
  479. {
  480. IsDie = true;
  481. OnDie();
  482. }
  483. }
  484. }
  485.  
  486. /// <summary>
  487. /// 设置脸的朝向
  488. /// </summary>
  489. private void SetFace(FaceDirection face)
  490. {
  491. if (_face != face)
  492. {
  493. _face = face;
  494. if (face == FaceDirection.Right)
  495. {
  496. RotationDegrees = 0;
  497. Scale = _startScale;
  498. }
  499. else
  500. {
  501. RotationDegrees = 180;
  502. Scale = new Vector2(_startScale.X, -_startScale.Y);
  503. }
  504. }
  505. }
  506. /// <summary>
  507. /// 连接信号: InteractiveArea.area_entered
  508. /// 与物体碰撞
  509. /// </summary>
  510. private void _OnPropsEnter(Area2D other)
  511. {
  512. ActivityObject propObject = other.AsActivityObject();
  513. if (propObject != null)
  514. {
  515. if (!_interactiveItemList.Contains(propObject))
  516. {
  517. _interactiveItemList.Add(propObject);
  518. }
  519. }
  520. }
  521.  
  522. /// <summary>
  523. /// 连接信号: InteractiveArea.area_exited
  524. /// 物体离开碰撞区域
  525. /// </summary>
  526. private void _OnPropsExit(Area2D other)
  527. {
  528. ActivityObject propObject = other.AsActivityObject();
  529. if (propObject != null)
  530. {
  531. if (_interactiveItemList.Contains(propObject))
  532. {
  533. _interactiveItemList.Remove(propObject);
  534. }
  535. if (InteractiveItem == propObject)
  536. {
  537. InteractiveItem = null;
  538. ChangeInteractiveItem(null);
  539. }
  540. }
  541. }
  542. }