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. public BulletData BulletData { get; private set; }
  43. /// <summary>
  44. /// 当前反弹次数
  45. /// </summary>
  46. public int CurrentBounce { get; protected set; } = 0;
  47.  
  48. /// <summary>
  49. /// 当前穿透次数
  50. /// </summary>
  51. public int CurrentPenetration { get; protected set; } = 0;
  52. //当前子弹已经飞行的距离
  53. private float CurrFlyDistance = 0;
  54.  
  55. private bool _init = false;
  56. private bool _isEnemyBullet = false;
  57.  
  58. public override void OnInit()
  59. {
  60. base.OnInit();
  61. OutlineColor = new Color(2.5f, 0, 0);
  62. SetBlendColor(new Color(2.0f, 2.0f, 2.0f));
  63. }
  64.  
  65. public virtual void InitData(BulletData data, uint attackLayer)
  66. {
  67. if (!_init)
  68. {
  69. CollisionArea.AreaEntered += OnArea2dEntered;
  70. _init = true;
  71. }
  72. CurrentBounce = 0;
  73. CurrentPenetration = 0;
  74. CurrFlyDistance = 0;
  75. BulletData = data;
  76. AttackLayer = attackLayer;
  77. Rotation = data.Rotation;
  78. var triggerRole = data.TriggerRole;
  79. if (data.TriggerRole != null && data.TriggerRole.AffiliationArea != null) //设置所属区域
  80. {
  81. if (triggerRole.AffiliationArea != null)
  82. {
  83. triggerRole.AffiliationArea.InsertItem(this);
  84. }
  85. }
  86. Position = data.Position + new Vector2(0, data.Altitude);
  87. Altitude = data.Altitude;
  88. if (data.VerticalSpeed != 0)
  89. {
  90. VerticalSpeed = data.VerticalSpeed;
  91. }
  92. else
  93. {
  94. VerticalSpeed = 0;
  95. }
  96.  
  97. //BasisVelocity = new Vector2(data.FlySpeed, 0).Rotated(Rotation);
  98. MoveController.AddForce(new Vector2(data.FlySpeed, 0).Rotated(Rotation));
  99. //如果子弹会对玩家造成伤害, 则显示红色描边
  100. if (Player.Current.CollisionWithMask(attackLayer))
  101. {
  102. if (!_isEnemyBullet)
  103. {
  104. _isEnemyBullet = true;
  105. ShowOutline = true;
  106. SetBlendSchedule(1);
  107. }
  108. }
  109. else if (_isEnemyBullet)
  110. {
  111. _isEnemyBullet = false;
  112. ShowOutline = false;
  113. SetBlendSchedule(0);
  114. }
  115. PutDown(RoomLayerEnum.YSortLayer);
  116. //播放子弹移动动画
  117. PlaySpriteAnimation(AnimatorNames.Move);
  118. //强制更新下坠逻辑处理
  119. UpdateFall((float)GetProcessDeltaTime());
  120.  
  121. //过期销毁
  122. if (data.LifeTime > 0)
  123. {
  124. this.CallDelay(data.LifeTime, OnLimeOver);
  125. }
  126. if (Particles2D != null)
  127. {
  128. foreach (var particles2D in Particles2D)
  129. {
  130. particles2D.Restart();
  131. }
  132. }
  133. }
  134.  
  135. public override void OnMoveCollision(KinematicCollision2D collision)
  136. {
  137. CurrentBounce++;
  138. if (CurrentBounce > BulletData.BounceCount) //反弹次数超过限制
  139. {
  140. //创建粒子特效
  141. OnPlayCollisionEffect(collision);
  142. DoReclaim();
  143. }
  144. }
  145.  
  146. /// <summary>
  147. /// 碰到目标
  148. /// </summary>
  149. public virtual void OnCollisionTarget(ActivityObject o)
  150. {
  151. if (o is Role role)
  152. {
  153. OnPlayDisappearEffect();
  154.  
  155. //击退
  156. if (role is not Player) //目标不是玩家才会触发击退
  157. {
  158. if (BulletData.Repel != 0)
  159. {
  160. role.AddRepelForce(Velocity.Normalized() * BulletData.Repel);
  161. }
  162. }
  163. //造成伤害
  164. role.CallDeferred(nameof(Role.Hurt), BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole, BulletData.Harm, Rotation);
  165.  
  166. //穿透次数
  167. CurrentPenetration++;
  168. if (CurrentPenetration > BulletData.Penetration)
  169. {
  170. DoReclaim();
  171. }
  172. }
  173. }
  174.  
  175. /// <summary>
  176. /// 到达最大运行距离
  177. /// </summary>
  178. public virtual void OnMaxDistance()
  179. {
  180. OnPlayDisappearEffect();
  181. DoReclaim();
  182. }
  183. /// <summary>
  184. /// 子弹生命周期结束
  185. /// </summary>
  186. public virtual void OnLimeOver()
  187. {
  188. OnPlayDisappearEffect();
  189. DoReclaim();
  190. }
  191. protected override void OnFallToGround()
  192. {
  193. //落地销毁
  194. OnPlayDisappearEffect();
  195. DoReclaim();
  196. }
  197. /// <summary>
  198. /// 显示红色描边
  199. /// </summary>
  200. public void ShowBorderFlashes()
  201. {
  202. ShowOutline = true;
  203. OutlineColor = new Color(1, 0, 0);
  204. StartCoroutine(BorderFlashes());
  205. }
  206. private IEnumerator BorderFlashes()
  207. {
  208. while (true)
  209. {
  210. ShowOutline = !ShowOutline;
  211. yield return new WaitForSeconds(0.12f);
  212. }
  213. }
  214.  
  215. /// <summary>
  216. /// 播放子弹消失的特效
  217. /// </summary>
  218. public virtual void OnPlayDisappearEffect()
  219. {
  220. PlayDisappearEffect(ResourcePath.prefab_effect_bullet_BulletDisappear0001_tscn);
  221. }
  222.  
  223. /// <summary>
  224. /// 播放撞墙特效
  225. /// </summary>
  226. public virtual void OnPlayCollisionEffect(KinematicCollision2D collision)
  227. {
  228. PlayCollisionEffect(collision, ResourcePath.prefab_effect_bullet_BulletSmoke0001_tscn);
  229. }
  230.  
  231. /// <summary>
  232. /// 播放子弹消失特效
  233. /// </summary>
  234. public void PlayDisappearEffect(string path)
  235. {
  236. var effect = ObjectManager.GetPoolItem<IEffect>(path);
  237. var node = (Node2D)effect;
  238. node.GlobalPosition = AnimatedSprite.GlobalPosition;
  239. node.AddToActivityRoot(RoomLayerEnum.YSortLayer);
  240. effect.PlayEffect();
  241. }
  242. /// <summary>
  243. /// 播放子弹消失特效
  244. /// </summary>
  245. public void PlayCollisionEffect(KinematicCollision2D collision, string path)
  246. {
  247. var effect = ObjectManager.GetPoolItem<IEffect>(path);
  248. var smoke = (Node2D)effect;
  249. var rotated = AnimatedSprite.Position.Rotated(Rotation);
  250. smoke.GlobalPosition = collision.GetPosition() + new Vector2(0, rotated.Y);
  251. smoke.GlobalRotation = collision.GetNormal().Angle();
  252. smoke.AddToActivityRoot(RoomLayerEnum.YSortLayer);
  253. effect.PlayEffect();
  254. }
  255. protected override void Process(float delta)
  256. {
  257. if (ActivityMaterial.DynamicCollision)
  258. {
  259. //子弹高度大于 16 关闭碰撞检测
  260. CollisionShape2D.Disabled = Altitude >= 16;
  261. }
  262. //距离太大, 自动销毁
  263. CurrFlyDistance += BulletData.FlySpeed * delta;
  264. if (CurrFlyDistance >= BulletData.MaxDistance)
  265. {
  266. OnMaxDistance();
  267. }
  268. }
  269. private void OnArea2dEntered(Area2D other)
  270. {
  271. if (IsDestroyed)
  272. {
  273. return;
  274. }
  275. var activityObject = other.AsActivityObject();
  276. OnCollisionTarget(activityObject);
  277. }
  278. public virtual void DoReclaim()
  279. {
  280. ObjectPool.Reclaim(this);
  281. }
  282. public virtual void OnReclaim()
  283. {
  284. Visible = false;
  285. if (Particles2D != null)
  286. {
  287. foreach (var particles2D in Particles2D)
  288. {
  289. particles2D.Emitting = false;
  290. }
  291. }
  292. if (OnReclaimEvent != null)
  293. {
  294. OnReclaimEvent();
  295. }
  296. if (AffiliationArea != null)
  297. {
  298. AffiliationArea.RemoveItem(this);
  299. }
  300. GetParent().CallDeferred(Node.MethodName.RemoveChild, this);
  301. }
  302.  
  303. public virtual void OnLeavePool()
  304. {
  305. Visible = true;
  306. MoveController.ClearForce();
  307. StopAllCoroutine();
  308. if (OnLeavePoolEvent != null)
  309. {
  310. OnLeavePoolEvent();
  311. }
  312. }
  313. }