Newer
Older
DungeonShooting / DungeonShooting_Godot / src / weapon / gun / Gun.cs
@小李xl 小李xl on 17 Aug 2022 19 KB 武器互动bug修复
  1. using Godot;
  2. using System;
  3.  
  4. /// <summary>
  5. /// 枪的基类
  6. /// </summary>
  7. public abstract class Gun : Area2D, IProp
  8. {
  9. /// <summary>
  10. /// 开火回调事件
  11. /// </summary>
  12. public event Action<Gun> FireEvent;
  13.  
  14. /// <summary>
  15. /// 属性数据
  16. /// </summary>
  17. public GunAttribute Attribute
  18. {
  19. get
  20. {
  21. if (_attribute == null)
  22. {
  23. throw new Exception("请先调用Init来初始化枪的属性");
  24. }
  25. return _attribute;
  26. }
  27. private set => _attribute = value;
  28. }
  29. private GunAttribute _attribute;
  30.  
  31. /// <summary>
  32. /// 枪的图片
  33. /// </summary>
  34. public Sprite GunSprite { get; private set; }
  35.  
  36. /// <summary>
  37. /// 动画播放器
  38. /// </summary>
  39. /// <value></value>
  40. public AnimationPlayer AnimationPlayer { get; private set; }
  41.  
  42. /// <summary>
  43. /// 枪攻击的目标阵营
  44. /// </summary>
  45. public CampEnum TargetCamp { get; set; }
  46.  
  47. /// <summary>
  48. /// 该武器的拥有者
  49. /// </summary>
  50. public Role Master { get; private set; }
  51.  
  52. /// <summary>
  53. /// 当前弹夹弹药剩余量
  54. /// </summary>
  55. public int CurrAmmo { get; private set; }
  56. /// <summary>
  57. /// 剩余弹药量
  58. /// </summary>
  59. public int ResidueAmmo { get; private set; }
  60.  
  61. /// <summary>
  62. /// 枪管的开火点
  63. /// </summary>
  64. public Position2D FirePoint { get; private set; }
  65. /// <summary>
  66. /// 枪管的原点
  67. /// </summary>
  68. public Position2D OriginPoint { get; private set; }
  69. /// <summary>
  70. /// 弹壳抛出的点
  71. /// </summary>
  72. public Position2D ShellPoint { get; private set; }
  73. /// <summary>
  74. /// 碰撞器节点
  75. /// </summary>
  76. /// <value></value>
  77. public CollisionShape2D CollisionShape2D { get; private set; }
  78. /// <summary>
  79. /// 枪的当前散射半径
  80. /// </summary>
  81. public float CurrScatteringRange { get; private set; } = 0;
  82. /// <summary>
  83. /// 是否在换弹中
  84. /// </summary>
  85. /// <value></value>
  86. public bool Reloading { get; private set; } = false;
  87. /// <summary>
  88. /// 换弹计时器
  89. /// </summary>
  90. public float ReloadTimer { get; private set; } = 0;
  91.  
  92. //是否按下
  93. private bool triggerFlag = false;
  94. //扳机计时器
  95. private float triggerTimer = 0;
  96. //开火前延时时间
  97. private float delayedTime = 0;
  98. //开火间隙时间
  99. private float fireInterval = 0;
  100. //开火枪口角度
  101. private float fireAngle = 0;
  102. //攻击冷却计时
  103. private float attackTimer = 0;
  104. //攻击状态
  105. private bool attackFlag = false;
  106. //按下的时间
  107. private float downTimer = 0;
  108. //松开的时间
  109. private float upTimer = 0;
  110. //连发次数
  111. private float continuousCount = 0;
  112. //连发状态记录
  113. private bool continuousShootFlag = false;
  114.  
  115. /// <summary>
  116. /// 初始化时调用
  117. /// </summary>
  118. protected abstract void Init();
  119.  
  120. /// <summary>
  121. /// 单次开火时调用的函数
  122. /// </summary>
  123. protected abstract void OnFire();
  124.  
  125. /// <summary>
  126. /// 换弹时调用
  127. /// </summary>
  128. protected abstract void OnReload();
  129.  
  130. /// <summary>
  131. /// 发射子弹时调用的函数, 每发射一枚子弹调用一次,
  132. /// 如果做霰弹枪效果, 一次开火发射5枚子弹, 则该函数调用5次
  133. /// </summary>
  134. protected abstract void OnShootBullet();
  135.  
  136. /// <summary>
  137. /// 当武器被拾起时调用
  138. /// </summary>
  139. /// <param name="master">拾起该武器的角色</param>
  140. protected abstract void OnPickUp(Role master);
  141.  
  142. /// <summary>
  143. /// 当武器被扔掉时调用
  144. /// </summary>
  145. protected abstract void OnThrowOut();
  146.  
  147. /// <summary>
  148. /// 当武器被激活时调用, 也就是使用当武器是调用
  149. /// </summary>
  150. protected abstract void OnActive();
  151.  
  152. /// <summary>
  153. /// 当武器被收起时调用
  154. /// </summary>
  155. protected abstract void OnConceal();
  156.  
  157. public override void _Process(float delta)
  158. {
  159. if (Master == null) //这把武器被扔在地上
  160. {
  161. Reloading = false;
  162. triggerTimer = triggerTimer > 0 ? triggerTimer - delta : 0;
  163. triggerFlag = false;
  164. attackFlag = false;
  165. attackTimer = attackTimer > 0 ? attackTimer - delta : 0;
  166. CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta, Attribute.StartScatteringRange);
  167. continuousCount = 0;
  168. delayedTime = 0;
  169. }
  170. else if (Master.Holster.ActiveGun != this) //当前武器没有被使用
  171. {
  172. Reloading = false;
  173. triggerTimer = triggerTimer > 0 ? triggerTimer - delta : 0;
  174. triggerFlag = false;
  175. attackFlag = false;
  176. attackTimer = attackTimer > 0 ? attackTimer - delta : 0;
  177. CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta, Attribute.StartScatteringRange);
  178. continuousCount = 0;
  179. delayedTime = 0;
  180. }
  181. else //正在使用中
  182. {
  183.  
  184. //换弹
  185. if (Reloading)
  186. {
  187. ReloadTimer -= delta;
  188. if (ReloadTimer <= 0)
  189. {
  190. ReloadTimer = 0;
  191. ReloadSuccess();
  192. }
  193. }
  194.  
  195. if (triggerFlag)
  196. {
  197. if (upTimer > 0) //第一帧按下扳机
  198. {
  199. upTimer = 0;
  200. DownTrigger();
  201. }
  202. downTimer += delta;
  203. }
  204. else
  205. {
  206. if (downTimer > 0) //第一帧松开扳机
  207. {
  208. downTimer = 0;
  209. UpTriggern();
  210. }
  211. upTimer += delta;
  212. }
  213.  
  214. // 攻击的计时器
  215. if (attackTimer > 0)
  216. {
  217. attackTimer -= delta;
  218. if (attackTimer < 0)
  219. {
  220. delayedTime += attackTimer;
  221. attackTimer = 0;
  222. }
  223. }
  224. else if (delayedTime > 0) //攻击延时
  225. {
  226. delayedTime -= delta;
  227. if (attackTimer < 0)
  228. {
  229. delayedTime = 0;
  230. }
  231. }
  232.  
  233. //连发判断
  234. if (continuousCount > 0 && delayedTime <= 0 && attackTimer <= 0)
  235. {
  236. //开火
  237. TriggernFire();
  238. }
  239.  
  240. if (!attackFlag && attackTimer <= 0)
  241. {
  242. CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta, Attribute.StartScatteringRange);
  243. }
  244. triggerTimer = triggerTimer > 0 ? triggerTimer - delta : 0;
  245. triggerFlag = false;
  246. attackFlag = false;
  247.  
  248. //枪身回归
  249. Position = Position.MoveToward(Vector2.Zero, 35 * delta);
  250. if (fireInterval == 0)
  251. {
  252. RotationDegrees = 0;
  253. }
  254. else
  255. {
  256. RotationDegrees = Mathf.Lerp(0, fireAngle, attackTimer / fireInterval);
  257. }
  258. }
  259. }
  260.  
  261. public void Init(GunAttribute attribute)
  262. {
  263. if (_attribute != null)
  264. {
  265. throw new Exception("当前武器已经初始化过了!");
  266. }
  267.  
  268. GunSprite = GetNode<Sprite>("GunSprite");
  269. FirePoint = GetNode<Position2D>("FirePoint");
  270. OriginPoint = GetNode<Position2D>("OriginPoint");
  271. ShellPoint = GetNode<Position2D>("ShellPoint");
  272. AnimationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
  273. CollisionShape2D = GetNode<CollisionShape2D>("Collision");
  274.  
  275. Attribute = attribute;
  276. //更新图片
  277. GunSprite.Texture = attribute.Sprite;
  278. GunSprite.Position = Attribute.CenterPosition;
  279. //开火位置
  280. FirePoint.Position = new Vector2(attribute.FirePosition.x, -attribute.FirePosition.y);
  281. OriginPoint.Position = new Vector2(0, -attribute.FirePosition.y);
  282.  
  283. //弹药量
  284. CurrAmmo = attribute.AmmoCapacity;
  285. //剩余弹药量
  286. ResidueAmmo = attribute.MaxAmmoCapacity - attribute.AmmoCapacity;
  287.  
  288. Init();
  289. }
  290.  
  291. /// <summary>
  292. /// 扳机函数, 调用即视为扣动扳机
  293. /// </summary>
  294. public void Trigger()
  295. {
  296. //是否第一帧按下
  297. var justDown = downTimer == 0;
  298. //是否能发射
  299. var flag = false;
  300. if (continuousCount <= 0) //不能处于连发状态下
  301. {
  302. if (Attribute.ContinuousShoot) //自动射击
  303. {
  304. if (triggerTimer > 0)
  305. {
  306. if (continuousShootFlag)
  307. {
  308. flag = true;
  309. }
  310. }
  311. else
  312. {
  313. flag = true;
  314. if (delayedTime <= 0 && attackTimer <= 0)
  315. {
  316. continuousShootFlag = true;
  317. }
  318. }
  319. }
  320. else //半自动
  321. {
  322. if (justDown && triggerTimer <= 0)
  323. {
  324. flag = true;
  325. }
  326. }
  327. }
  328.  
  329. if (flag)
  330. {
  331. if (Reloading)
  332. {
  333. //换弹中
  334. GD.Print("换弹中..." + (ReloadTimer / Attribute.ReloadTime));
  335. }
  336. else if (CurrAmmo <= 0)
  337. {
  338. //子弹不够
  339. GD.Print("弹夹打空了, 按R换弹!");
  340. }
  341. else
  342. {
  343. if (justDown)
  344. {
  345. //开火前延时
  346. delayedTime = Attribute.DelayedTime;
  347. //扳机按下间隔
  348. triggerTimer = Attribute.TriggerInterval;
  349. //连发数量
  350. if (!Attribute.ContinuousShoot)
  351. {
  352. continuousCount = MathUtils.RandRangeInt(Attribute.MinContinuousCount, Attribute.MaxContinuousCount);
  353. }
  354. }
  355. if (delayedTime <= 0 && attackTimer <= 0)
  356. {
  357. TriggernFire();
  358. }
  359. attackFlag = true;
  360. }
  361.  
  362. }
  363. triggerFlag = true;
  364. }
  365.  
  366. /// <summary>
  367. /// 刚按下扳机
  368. /// </summary>
  369. private void DownTrigger()
  370. {
  371.  
  372. }
  373.  
  374. /// <summary>
  375. /// 刚松开扳机
  376. /// </summary>
  377. private void UpTriggern()
  378. {
  379. continuousShootFlag = false;
  380. if (delayedTime > 0)
  381. {
  382. continuousCount = 0;
  383. }
  384. }
  385.  
  386. /// <summary>
  387. /// 触发开火
  388. /// </summary>
  389. private void TriggernFire()
  390. {
  391. continuousCount = continuousCount > 0 ? continuousCount - 1 : 0;
  392.  
  393. //减子弹数量
  394. CurrAmmo--;
  395. //开火间隙
  396. fireInterval = 60 / Attribute.StartFiringSpeed;
  397. //攻击冷却
  398. attackTimer += fireInterval;
  399.  
  400. //触发开火函数
  401. OnFire();
  402.  
  403. //开火发射的子弹数量
  404. var bulletCount = MathUtils.RandRangeInt(Attribute.MaxFireBulletCount, Attribute.MinFireBulletCount);
  405. //枪口角度
  406. var angle = new Vector2(GameConfig.ScatteringDistance, CurrScatteringRange).Angle();
  407.  
  408. //创建子弹
  409. for (int i = 0; i < bulletCount; i++)
  410. {
  411. //先算枪口方向
  412. Rotation = (float)GD.RandRange(-angle, angle);
  413. //发射子弹
  414. OnShootBullet();
  415. }
  416.  
  417. //当前的散射半径
  418. CurrScatteringRange = Mathf.Min(CurrScatteringRange + Attribute.ScatteringRangeAddValue, Attribute.FinalScatteringRange);
  419. //枪的旋转角度
  420. RotationDegrees -= Attribute.UpliftAngle;
  421. fireAngle = RotationDegrees;
  422. //枪身位置
  423. Position = new Vector2(Mathf.Max(-Attribute.MaxBacklash, Position.x - MathUtils.RandRange(Attribute.MinBacklash, Attribute.MaxBacklash)), Position.y);
  424.  
  425. if (FireEvent != null)
  426. {
  427. FireEvent(this);
  428. }
  429. }
  430.  
  431. /// <summary>
  432. /// 返回弹药是否到达上限
  433. /// </summary>
  434. public bool IsFullAmmo()
  435. {
  436. return CurrAmmo + ResidueAmmo >= Attribute.MaxAmmoCapacity;
  437. }
  438.  
  439. /// <summary>
  440. /// 返回是否弹药耗尽
  441. /// </summary>
  442. public bool IsEmptyAmmo()
  443. {
  444. return CurrAmmo + ResidueAmmo == 0;
  445. }
  446.  
  447. /// <summary>
  448. /// 拾起的弹药数量, 如果到达容量上限, 则返回拾取完毕后剩余的弹药数量
  449. /// </summary>
  450. /// <param name="count">弹药数量</param>
  451. public int PickUpAmmo(int count)
  452. {
  453. var num = ResidueAmmo;
  454. ResidueAmmo = Mathf.Min(ResidueAmmo + count, Attribute.MaxAmmoCapacity - CurrAmmo);
  455. return count - ResidueAmmo + num;
  456. }
  457.  
  458. /// <summary>
  459. /// 触发换弹
  460. /// </summary>
  461. public void _Reload()
  462. {
  463. if (CurrAmmo < Attribute.AmmoCapacity && ResidueAmmo > 0 && !Reloading)
  464. {
  465. Reloading = true;
  466. ReloadTimer = Attribute.ReloadTime;
  467. OnReload();
  468. }
  469. }
  470.  
  471. /// <summary>
  472. /// 换弹计时器时间到, 执行换弹操作
  473. /// </summary>
  474. private void ReloadSuccess()
  475. {
  476. if (Attribute.AloneReload) //单独装填
  477. {
  478.  
  479. }
  480. else //换弹结束
  481. {
  482. Reloading = false;
  483. if (ResidueAmmo >= Attribute.AmmoCapacity)
  484. {
  485. ResidueAmmo -= Attribute.AmmoCapacity - CurrAmmo;
  486. CurrAmmo = Attribute.AmmoCapacity;
  487. }
  488. else
  489. {
  490. CurrAmmo = ResidueAmmo;
  491. ResidueAmmo = 0;
  492. }
  493. }
  494. }
  495.  
  496. public CheckInteractiveResult CheckInteractive(Role master)
  497. {
  498. var result = new CheckInteractiveResult(this);
  499. if (Master == null)
  500. {
  501. var masterGun = master.Holster.ActiveGun;
  502. //查找是否有同类型武器
  503. var index = master.Holster.FindGun(Attribute.Id);
  504. if (index != -1) //如果有这个武器
  505. {
  506. if (CurrAmmo + ResidueAmmo != 0) //子弹不为空
  507. {
  508. var targetGun = master.Holster.GetGun(index);
  509. if (!targetGun.IsFullAmmo()) //背包里面的武器子弹未满
  510. {
  511. //可以互动拾起弹药
  512. result.CanInteractive = true;
  513. result.Message = Attribute.Name;
  514. result.ShowIcon = "res://resource/sprite/ui/icon/icon_bullet.png";
  515. return result;
  516. }
  517. }
  518. }
  519. else //没有武器
  520. {
  521. if (master.Holster.CanPickupGun(this)) //能拾起武器
  522. {
  523. //可以互动, 拾起武器
  524. result.CanInteractive = true;
  525. result.Message = Attribute.Name;
  526. result.ShowIcon = "res://resource/sprite/ui/icon/icon_pickup.png";
  527. return result;
  528. }
  529. else if (masterGun != null && masterGun.Attribute.WeightType == Attribute.WeightType) //替换武器
  530. {
  531. //可以互动, 切换武器
  532. result.CanInteractive = true;
  533. result.Message = Attribute.Name;
  534. result.ShowIcon = "res://resource/sprite/ui/icon/icon_replace.png";
  535. return result;
  536. }
  537. }
  538. }
  539. return result;
  540. }
  541.  
  542. public void Interactive(Role master)
  543. {
  544. //查找是否有同类型武器
  545. var index = master.Holster.FindGun(Attribute.Id);
  546. if (index != -1) //如果有这个武器
  547. {
  548. if (CurrAmmo + ResidueAmmo == 0) //没有子弹了
  549. {
  550. return;
  551. }
  552. var gun = master.Holster.GetGun(index);
  553. //子弹上限
  554. var maxCount = Attribute.MaxAmmoCapacity;
  555. //是否捡到子弹
  556. var flag = false;
  557. if (ResidueAmmo > 0 && gun.CurrAmmo + gun.ResidueAmmo < maxCount)
  558. {
  559. var count = gun.PickUpAmmo(ResidueAmmo);
  560. if (count != ResidueAmmo)
  561. {
  562. ResidueAmmo = count;
  563. flag = true;
  564. }
  565. }
  566. if (CurrAmmo > 0 && gun.CurrAmmo + gun.ResidueAmmo < maxCount)
  567. {
  568. var count = gun.PickUpAmmo(CurrAmmo);
  569. if (count != CurrAmmo)
  570. {
  571. CurrAmmo = count;
  572. flag = true;
  573. }
  574. }
  575. //播放互动效果
  576. if (flag)
  577. {
  578. this.StartThrow<ThrowGun>(new Vector2(20, 20), GlobalPosition, 0, 0,
  579. MathUtils.RandRangeInt(-20, 20), MathUtils.RandRangeInt(20, 50),
  580. MathUtils.RandRangeInt(-180, 180), GunSprite);
  581. }
  582. }
  583. else //没有武器
  584. {
  585. if (master.Holster.PickupGun(this) == -1)
  586. {
  587. var slot = master.Holster.SlotList[master.Holster.ActiveIndex];
  588. if (slot.Type == Attribute.WeightType)
  589. {
  590. var gun = master.Holster.RmoveGun(master.Holster.ActiveIndex);
  591. gun.StartThrowGun(master);
  592. master.PickUpGun(this);
  593. }
  594. }
  595. }
  596. }
  597.  
  598. public Vector2 GetItemPosition()
  599. {
  600. return GlobalPosition;
  601. }
  602.  
  603. /// <summary>
  604. /// 触发落到地面
  605. /// </summary>
  606. public void _FallToGround()
  607. {
  608. //启用碰撞
  609. CollisionShape2D.Disabled = false;
  610. }
  611.  
  612. /// <summary>
  613. /// 触发拾起
  614. /// </summary>
  615. public void _PickUpGun(Role master)
  616. {
  617. Master = master;
  618. //握把位置
  619. GunSprite.Position = Attribute.HoldPosition;
  620. //清除泛白效果
  621. ShaderMaterial sm = GunSprite.Material as ShaderMaterial;
  622. sm.SetShaderParam("schedule", 0);
  623. //停止动画
  624. AnimationPlayer.Stop();
  625. ZIndex = 0;
  626. //禁用碰撞
  627. CollisionShape2D.Disabled = true;
  628. OnPickUp(master);
  629. }
  630.  
  631. /// <summary>
  632. /// 触发抛出
  633. /// </summary>
  634. public void _ThrowOutGun()
  635. {
  636. Master = null;
  637. GunSprite.Position = Attribute.CenterPosition;
  638. AnimationPlayer.Play("Floodlight");
  639. OnThrowOut();
  640. }
  641.  
  642. /// <summary>
  643. /// 触发启用武器
  644. /// </summary>
  645. public void _Active()
  646. {
  647. OnActive();
  648. }
  649.  
  650. /// <summary>
  651. /// 触发收起武器
  652. /// </summary>
  653. public void _Conceal()
  654. {
  655. OnConceal();
  656. }
  657.  
  658. /// <summary>
  659. /// 实例化并返回子弹对象
  660. /// </summary>
  661. /// <param name="bulletPack">子弹的预制体</param>
  662. protected T CreateBullet<T>(PackedScene bulletPack, Vector2 globalPostion, float globalRotation, Node parent = null) where T : Node2D, IBullet
  663. {
  664. return (T)CreateBullet(bulletPack, globalPostion, globalRotation, parent);
  665. }
  666.  
  667. /// <summary>
  668. /// 实例化并返回子弹对象
  669. /// </summary>
  670. /// <param name="bulletPack">子弹的预制体</param>
  671. protected IBullet CreateBullet(PackedScene bulletPack, Vector2 globalPostion, float globalRotation, Node parent = null)
  672. {
  673. // 实例化子弹
  674. Node2D bullet = bulletPack.Instance<Node2D>();
  675. // 设置坐标
  676. bullet.GlobalPosition = globalPostion;
  677. // 旋转角度
  678. bullet.GlobalRotation = globalRotation;
  679. if (parent == null)
  680. {
  681. RoomManager.Current.SortRoot.AddChild(bullet);
  682. }
  683. else
  684. {
  685. parent.AddChild(bullet);
  686. }
  687. // 调用初始化
  688. IBullet result = (IBullet)bullet;
  689. result.Init(TargetCamp, this, null);
  690. return result;
  691. }
  692. }