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 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. BulletData = data;
  65. AttackLayer = attackLayer;
  66. Position = data.Position;
  67. Rotation = data.Rotation;
  68.  
  69. //计算射线最大距离, 也就是撞到墙壁的距离
  70. var targetPosition = data.Position + Vector2.FromAngle(data.Rotation) * data.MaxDistance;
  71. var parameters = PhysicsRayQueryParameters2D.Create(data.Position, targetPosition, PhysicsLayer.Wall);
  72. var result = GetWorld2D().DirectSpaceState.IntersectRay(parameters);
  73. float distance;
  74. var doRebound = false; //是否需要执行反弹
  75. Vector2? reboundPosition = null;
  76. Vector2? reboundNormal = null;
  77. if (result != null && result.TryGetValue("position", out var point)) //撞到墙壁
  78. {
  79. doRebound = true;
  80. reboundPosition = (Vector2)point;
  81. reboundNormal = (Vector2)result["normal"];
  82. distance = Position.DistanceTo(reboundPosition.Value);
  83. }
  84. else //没撞到墙壁
  85. {
  86. distance = data.MaxDistance;
  87. }
  88. Collision.SetDeferred(CollisionShape2D.PropertyName.Disabled, false);
  89. Collision.Position = Vector2.Zero;
  90. Shape.Size = Vector2.Zero;
  91. LineSprite.Scale = new Vector2(0, width * _pixelScale);
  92.  
  93. //如果子弹会对玩家造成伤害, 则显示成红色
  94. if (Player.Current.CollisionWithMask(attackLayer))
  95. {
  96. LineSprite.Modulate = new Color(2.5f, 0.5f, 0.5f);
  97. }
  98. else
  99. {
  100. LineSprite.Modulate = new Color(1.5f, 1.5f, 1.5f);
  101. }
  102. //激光飞行时间
  103. var time = distance / _speed;
  104.  
  105. _tween = CreateTween();
  106. _tween.SetParallel();
  107. _tween.TweenProperty(LineSprite, "scale", new Vector2(distance, width * _pixelScale), time);
  108. _tween.TweenProperty(Collision, "position", new Vector2(distance * 0.5f, 0), time);
  109. _tween.TweenProperty(Shape, "size", new Vector2(distance, width), time);
  110. _tween.Chain();
  111. //持续时间
  112. // tween.TweenInterval(0.2f);
  113. // tween.Chain();
  114. _tween.TweenCallback(Callable.From(() =>
  115. {
  116. //执行反弹
  117. if (doRebound)
  118. {
  119. CallDeferred(nameof(OnRebound), reboundPosition.Value, reboundNormal.Value);
  120. }
  121. Collision.SetDeferred(CollisionShape2D.PropertyName.Disabled, false);
  122. }));
  123. _tween.Chain();
  124. _tween.TweenProperty(LineSprite, "scale", new Vector2(distance, 0), 0.3f);
  125. _tween.Chain();
  126. _tween.TweenCallback(Callable.From(() =>
  127. {
  128. _tween = null;
  129. DoReclaim();
  130. }));
  131. _tween.Play();
  132. if (Particles2D != null)
  133. {
  134. foreach (var particles2D in Particles2D)
  135. {
  136. particles2D.Restart();
  137. }
  138. }
  139. }
  140.  
  141. public override void _Process(double delta)
  142. {
  143. ProxyCoroutineHandler.ProxyUpdateCoroutine(ref _coroutineList, (float)delta);
  144. }
  145.  
  146. public void Destroy()
  147. {
  148. if (IsDestroyed)
  149. {
  150. return;
  151. }
  152. QueueFree();
  153. }
  154.  
  155. //激光撞墙反弹逻辑
  156. private void OnRebound(Vector2 position, Vector2 normal)
  157. {
  158. if (BulletData.BounceCount > 0)
  159. {
  160. var newDistance = BulletData.MaxDistance - BulletData.Position.DistanceTo(position);
  161. if (newDistance > 0)
  162. {
  163. float rotation;
  164. if (normal.X == 0 && normal.Y == 0)
  165. {
  166. rotation = (BulletData.Rotation + Mathf.Pi) % (Mathf.Pi * 2);
  167. }
  168. else
  169. {
  170. rotation = Utils.ReflectByNormal(BulletData.Rotation, normal);
  171. }
  172. var bulletData = BulletData.Clone();
  173. bulletData.Position = position;
  174. bulletData.BounceCount -= 1;
  175. bulletData.MaxDistance = newDistance;
  176. bulletData.Rotation = rotation;
  177. FireManager.ShootBullet(bulletData, AttackLayer);
  178. }
  179. }
  180. }
  181. private void OnArea2dEntered(Area2D other)
  182. {
  183. var role = other.AsActivityObject<Role>();
  184. if (role != null)
  185. {
  186. //击退
  187. if (BulletData.Repel != 0)
  188. {
  189. role.AddRepelForce(Vector2.FromAngle(Rotation) * BulletData.Repel);
  190. }
  191. //造成伤害
  192. role.CallDeferred(nameof(Role.Hurt), BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole, BulletData.Harm, Rotation);
  193. }
  194. }
  195.  
  196. public long StartCoroutine(IEnumerator able)
  197. {
  198. return ProxyCoroutineHandler.ProxyStartCoroutine(ref _coroutineList, able);
  199. }
  200.  
  201. public void StopCoroutine(long coroutineId)
  202. {
  203. ProxyCoroutineHandler.ProxyStopCoroutine(ref _coroutineList, coroutineId);
  204. }
  205.  
  206. public bool IsCoroutineOver(long coroutineId)
  207. {
  208. return ProxyCoroutineHandler.ProxyIsCoroutineOver(ref _coroutineList, coroutineId);
  209. }
  210.  
  211. public void StopAllCoroutine()
  212. {
  213. ProxyCoroutineHandler.ProxyStopAllCoroutine(ref _coroutineList);
  214. }
  215. public void DoReclaim()
  216. {
  217. ObjectPool.Reclaim(this);
  218. }
  219. public virtual void OnReclaim()
  220. {
  221. if (Particles2D != null)
  222. {
  223. foreach (var particles2D in Particles2D)
  224. {
  225. particles2D.Emitting = false;
  226. }
  227. }
  228. if (OnReclaimEvent != null)
  229. {
  230. OnReclaimEvent();
  231. }
  232.  
  233. if (_tween != null)
  234. {
  235. _tween.Dispose();
  236. _tween = null;
  237. }
  238. GetParent().CallDeferred(Node.MethodName.RemoveChild, this);
  239. }
  240.  
  241. public virtual void OnLeavePool()
  242. {
  243. StopAllCoroutine();
  244. if (OnLeavePoolEvent != null)
  245. {
  246. OnLeavePoolEvent();
  247. }
  248. }
  249. }