Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / activity / bullet / laser / Laser.cs
@小李xl 小李xl on 4 Jan 2024 7 KB 修复子弹特效层级问题
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Godot;
  5. using Godot.Collections;
  6.  
  7. /// <summary>
  8. /// 激光子弹
  9. /// </summary>
  10. public partial class Laser : Area2D, IBullet
  11. {
  12. /// <summary>
  13. /// 激光默认宽度
  14. /// </summary>
  15. public const float LaserDefaultWidth = 3f;
  16. /// <summary>
  17. /// 子节点包含的例子特效, 在创建完成后自动播放
  18. /// </summary>
  19. [Export]
  20. public Array<GpuParticles2D> Particles2D { get; set; }
  21. public CollisionShape2D Collision { get; private set; }
  22. public Sprite2D LineSprite { get; private set; }
  23. public RectangleShape2D Shape { get; private set; }
  24.  
  25. public event Action OnReclaimEvent;
  26. public event Action OnLeavePoolEvent;
  27. public bool IsRecycled { get; set; }
  28. public string Logotype { get; set; }
  29.  
  30. public uint AttackLayer
  31. {
  32. get => CollisionMask;
  33. set => CollisionMask = value;
  34. }
  35.  
  36. public BulletData BulletData { get; private set; }
  37.  
  38. public bool IsDestroyed { get; private set; }
  39. public float Width { get; set; }
  40. //开启的协程
  41. private List<CoroutineData> _coroutineList;
  42. private float _pixelScale;
  43. private float _speed = 2000;
  44. private Tween _tween;
  45. private bool _init = false;
  46.  
  47. public void InitData(BulletData data, uint attackLayer)
  48. {
  49. InitData(data, attackLayer, LaserDefaultWidth);
  50. }
  51. public void InitData(BulletData data, uint attackLayer, float width)
  52. {
  53. if (!_init)
  54. {
  55. Collision = GetNodeOrNull<CollisionShape2D>("CollisionShape2D");
  56. Collision.Disabled = true;
  57. Shape = (RectangleShape2D)Collision.Shape;
  58. LineSprite = GetNodeOrNull<Sprite2D>("LineSprite");
  59. _pixelScale = 1f / LineSprite.Texture.GetHeight();
  60.  
  61. AreaEntered += OnArea2dEntered;
  62. _init = true;
  63. }
  64.  
  65. ZIndex = 1;
  66. BulletData = data;
  67. AttackLayer = attackLayer;
  68. Position = data.Position;
  69. Rotation = data.Rotation;
  70.  
  71. //计算射线最大距离, 也就是撞到墙壁的距离
  72. var targetPosition = data.Position + Vector2.FromAngle(data.Rotation) * data.MaxDistance;
  73. var parameters = PhysicsRayQueryParameters2D.Create(data.Position + new Vector2(0, data.Altitude), targetPosition + new Vector2(0, data.Altitude), PhysicsLayer.Wall);
  74. var result = GetWorld2D().DirectSpaceState.IntersectRay(parameters);
  75. float distance;
  76. var doRebound = false; //是否需要执行反弹
  77. Vector2? reboundPosition = null;
  78. Vector2? reboundNormal = null;
  79. if (result != null && result.TryGetValue("position", out var point)) //撞到墙壁
  80. {
  81. doRebound = true;
  82. reboundPosition = (Vector2)point - new Vector2(0, data.Altitude);
  83. reboundNormal = (Vector2)result["normal"];
  84. distance = Position.DistanceTo(reboundPosition.Value);
  85. }
  86. else //没撞到墙壁
  87. {
  88. distance = data.MaxDistance;
  89. }
  90. Collision.SetDeferred(CollisionShape2D.PropertyName.Disabled, false);
  91. Collision.Position = Vector2.Zero;
  92. Shape.Size = Vector2.Zero;
  93. LineSprite.Scale = new Vector2(0, width * _pixelScale);
  94.  
  95. //如果子弹会对玩家造成伤害, 则显示成红色
  96. if (Player.Current.CollisionWithMask(attackLayer))
  97. {
  98. LineSprite.Modulate = new Color(2.5f, 0.5f, 0.5f);
  99. }
  100. else
  101. {
  102. LineSprite.Modulate = new Color(1.5f, 1.5f, 1.5f);
  103. }
  104. //激光飞行时间
  105. var time = distance / _speed;
  106.  
  107. _tween = CreateTween();
  108. _tween.SetParallel();
  109. _tween.TweenProperty(LineSprite, "scale", new Vector2(distance, width * _pixelScale), time);
  110. _tween.TweenProperty(Collision, "position", new Vector2(distance * 0.5f, 0), time);
  111. _tween.TweenProperty(Shape, "size", new Vector2(distance, width), time);
  112. _tween.Chain();
  113. //持续时间
  114. // tween.TweenInterval(0.2f);
  115. // tween.Chain();
  116. _tween.TweenCallback(Callable.From(() =>
  117. {
  118. //执行反弹
  119. if (doRebound)
  120. {
  121. CallDeferred(nameof(OnRebound), reboundPosition.Value, reboundNormal.Value);
  122. }
  123. Collision.SetDeferred(CollisionShape2D.PropertyName.Disabled, false);
  124. }));
  125. _tween.Chain();
  126. _tween.TweenProperty(LineSprite, "scale", new Vector2(distance, 0), 0.3f);
  127. _tween.Chain();
  128. _tween.TweenCallback(Callable.From(() =>
  129. {
  130. _tween = null;
  131. DoReclaim();
  132. }));
  133. _tween.Play();
  134. if (Particles2D != null)
  135. {
  136. foreach (var particles2D in Particles2D)
  137. {
  138. particles2D.Restart();
  139. }
  140. }
  141. }
  142.  
  143. public override void _Process(double delta)
  144. {
  145. ProxyCoroutineHandler.ProxyUpdateCoroutine(ref _coroutineList, (float)delta);
  146. }
  147.  
  148. public void Destroy()
  149. {
  150. if (IsDestroyed)
  151. {
  152. return;
  153. }
  154. QueueFree();
  155. }
  156.  
  157. //激光撞墙反弹逻辑
  158. private void OnRebound(Vector2 position, Vector2 normal)
  159. {
  160. if (BulletData.BounceCount > 0)
  161. {
  162. var newDistance = BulletData.MaxDistance - BulletData.Position.DistanceTo(position);
  163. if (newDistance > 0)
  164. {
  165. float rotation;
  166. if (normal.X == 0 && normal.Y == 0)
  167. {
  168. rotation = (BulletData.Rotation + Mathf.Pi) % (Mathf.Pi * 2);
  169. }
  170. else
  171. {
  172. rotation = Utils.ReflectByNormal(BulletData.Rotation, normal);
  173. }
  174. var bulletData = BulletData.Clone();
  175. bulletData.Position = position;
  176. bulletData.BounceCount -= 1;
  177. bulletData.MaxDistance = newDistance;
  178. bulletData.Rotation = rotation;
  179. FireManager.ShootBullet(bulletData, AttackLayer);
  180. }
  181. }
  182. }
  183. private void OnArea2dEntered(Area2D other)
  184. {
  185. var role = other.AsActivityObject<Role>();
  186. if (role != null)
  187. {
  188. //击退
  189. if (BulletData.Repel != 0)
  190. {
  191. role.AddRepelForce(Vector2.FromAngle(Rotation) * BulletData.Repel);
  192. }
  193. //造成伤害
  194. role.CallDeferred(nameof(Role.Hurt), BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole, BulletData.Harm, Rotation);
  195. }
  196. }
  197.  
  198. public long StartCoroutine(IEnumerator able)
  199. {
  200. return ProxyCoroutineHandler.ProxyStartCoroutine(ref _coroutineList, able);
  201. }
  202.  
  203. public void StopCoroutine(long coroutineId)
  204. {
  205. ProxyCoroutineHandler.ProxyStopCoroutine(ref _coroutineList, coroutineId);
  206. }
  207.  
  208. public bool IsCoroutineOver(long coroutineId)
  209. {
  210. return ProxyCoroutineHandler.ProxyIsCoroutineOver(ref _coroutineList, coroutineId);
  211. }
  212.  
  213. public void StopAllCoroutine()
  214. {
  215. ProxyCoroutineHandler.ProxyStopAllCoroutine(ref _coroutineList);
  216. }
  217. public void DoReclaim()
  218. {
  219. ObjectPool.Reclaim(this);
  220. }
  221. public virtual void OnReclaim()
  222. {
  223. if (Particles2D != null)
  224. {
  225. foreach (var particles2D in Particles2D)
  226. {
  227. particles2D.Emitting = false;
  228. }
  229. }
  230. if (OnReclaimEvent != null)
  231. {
  232. OnReclaimEvent();
  233. }
  234.  
  235. if (_tween != null)
  236. {
  237. _tween.Dispose();
  238. _tween = null;
  239. }
  240. GetParent().CallDeferred(Node.MethodName.RemoveChild, this);
  241. }
  242.  
  243. public virtual void OnLeavePool()
  244. {
  245. StopAllCoroutine();
  246. if (OnLeavePoolEvent != null)
  247. {
  248. OnLeavePoolEvent();
  249. }
  250. }
  251. }