Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / activity / bullet / laser / Laser.cs
  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 Tween _tween;
  44. private bool _init = false;
  45.  
  46. public void InitData(BulletData data, uint attackLayer)
  47. {
  48. InitData(data, attackLayer, LaserDefaultWidth);
  49. }
  50. public void InitData(BulletData data, uint attackLayer, float width)
  51. {
  52. if (!_init)
  53. {
  54. Collision = GetNodeOrNull<CollisionShape2D>("CollisionShape2D");
  55. Collision.Disabled = true;
  56. Shape = (RectangleShape2D)Collision.Shape;
  57. LineSprite = GetNodeOrNull<Sprite2D>("LineSprite");
  58. _pixelScale = 1f / LineSprite.Texture.GetHeight();
  59.  
  60. AreaEntered += OnArea2dEntered;
  61. BodyEntered += OnBodyEntered;
  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 / data.FlySpeed;
  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. LogicalFinish();
  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.  
  184. private void OnBodyEntered(Node2D body)
  185. {
  186. if (body is IHurt hurt)
  187. {
  188. HandlerCollision(hurt);
  189. }
  190. }
  191. private void OnArea2dEntered(Area2D other)
  192. {
  193. if (other is IHurt hurt)
  194. {
  195. HandlerCollision(hurt);
  196. }
  197. }
  198.  
  199. private void HandlerCollision(IHurt hurt)
  200. {
  201. if (BulletData.Repel != 0)
  202. {
  203. var o = hurt.GetActivityObject();
  204. if (o != null && o is not Player) //目标不是玩家才会触发击退
  205. {
  206. o.AddRepelForce(Vector2.FromAngle(Rotation) * BulletData.Repel);
  207. }
  208. }
  209.  
  210. //造成伤害
  211. hurt.Hurt(BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole, BulletData.Harm, Rotation);
  212. }
  213.  
  214. public long StartCoroutine(IEnumerator able)
  215. {
  216. return ProxyCoroutineHandler.ProxyStartCoroutine(ref _coroutineList, able);
  217. }
  218.  
  219. public void StopCoroutine(long coroutineId)
  220. {
  221. ProxyCoroutineHandler.ProxyStopCoroutine(ref _coroutineList, coroutineId);
  222. }
  223.  
  224. public bool IsCoroutineOver(long coroutineId)
  225. {
  226. return ProxyCoroutineHandler.ProxyIsCoroutineOver(ref _coroutineList, coroutineId);
  227. }
  228.  
  229. public void StopAllCoroutine()
  230. {
  231. ProxyCoroutineHandler.ProxyStopAllCoroutine(ref _coroutineList);
  232. }
  233. public void LogicalFinish()
  234. {
  235. ObjectPool.Reclaim(this);
  236. }
  237. public virtual void OnReclaim()
  238. {
  239. if (Particles2D != null)
  240. {
  241. foreach (var particles2D in Particles2D)
  242. {
  243. particles2D.Emitting = false;
  244. }
  245. }
  246. if (OnReclaimEvent != null)
  247. {
  248. OnReclaimEvent();
  249. }
  250.  
  251. if (_tween != null)
  252. {
  253. _tween.Dispose();
  254. _tween = null;
  255. }
  256. GetParent().CallDeferred(Node.MethodName.RemoveChild, this);
  257. }
  258.  
  259. public virtual void OnLeavePool()
  260. {
  261. StopAllCoroutine();
  262. if (OnLeavePoolEvent != null)
  263. {
  264. OnLeavePoolEvent();
  265. }
  266. }
  267. }