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 CampEnum Camp { get; set; }
  26. public event Action OnReclaimEvent;
  27. public event Action OnLeavePoolEvent;
  28. public bool IsRecycled { get; set; }
  29. public string Logotype { get; set; }
  30.  
  31. public BulletData BulletData { get; private set; }
  32. public BulletStateEnum State { get; protected set; } = BulletStateEnum.Normal;
  33.  
  34. public bool IsDestroyed { get; private set; }
  35. public float Width { get; set; }
  36. //开启的协程
  37. private List<CoroutineData> _coroutineList;
  38. private float _pixelScale;
  39. private Tween _tween;
  40. private bool _init = false;
  41.  
  42. public override void _Ready()
  43. {
  44. CollisionMask = Role.AttackLayer;
  45. }
  46.  
  47. public void InitData(BulletData data, CampEnum camp)
  48. {
  49. InitData(data, camp, LaserDefaultWidth);
  50. }
  51.  
  52. public void InitData(BulletData data, CampEnum camp, float width)
  53. {
  54. if (!_init)
  55. {
  56. Collision = GetNodeOrNull<CollisionShape2D>("CollisionShape2D");
  57. Collision.Disabled = true;
  58. Shape = (RectangleShape2D)Collision.Shape;
  59. LineSprite = GetNodeOrNull<Sprite2D>("LineSprite");
  60. _pixelScale = 1f / LineSprite.Texture.GetHeight();
  61.  
  62. AreaEntered += OnArea2dEntered;
  63. BodyEntered += OnBodyEntered;
  64. _init = true;
  65. }
  66.  
  67. Camp = camp;
  68. ZIndex = 1;
  69. BulletData = data;
  70. Position = data.Position;
  71. Rotation = data.Rotation;
  72.  
  73. //计算射线最大距离, 也就是撞到墙壁的距离
  74. var targetPosition = data.Position + Vector2.FromAngle(data.Rotation) * data.MaxDistance;
  75. var parameters = PhysicsRayQueryParameters2D.Create(data.Position + new Vector2(0, data.Altitude), targetPosition + new Vector2(0, data.Altitude), PhysicsLayer.Wall);
  76. var result = GetWorld2D().DirectSpaceState.IntersectRay(parameters);
  77. float distance;
  78. var doRebound = false; //是否需要执行反弹
  79. Vector2? reboundPosition = null;
  80. Vector2? reboundNormal = null;
  81. if (result != null && result.TryGetValue("position", out var point)) //撞到墙壁
  82. {
  83. doRebound = true;
  84. reboundPosition = (Vector2)point - new Vector2(0, data.Altitude);
  85. reboundNormal = (Vector2)result["normal"];
  86. distance = Position.DistanceTo(reboundPosition.Value);
  87. }
  88. else //没撞到墙壁
  89. {
  90. distance = data.MaxDistance;
  91. }
  92. Collision.SetDeferred(CollisionShape2D.PropertyName.Disabled, false);
  93. Collision.Position = Vector2.Zero;
  94. Shape.Size = Vector2.Zero;
  95. LineSprite.Scale = new Vector2(0, width * _pixelScale);
  96.  
  97. //如果子弹会对玩家造成伤害, 则显示成红色
  98. if (BulletData.TriggerRole != null && BulletData.TriggerRole.IsEnemyWithPlayer())
  99. {
  100. LineSprite.Modulate = new Color(2.5f, 0.5f, 0.5f);
  101. }
  102. else
  103. {
  104. LineSprite.Modulate = new Color(1.5f, 1.5f, 1.5f);
  105. }
  106. //激光飞行时间
  107. var time = distance / data.FlySpeed;
  108.  
  109. _tween = CreateTween();
  110. _tween.SetParallel();
  111. _tween.TweenProperty(LineSprite, "scale", new Vector2(distance, width * _pixelScale), time);
  112. _tween.TweenProperty(Collision, "position", new Vector2(distance * 0.5f, 0), time);
  113. _tween.TweenProperty(Shape, "size", new Vector2(distance, width), time);
  114. _tween.Chain();
  115. //持续时间
  116. // tween.TweenInterval(0.2f);
  117. // tween.Chain();
  118. _tween.TweenCallback(Callable.From(() =>
  119. {
  120. //执行反弹
  121. if (doRebound)
  122. {
  123. CallDeferred(nameof(OnRebound), reboundPosition.Value, reboundNormal.Value);
  124. }
  125. Collision.SetDeferred(CollisionShape2D.PropertyName.Disabled, false);
  126. }));
  127. _tween.Chain();
  128. _tween.TweenProperty(LineSprite, "scale", new Vector2(distance, 0), 0.3f);
  129. _tween.Chain();
  130. _tween.TweenCallback(Callable.From(() =>
  131. {
  132. State = BulletStateEnum.MaxDistance;
  133. _tween = null;
  134. LogicalFinish();
  135. }));
  136. _tween.Play();
  137. if (Particles2D != null)
  138. {
  139. foreach (var particles2D in Particles2D)
  140. {
  141. particles2D.Restart();
  142. }
  143. }
  144. }
  145.  
  146. public override void _Process(double delta)
  147. {
  148. ProxyCoroutineHandler.ProxyUpdateCoroutine(ref _coroutineList, (float)delta);
  149. }
  150.  
  151. public void Destroy()
  152. {
  153. if (IsDestroyed)
  154. {
  155. return;
  156. }
  157. QueueFree();
  158. }
  159.  
  160. //激光撞墙反弹逻辑
  161. private void OnRebound(Vector2 position, Vector2 normal)
  162. {
  163. if (BulletData.BounceCount > 0)
  164. {
  165. var newDistance = BulletData.MaxDistance - BulletData.Position.DistanceTo(position);
  166. if (newDistance > 0)
  167. {
  168. float rotation;
  169. if (normal.X == 0 && normal.Y == 0)
  170. {
  171. rotation = (BulletData.Rotation + Mathf.Pi) % (Mathf.Pi * 2);
  172. }
  173. else
  174. {
  175. rotation = Utils.ReflectByNormal(BulletData.Rotation, normal);
  176. }
  177. var bulletData = BulletData.Clone();
  178. bulletData.Position = position;
  179. bulletData.BounceCount -= 1;
  180. bulletData.MaxDistance = newDistance;
  181. bulletData.Rotation = rotation;
  182. FireManager.ShootBullet(bulletData, Camp);
  183. }
  184. }
  185. }
  186.  
  187. private void OnBodyEntered(Node2D body)
  188. {
  189. if (body is IHurt hurt)
  190. {
  191. HandlerCollision(hurt);
  192. }
  193. }
  194. private void OnArea2dEntered(Area2D other)
  195. {
  196. if (other is IHurt hurt)
  197. {
  198. HandlerCollision(hurt);
  199. }
  200. }
  201.  
  202. private void HandlerCollision(IHurt hurt)
  203. {
  204. var target = BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole;
  205. if (hurt.CanHurt(target))
  206. {
  207. if (BulletData.Repel != 0)
  208. {
  209. var o = hurt.GetActivityObject();
  210. if (o != null && o is not Player) //目标不是玩家才会触发击退
  211. {
  212. o.AddRepelForce(Vector2.FromAngle(Rotation) * BulletData.Repel);
  213. }
  214. }
  215. //造成伤害
  216. hurt.Hurt(target, BulletData.Harm, Rotation);
  217. }
  218. }
  219.  
  220. public long StartCoroutine(IEnumerator able)
  221. {
  222. return ProxyCoroutineHandler.ProxyStartCoroutine(ref _coroutineList, able);
  223. }
  224.  
  225. public void StopCoroutine(long coroutineId)
  226. {
  227. ProxyCoroutineHandler.ProxyStopCoroutine(ref _coroutineList, coroutineId);
  228. }
  229.  
  230. public bool IsCoroutineOver(long coroutineId)
  231. {
  232. return ProxyCoroutineHandler.ProxyIsCoroutineOver(ref _coroutineList, coroutineId);
  233. }
  234.  
  235. public void StopAllCoroutine()
  236. {
  237. ProxyCoroutineHandler.ProxyStopAllCoroutine(ref _coroutineList);
  238. }
  239. public void LogicalFinish()
  240. {
  241. ObjectPool.Reclaim(this);
  242. }
  243. public virtual void OnReclaim()
  244. {
  245. State = BulletStateEnum.Normal;
  246. if (Particles2D != null)
  247. {
  248. foreach (var particles2D in Particles2D)
  249. {
  250. particles2D.Emitting = false;
  251. }
  252. }
  253. if (OnReclaimEvent != null)
  254. {
  255. OnReclaimEvent();
  256. }
  257.  
  258. if (_tween != null)
  259. {
  260. _tween.Dispose();
  261. _tween = null;
  262. }
  263. GetParent().CallDeferred(Node.MethodName.RemoveChild, this);
  264. }
  265.  
  266. public virtual void OnLeavePool()
  267. {
  268. StopAllCoroutine();
  269. if (OnLeavePoolEvent != null)
  270. {
  271. OnLeavePoolEvent();
  272. }
  273. }
  274. }