Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / activity / bullet / normal / Bullet.cs
@小李xl 小李xl on 4 Apr 2024 10 KB 解决换阵营子弹碰撞问题
  1.  
  2. using System;
  3. using System.Collections;
  4. using Godot;
  5. using Godot.Collections;
  6.  
  7. /// <summary>
  8. /// 子弹类
  9. /// </summary>
  10. [Tool]
  11. public partial class Bullet : ActivityObject, IBullet
  12. {
  13. public event Action OnReclaimEvent;
  14. public event Action OnLeavePoolEvent;
  15. public bool IsRecycled { get; set; }
  16. public string Logotype { get; set; }
  17. public CampEnum Camp { get; set; }
  18. /// <summary>
  19. /// 子弹伤害碰撞区域
  20. /// </summary>
  21. [Export, ExportFillNode]
  22. public Area2D CollisionArea { get; set; }
  23. /// <summary>
  24. /// 子弹伤害碰撞检测形状
  25. /// </summary>
  26. [Export, ExportFillNode]
  27. public CollisionShape2D CollisionShape2D { get; set; }
  28. /// <summary>
  29. /// 子节点包含的例子特效, 在创建完成后自动播放
  30. /// </summary>
  31. [Export]
  32. public Array<GpuParticles2D> Particles2D { get; set; }
  33.  
  34. /// <summary>
  35. /// 子弹使用的数据
  36. /// </summary>
  37. public BulletData BulletData { get; private set; }
  38. /// <summary>
  39. /// 当前反弹次数
  40. /// </summary>
  41. public int CurrentBounce { get; protected set; } = 0;
  42.  
  43. /// <summary>
  44. /// 当前穿透次数
  45. /// </summary>
  46. public int CurrentPenetration { get; protected set; } = 0;
  47. /// <summary>
  48. /// 是否是敌人使用的子弹
  49. /// </summary>
  50. public bool IsEnemyBullet { get; private set; } = false;
  51.  
  52. /// <summary>
  53. /// 子弹状态
  54. /// </summary>
  55. public BulletStateEnum State { get; protected set; } = BulletStateEnum.Normal;
  56.  
  57. //当前子弹已经飞行的距离
  58. private float CurrFlyDistance = 0;
  59.  
  60. private bool _init = false;
  61.  
  62. public override void OnInit()
  63. {
  64. base.OnInit();
  65. OutlineColor = new Color(2.5f, 0, 0);
  66. SetBlendColor(new Color(2.5f, 2.5f, 2.5f));
  67.  
  68. CollisionArea.CollisionMask = Role.AttackLayer;
  69. }
  70.  
  71. public virtual void InitData(BulletData data, CampEnum camp)
  72. {
  73. if (!_init)
  74. {
  75. CollisionArea.AreaEntered += OnArea2dEntered;
  76. CollisionArea.BodyEntered += OnBodyEntered;
  77. _init = true;
  78. }
  79.  
  80. Camp = camp;
  81. CurrentBounce = 0;
  82. CurrentPenetration = 0;
  83. CurrFlyDistance = 0;
  84. BulletData = data;
  85. Rotation = data.Rotation;
  86. var triggerRole = data.TriggerRole;
  87. if (data.TriggerRole != null && data.TriggerRole.AffiliationArea != null) //设置所属区域
  88. {
  89. if (triggerRole.AffiliationArea != null)
  90. {
  91. triggerRole.AffiliationArea.InsertItem(this);
  92. }
  93. }
  94. Position = data.Position + new Vector2(0, data.Altitude);
  95. Altitude = data.Altitude;
  96. if (data.VerticalSpeed != 0)
  97. {
  98. VerticalSpeed = data.VerticalSpeed;
  99. }
  100. else
  101. {
  102. VerticalSpeed = 0;
  103. }
  104.  
  105. //BasisVelocity = new Vector2(data.FlySpeed, 0).Rotated(Rotation);
  106. MoveController.AddForce(new Vector2(data.FlySpeed, 0).Rotated(Rotation));
  107. //如果子弹会对玩家造成伤害, 则显示红色描边
  108. if (triggerRole != null && triggerRole.IsEnemyWithPlayer())
  109. {
  110. if (!IsEnemyBullet)
  111. {
  112. RefreshBulletColor(true);
  113. }
  114. }
  115. else if (IsEnemyBullet)
  116. {
  117. RefreshBulletColor(false);
  118. }
  119. PutDown(RoomLayerEnum.YSortLayer);
  120. //播放子弹移动动画
  121. PlaySpriteAnimation(AnimatorNames.Move);
  122. //强制更新下坠逻辑处理
  123. UpdateFall((float)GetProcessDeltaTime());
  124.  
  125. //过期销毁
  126. if (data.LifeTime > 0)
  127. {
  128. this.CallDelay(data.LifeTime, () =>
  129. {
  130. State = BulletStateEnum.LimeOver;
  131. OnLimeOver();
  132. });
  133. }
  134. if (Particles2D != null)
  135. {
  136. foreach (var particles2D in Particles2D)
  137. {
  138. particles2D.Restart();
  139. }
  140. }
  141. }
  142.  
  143. /// <summary>
  144. /// 刷新子弹的颜色
  145. /// </summary>
  146. /// <param name="isEnemyBullet">是否是敌人使用的子弹</param>
  147. public virtual void RefreshBulletColor(bool isEnemyBullet)
  148. {
  149. IsEnemyBullet = isEnemyBullet;
  150. if (isEnemyBullet)
  151. {
  152. ShowOutline = true;
  153. SetBlendSchedule(1);
  154. }
  155. else
  156. {
  157. ShowOutline = false;
  158. SetBlendSchedule(0);
  159. }
  160. }
  161. public override void OnMoveCollision(KinematicCollision2D collision)
  162. {
  163. CurrentBounce++;
  164. if (CurrentBounce > BulletData.BounceCount) //反弹次数超过限制
  165. {
  166. State = BulletStateEnum.MoveCollision;
  167. //创建粒子特效
  168. OnPlayCollisionEffect(collision);
  169. LogicalFinish();
  170. }
  171. }
  172.  
  173. /// <summary>
  174. /// 碰到目标
  175. /// </summary>
  176. public virtual void OnCollisionTarget(IHurt hurt)
  177. {
  178. if (hurt.CanHurt(Camp))
  179. {
  180. OnPlayDisappearEffect();
  181. if (BulletData.Repel != 0)
  182. {
  183. var o = hurt.GetActivityObject();
  184. if (o != null && o is not Player) //目标不是玩家才会触发击退
  185. {
  186. o.AddRepelForce(Velocity.Normalized() * BulletData.Repel);
  187. }
  188. }
  189. //造成伤害
  190. var target = BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole;
  191. hurt.Hurt(target, BulletData.Harm, Rotation);
  192. //穿透次数
  193. CurrentPenetration++;
  194. if (CurrentPenetration > BulletData.Penetration)
  195. {
  196. State = BulletStateEnum.CollisionTarget;
  197. CallDeferred(nameof(LogicalFinish));
  198. }
  199. }
  200. }
  201.  
  202. /// <summary>
  203. /// 到达最大运行距离
  204. /// </summary>
  205. public virtual void OnMaxDistance()
  206. {
  207. OnPlayDisappearEffect();
  208. LogicalFinish();
  209. }
  210. /// <summary>
  211. /// 子弹生命周期结束
  212. /// </summary>
  213. public virtual void OnLimeOver()
  214. {
  215. OnPlayDisappearEffect();
  216. LogicalFinish();
  217. }
  218. protected override void OnFallToGround()
  219. {
  220. State = BulletStateEnum.FallToGround;
  221. //落地销毁
  222. OnPlayDisappearEffect();
  223. LogicalFinish();
  224. }
  225. /// <summary>
  226. /// 显示红色描边
  227. /// </summary>
  228. public void ShowBorderFlashes()
  229. {
  230. ShowOutline = true;
  231. OutlineColor = new Color(1, 0, 0);
  232. StartCoroutine(BorderFlashes());
  233. }
  234. private IEnumerator BorderFlashes()
  235. {
  236. while (true)
  237. {
  238. ShowOutline = !ShowOutline;
  239. yield return new WaitForSeconds(0.12f);
  240. }
  241. }
  242.  
  243. /// <summary>
  244. /// 播放子弹消失的特效
  245. /// </summary>
  246. public virtual void OnPlayDisappearEffect()
  247. {
  248. PlayDisappearEffect(ResourcePath.prefab_effect_bullet_BulletDisappear0001_tscn);
  249. }
  250.  
  251. /// <summary>
  252. /// 播放撞墙特效
  253. /// </summary>
  254. public virtual void OnPlayCollisionEffect(KinematicCollision2D collision)
  255. {
  256. PlayCollisionEffect(collision, ResourcePath.prefab_effect_bullet_BulletSmoke0001_tscn);
  257. }
  258.  
  259. /// <summary>
  260. /// 播放子弹消失特效
  261. /// </summary>
  262. public void PlayDisappearEffect(string path)
  263. {
  264. var effect = ObjectManager.GetPoolItem<IEffect>(path);
  265. var node = (Node2D)effect;
  266. node.GlobalPosition = AnimatedSprite.GlobalPosition;
  267. node.AddToActivityRoot(RoomLayerEnum.YSortLayer);
  268. effect.PlayEffect();
  269. }
  270. /// <summary>
  271. /// 播放子弹撞墙消失特效
  272. /// </summary>
  273. public void PlayCollisionEffect(KinematicCollision2D collision, string path)
  274. {
  275. var effect = ObjectManager.GetPoolItem<IEffect>(path);
  276. var smoke = (Node2D)effect;
  277. var rotated = AnimatedSprite.Position.Rotated(Rotation);
  278. smoke.GlobalPosition = collision.GetPosition() + new Vector2(0, rotated.Y);
  279. smoke.GlobalRotation = collision.GetNormal().Angle();
  280. smoke.AddToActivityRoot(RoomLayerEnum.YSortLayer);
  281. effect.PlayEffect();
  282. }
  283. protected override void Process(float delta)
  284. {
  285. if (ActivityMaterial.DynamicCollision)
  286. {
  287. //子弹高度大于 32 关闭碰撞检测
  288. CollisionShape2D.Disabled = Altitude >= 32;
  289. }
  290. //距离太大, 自动销毁
  291. if (MoveController.Enable)
  292. {
  293. //CurrFlyDistance += Velocity.Length() * delta;
  294. CurrFlyDistance += BulletData.FlySpeed * delta;
  295. if (CurrFlyDistance >= BulletData.MaxDistance)
  296. {
  297. State = BulletStateEnum.MaxDistance;
  298. OnMaxDistance();
  299. }
  300. }
  301. }
  302.  
  303. protected virtual void OnBodyEntered(Node2D body)
  304. {
  305. if (IsDestroyed)
  306. {
  307. return;
  308. }
  309.  
  310. if (body is IHurt hurt)
  311. {
  312. OnCollisionTarget(hurt);
  313. }
  314. }
  315. protected virtual void OnArea2dEntered(Area2D other)
  316. {
  317. if (IsDestroyed)
  318. {
  319. return;
  320. }
  321. if (other is IHurt hurt)
  322. {
  323. OnCollisionTarget(hurt);
  324. }
  325. }
  326.  
  327. public virtual void LogicalFinish()
  328. {
  329. ObjectPool.Reclaim(this);
  330. }
  331. public virtual void OnReclaim()
  332. {
  333. State = BulletStateEnum.Normal;
  334. Visible = false;
  335. if (Particles2D != null)
  336. {
  337. foreach (var particles2D in Particles2D)
  338. {
  339. particles2D.Emitting = false;
  340. }
  341. }
  342. if (OnReclaimEvent != null)
  343. {
  344. OnReclaimEvent();
  345. }
  346. if (AffiliationArea != null)
  347. {
  348. AffiliationArea.RemoveItem(this);
  349. }
  350. GetParent().CallDeferred(Node.MethodName.RemoveChild, this);
  351. }
  352.  
  353. public virtual void OnLeavePool()
  354. {
  355. Visible = true;
  356. MoveController.ClearForce();
  357. StopAllCoroutine();
  358. if (OnLeavePoolEvent != null)
  359. {
  360. OnLeavePoolEvent();
  361. }
  362. }
  363.  
  364. /// <summary>
  365. /// 设置是否启用移动逻辑
  366. /// </summary>
  367. public void SetEnableMovement(bool v)
  368. {
  369. MoveController.Enable = v;
  370. CollisionArea.Monitoring = v;
  371. CollisionArea.Monitorable = v;
  372. Collision.Disabled = !v;
  373. }
  374. }