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