Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / camera / GameCamera.cs
@小李xl 小李xl on 30 Nov 2023 6 KB 小地图,制作中
  1. using System;
  2. using System.Collections.Generic;
  3. using Godot;
  4.  
  5. /// <summary>
  6. /// 游戏相机
  7. /// </summary>
  8. public partial class GameCamera : Camera2D
  9. {
  10. private class ShakeData
  11. {
  12. public Vector2 Value;
  13. public bool Decline;
  14. public float DataDelta;
  15.  
  16. public ShakeData(Vector2 value, bool decline, float dataDelta)
  17. {
  18. Value = value;
  19. Decline = decline;
  20. DataDelta = dataDelta;
  21. }
  22. }
  23.  
  24. /// <summary>
  25. /// 当前场景的相机对象
  26. /// </summary>
  27. public static GameCamera Main { get; private set; }
  28.  
  29. /// <summary>
  30. /// 相机坐标更新完成事件, 参数为 delta
  31. /// </summary>
  32. public event Action<float> OnPositionUpdateEvent;
  33.  
  34. /// <summary>
  35. /// 恢复系数
  36. /// </summary>
  37. [Export] public float RecoveryCoefficient = 25f;
  38.  
  39. /// <summary>
  40. /// 抖动开关
  41. /// </summary>
  42. public bool EnableShake { get; set; } = true;
  43.  
  44. /// <summary>
  45. /// 镜头跟随鼠标进度 (0 - 1)
  46. /// </summary>
  47. public float FollowsMouseAmount = 0.15f;
  48.  
  49. /// <summary>
  50. /// 相机跟随目标
  51. /// </summary>
  52. private Role _followTarget;
  53.  
  54. /// <summary>
  55. /// SubViewportContainer 中的像素偏移, 因为游戏开启了完美像素, SubViewport 节点下的相机运动会造成非常大的抖动,
  56. /// 为了解决这个问题, 在 SubViewport 父节点中对 SubViewport 进行整体偏移, 以抵消相机造成的巨大抖动
  57. /// </summary>
  58. public Vector2 PixelOffset { get; private set; }
  59.  
  60. private long _index = 0;
  61. private Vector2 _processDistanceSquared = Vector2.Zero;
  62. private Vector2 _processDirection = Vector2.Zero;
  63. //抖动数据
  64. private readonly Dictionary<long, ShakeData> _shakeMap = new Dictionary<long, ShakeData>();
  65. private Vector2 _camPos;
  66. private Vector2 _shakeOffset = Vector2.Zero;
  67. public ShaderMaterial _offsetShader;
  68.  
  69. public GameCamera()
  70. {
  71. Main = this;
  72. }
  73. public override void _Ready()
  74. {
  75. _offsetShader = (ShaderMaterial)GameApplication.Instance.SubViewportContainer.Material;
  76. _camPos = GlobalPosition;
  77. }
  78. //_PhysicsProcess
  79. public override void _PhysicsProcess(double delta)
  80. {
  81. var newDelta = (float)delta;
  82. _Shake(newDelta);
  83. var world = GameApplication.Instance.World;
  84. if (world != null && !world.Pause && _followTarget != null)
  85. {
  86. var mousePosition = InputManager.CursorPosition;
  87. var targetPosition = _followTarget.GlobalPosition;
  88. if (targetPosition.DistanceSquaredTo(mousePosition) >= (60 / FollowsMouseAmount) * (60 / FollowsMouseAmount))
  89. {
  90. _camPos = targetPosition.MoveToward(mousePosition, 60);
  91. }
  92. else
  93. {
  94. _camPos = targetPosition.Lerp(mousePosition, FollowsMouseAmount);
  95. }
  96.  
  97. var cameraPosition = _camPos;
  98. var roundPos = cameraPosition.Round();
  99. PixelOffset = roundPos - cameraPosition;
  100. _offsetShader.SetShaderParameter("offset", PixelOffset);
  101. GlobalPosition = roundPos;
  102. Offset = _shakeOffset.Round();
  103. //调用相机更新事件
  104. if (OnPositionUpdateEvent != null)
  105. {
  106. OnPositionUpdateEvent(newDelta);
  107. }
  108. }
  109. }
  110.  
  111. /// <summary>
  112. /// 设置相机跟随目标
  113. /// </summary>
  114. public void SetFollowTarget(Role target)
  115. {
  116. _followTarget = target;
  117. if (target != null)
  118. {
  119. _camPos = target.GlobalPosition;
  120. GlobalPosition = _camPos;
  121. }
  122. }
  123.  
  124. /// <summary>
  125. /// 获取相机跟随目标
  126. /// </summary>
  127. public Role GetFollowTarget()
  128. {
  129. return _followTarget;
  130. }
  131. /// <summary>
  132. /// 设置帧抖动, 结束后自动清零, 需要每一帧调用
  133. /// </summary>
  134. /// <param name="value">抖动的力度</param>
  135. public void Shake(Vector2 value)
  136. {
  137. if (value.LengthSquared() > _processDistanceSquared.LengthSquared())
  138. {
  139. _processDistanceSquared = value;
  140. }
  141. }
  142. /// <summary>
  143. /// 添加一个单方向上的抖动, 该帧结束后自动清零
  144. /// </summary>
  145. public void DirectionalShake(Vector2 value)
  146. {
  147. _processDirection += value;
  148. }
  149. /// <summary>
  150. /// 创建一个抖动, 并设置抖动时间
  151. /// </summary>
  152. public async void CreateShake(Vector2 value, float time, bool decline = false)
  153. {
  154. if (time > 0)
  155. {
  156. value.X = Mathf.Abs(value.X);
  157. value.Y = Mathf.Abs(value.Y);
  158. var tempIndex = _index++;
  159. var sceneTreeTimer = GetTree().CreateTimer(time);
  160. if (decline)
  161. {
  162. _shakeMap[tempIndex] = new ShakeData(value, true, value.Length() / time);
  163. }
  164. else
  165. {
  166. _shakeMap[tempIndex] = new ShakeData(value, false, 0);
  167. }
  168.  
  169. await ToSignal(sceneTreeTimer, Timer.SignalName.Timeout);
  170. _shakeMap.Remove(tempIndex);
  171. }
  172. }
  173.  
  174. /// <summary>
  175. /// 播放玩家死亡特写镜头
  176. /// </summary>
  177. public void PlayPlayerDieFeatures()
  178. {
  179. }
  180.  
  181. //抖动调用
  182. private void _Shake(float delta)
  183. {
  184. if (EnableShake)
  185. {
  186. var distance = _CalculateDistanceSquared(delta);
  187. distance = new Vector2(Mathf.Sqrt(distance.X), Mathf.Sqrt(distance.Y));
  188. var offset = Offset;
  189. _shakeOffset += _processDirection + new Vector2(
  190. (float)GD.RandRange(-distance.X, distance.X) - offset.X,
  191. (float)GD.RandRange(-distance.Y, distance.Y) - offset.Y
  192. );
  193. _processDistanceSquared = Vector2.Zero;
  194. _processDirection = _processDirection.Lerp(Vector2.Zero, RecoveryCoefficient * delta);
  195. }
  196. else
  197. {
  198. _shakeOffset = _shakeOffset.Lerp(Vector2.Zero, RecoveryCoefficient * delta);
  199. }
  200. }
  201.  
  202. //计算相机需要抖动的值
  203. private Vector2 _CalculateDistanceSquared(float delta)
  204. {
  205. var temp = Vector2.Zero;
  206. float length = 0;
  207.  
  208. foreach (var keyValuePair in _shakeMap)
  209. {
  210. var shakeData = keyValuePair.Value;
  211. var tempLenght = shakeData.Value.LengthSquared();
  212. if (tempLenght > length)
  213. {
  214. length = tempLenght;
  215. temp = shakeData.Value;
  216. if (shakeData.Decline)
  217. {
  218. shakeData.Value = shakeData.Value.MoveToward(Vector2.Zero, shakeData.DataDelta * delta);
  219. //Debug.Log("shakeData.Value: " + shakeData.Value + ", _processDistanceSquared: " + _processDistanceSquared);
  220. }
  221. }
  222. }
  223.  
  224. //return temp;
  225. return _processDistanceSquared.LengthSquared() > length ? _processDistanceSquared : temp;
  226. }
  227. }