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