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