RoamCameraController.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. using UnityEngine;
  2. using DG.Tweening;
  3. /// <summary>
  4. /// 漫游视角 相机控制脚本
  5. /// </summary>
  6. public class RoamCameraController : MonoBehaviour
  7. {
  8. /// <summary>
  9. /// 相机状态类
  10. /// </summary>
  11. private class CameraState
  12. {
  13. /// <summary>
  14. /// 坐标x值
  15. /// </summary>
  16. public float posX;
  17. /// <summary>
  18. /// 坐标y值
  19. /// </summary>
  20. public float posY;
  21. /// <summary>
  22. /// 坐标z值
  23. /// </summary>
  24. public float posZ;
  25. /// <summary>
  26. /// 旋转x值
  27. /// </summary>
  28. public float rotX;
  29. /// <summary>
  30. /// 旋转y值
  31. /// </summary>
  32. public float rotY;
  33. /// <summary>
  34. /// 旋转z值
  35. /// </summary>
  36. public float rotZ;
  37. //活动区域限制
  38. private readonly float xMinValue;
  39. private readonly float xMaxValue;
  40. private readonly float yMinValue;
  41. private readonly float yMaxValue;
  42. private readonly float zMinValue;
  43. private readonly float zMaxValue;
  44. /// <summary>
  45. /// 默认构造函数
  46. /// </summary>
  47. public CameraState()
  48. {
  49. xMinValue = float.MinValue;
  50. xMaxValue = float.MaxValue;
  51. yMinValue = float.MinValue;
  52. yMaxValue = float.MaxValue;
  53. zMinValue = float.MinValue;
  54. zMaxValue = float.MaxValue;
  55. }
  56. /// <summary>
  57. /// 构造函数
  58. /// </summary>
  59. /// <param name="xMinValue"></param>
  60. /// <param name="xMaxValue"></param>
  61. /// <param name="yMinValue"></param>
  62. /// <param name="yMaxValue"></param>
  63. /// <param name="zMinValue"></param>
  64. /// <param name="zMaxValue"></param>
  65. public CameraState(float xMinValue, float xMaxValue, float yMinValue, float yMaxValue, float zMinValue, float zMaxValue)
  66. {
  67. this.xMinValue = xMinValue;
  68. this.xMaxValue = xMaxValue;
  69. this.yMinValue = yMinValue;
  70. this.yMaxValue = yMaxValue;
  71. this.zMinValue = zMinValue;
  72. this.zMaxValue = zMaxValue;
  73. }
  74. /// <summary>
  75. /// 根据Transform组件更新状态
  76. /// </summary>
  77. /// <param name="t">Transform组件</param>
  78. public void SetFromTransform(Transform t)
  79. {
  80. posX = t.position.x;
  81. posY = t.position.y;
  82. posZ = t.position.z;
  83. rotX = t.eulerAngles.x;
  84. rotY = t.eulerAngles.y;
  85. rotZ = t.eulerAngles.z;
  86. }
  87. /// <summary>
  88. /// 移动
  89. /// </summary>
  90. public void Translate(Vector3 translation)
  91. {
  92. Vector3 rotatedTranslation = Quaternion.Euler(rotX, rotY, rotZ) * translation;
  93. posX += rotatedTranslation.x;
  94. posY += rotatedTranslation.y;
  95. posZ += rotatedTranslation.z;
  96. posX = Mathf.Clamp(posX, xMinValue, xMaxValue);
  97. posY = Mathf.Clamp(posY, yMinValue, yMaxValue);
  98. posZ = Mathf.Clamp(posZ, zMinValue, zMaxValue);
  99. }
  100. /// <summary>
  101. /// 根据目标状态插值运算
  102. /// </summary>
  103. /// <param name="target">目标状态</param>
  104. /// <param name="positionLerpPct">位置插值率</param>
  105. /// <param name="rotationLerpPct">旋转插值率</param>
  106. public void LerpTowards(CameraState target, float positionLerpPct, float rotationLerpPct)
  107. {
  108. posX = Mathf.Lerp(posX, target.posX, positionLerpPct);
  109. posY = Mathf.Lerp(posY, target.posY, positionLerpPct);
  110. posZ = Mathf.Lerp(posZ, target.posZ, positionLerpPct);
  111. rotX = Mathf.Lerp(rotX, target.rotX, rotationLerpPct);
  112. rotY = Mathf.Lerp(rotY, target.rotY, rotationLerpPct);
  113. rotZ = Mathf.Lerp(rotZ, target.rotZ, rotationLerpPct);
  114. }
  115. /// <summary>
  116. /// 根据状态刷新Transform组件
  117. /// </summary>
  118. /// <param name="t">Transform组件</param>
  119. public void UpdateTransform(Transform t)
  120. {
  121. t.position = new Vector3(posX, posY, posZ);
  122. t.rotation = Quaternion.Euler(rotX, rotY, rotZ);
  123. }
  124. }
  125. #region Private Variables
  126. //控制开关
  127. [SerializeField] private bool toggle = true;
  128. //是否限制活动范围
  129. [SerializeField] private bool isRangeClamped;
  130. //限制范围 当isRangeClamped为true时起作用
  131. [SerializeField] private float xMinValue = -100f; //x最小值
  132. [SerializeField] private float xMaxValue = 100f; //x最大值
  133. [SerializeField] private float yMinValue = 0f; //y最小值
  134. [SerializeField] private float yMaxValue = 100f; //y最大值
  135. [SerializeField] private float zMinValue = -100f; //z最小值
  136. [SerializeField] private float zMaxValue = 100f; //z最大值
  137. //移动速度
  138. [SerializeField] private float translateSpeed = 10f;
  139. //加速系数 Shift按下时起作用
  140. [SerializeField] private float boost = 3.5f;
  141. //插值到目标位置所需的时间
  142. [Range(0.01f, 1f), SerializeField] private float positionLerpTime = 1f;
  143. //插值到目标旋转所需的时间
  144. [Range(0.01f, 1f), SerializeField] private float rotationLerpTime = 1f;
  145. //鼠标运动的灵敏度
  146. [Range(0.1f, 10f), SerializeField] private float mouseMovementSensitivity = 1f;
  147. //鼠标滚轮运动的速度
  148. [SerializeField] private float mouseScrollMoveSpeed = 10f;
  149. //用于鼠标滚轮移动 是否反转方向
  150. [SerializeField] private bool invertScrollDirection = false;
  151. //视角旋转是否反转y反向
  152. [SerializeField] private bool invertY = false;
  153. //用于限制垂直方向上旋转的最大值
  154. [SerializeField] private float verticalLimitMax = 60f;
  155. //用于限制垂直方向上旋转的最小值
  156. [SerializeField] private float verticalLimitMin = -60f;
  157. //移动量
  158. private Vector3 translation = Vector3.zero;
  159. //Tween动画
  160. private Tween tween;
  161. private CameraState initialCameraState;
  162. private CameraState targetCameraState;
  163. private CameraState interpolatingCameraState;
  164. #endregion
  165. #region Private Methods
  166. private void Awake()
  167. {
  168. //初始化
  169. if (isRangeClamped)
  170. {
  171. initialCameraState = new CameraState(xMinValue, xMaxValue, yMinValue, yMaxValue, zMinValue, zMaxValue);
  172. targetCameraState = new CameraState(xMinValue, xMaxValue, yMinValue, yMaxValue, zMinValue, zMaxValue);
  173. interpolatingCameraState = new CameraState(xMinValue, xMaxValue, yMinValue, yMaxValue, zMinValue, zMaxValue);
  174. }
  175. else
  176. {
  177. initialCameraState = new CameraState();
  178. targetCameraState = new CameraState();
  179. interpolatingCameraState = new CameraState();
  180. }
  181. }
  182. private void OnEnable()
  183. {
  184. initialCameraState.SetFromTransform(transform);
  185. targetCameraState.SetFromTransform(transform);
  186. interpolatingCameraState.SetFromTransform(transform);
  187. }
  188. private void LateUpdate()
  189. {
  190. if (!toggle) return;
  191. OnRotateUpdate();
  192. OnTranslateUpdate();
  193. OnResetUpdate();
  194. }
  195. private void OnRotateUpdate()
  196. {
  197. #if ENABLE_INPUT_SYSTEM
  198. bool pressed = Mouse.current.rightButton.isPressed;
  199. #else
  200. bool pressed = Input.GetMouseButton(1);
  201. #endif
  202. if (pressed)
  203. {
  204. #if ENABLE_INPUT_SYSTEM
  205. Vector2 input = Mouse.current.delta.ReadValue();
  206. #else
  207. Vector2 input = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
  208. #endif
  209. input.y *= invertY ? 1 : -1;
  210. //float mouseSensitivityFactor = mouseRotationSensitivityCurve.Evaluate(input.magnitude);
  211. targetCameraState.rotY += input.x * mouseMovementSensitivity;
  212. targetCameraState.rotX += input.y * mouseMovementSensitivity;
  213. targetCameraState.rotX = Mathf.Clamp(targetCameraState.rotX, verticalLimitMin, verticalLimitMax);
  214. }
  215. }
  216. private void OnResetUpdate()
  217. {
  218. #if ENABLE_INPUT_SYSTEM
  219. bool uPressed = Keyboard.current.uKey.wasPressedThisFrame;
  220. #else
  221. bool uPressed = Input.GetKeyDown(KeyCode.U);
  222. #endif
  223. //U键按下重置到初始状态
  224. if (uPressed)
  225. {
  226. ResetCamera();
  227. }
  228. }
  229. private void OnTranslateUpdate()
  230. {
  231. translation = GetInputTranslation() * Time.deltaTime * translateSpeed;
  232. targetCameraState.Translate(translation);
  233. float positionLerpPct = 1f - Mathf.Exp(Mathf.Log(1f - .99f) / positionLerpTime * Time.deltaTime);
  234. float rotationLerpPct = 1f - Mathf.Exp(Mathf.Log(1f - .99f) / rotationLerpTime * Time.deltaTime);
  235. interpolatingCameraState.LerpTowards(targetCameraState, positionLerpPct, rotationLerpPct);
  236. interpolatingCameraState.UpdateTransform(transform);
  237. }
  238. //获取输入
  239. private Vector3 GetInputTranslation()
  240. {
  241. Vector3 ts = new Vector3();
  242. //键盘W、A、S、D、Q、E控制运动方向
  243. #if ENABLE_INPUT_SYSTEM
  244. if (Keyboard.current.wKey.isPressed) ts += Vector3.forward;
  245. if (Keyboard.current.sKey.isPressed) ts += Vector3.back;
  246. if (Keyboard.current.aKey.isPressed) ts += Vector3.left;
  247. if (Keyboard.current.dKey.isPressed) ts += Vector3.right;
  248. if (Keyboard.current.qKey.isPressed) ts += Vector3.down;
  249. if (Keyboard.current.eKey.isPressed) ts += Vector3.up;
  250. #else
  251. if (Input.GetKey(KeyCode.W)) ts += Vector3.forward;
  252. if (Input.GetKey(KeyCode.S)) ts += Vector3.back;
  253. if (Input.GetKey(KeyCode.A)) ts += Vector3.left;
  254. if (Input.GetKey(KeyCode.D)) ts += Vector3.right;
  255. if (Input.GetKey(KeyCode.Q)) ts += Vector3.down;
  256. if (Input.GetKey(KeyCode.E)) ts += Vector3.up;
  257. #endif
  258. #if ENABLE_INPUT_SYSTEM
  259. //读取鼠标滚轮滚动值
  260. float wheelValue = Mouse.current.scroll.ReadValue().y;
  261. #else
  262. float wheelValue = Input.GetAxis("Mouse ScrollWheel");
  263. #endif
  264. ts += (wheelValue == 0 ? Vector3.zero : (wheelValue > 0 ? Vector3.forward : Vector3.back) * (invertScrollDirection ? -1 : 1)) * mouseScrollMoveSpeed;
  265. #if ENABLE_INPUT_SYSTEM
  266. //左Shift键按下时加速
  267. if (Keyboard.current.leftShiftKey.isPressed) ts *= boost;
  268. #elif ENABLE_INPUT_SYSTEM
  269. if (Input.GetKey(KeyCode.LeftShift)) ts *= boost;
  270. #else
  271. if (Input.GetMouseButton(2))
  272. {
  273. ts.y = -Input.GetAxis("Mouse Y");
  274. ts.x = -Input.GetAxis("Mouse X");
  275. }
  276. #endif
  277. return ts;
  278. }
  279. #if UNITY_EDITOR
  280. private void OnDrawGizmos()
  281. {
  282. //如果限制活动范围 将区域范围绘制出来
  283. if (isRangeClamped)
  284. {
  285. Gizmos.color = Color.red;
  286. Vector3[] points = new Vector3[8]
  287. {
  288. new Vector3(xMinValue, yMinValue, zMinValue),
  289. new Vector3(xMaxValue, yMinValue, zMinValue),
  290. new Vector3(xMaxValue, yMinValue, zMaxValue),
  291. new Vector3(xMinValue, yMinValue, zMaxValue),
  292. new Vector3(xMinValue, yMaxValue, zMinValue),
  293. new Vector3(xMaxValue, yMaxValue, zMinValue),
  294. new Vector3(xMaxValue, yMaxValue, zMaxValue),
  295. new Vector3(xMinValue, yMaxValue, zMaxValue)
  296. };
  297. for (int i = 0; i < 4; i++)
  298. {
  299. int start = i % 4;
  300. int end = (i + 1) % 4;
  301. Gizmos.DrawLine(points[start], points[end]);
  302. Gizmos.DrawLine(points[start + 4], points[end + 4]);
  303. Gizmos.DrawLine(points[start], points[i + 4]);
  304. }
  305. }
  306. }
  307. #endif
  308. #endregion
  309. #region Public Methods
  310. /// <summary>
  311. /// 重置摄像机到初始状态
  312. /// </summary>
  313. public void ResetCamera()
  314. {
  315. initialCameraState.UpdateTransform(transform);
  316. targetCameraState.SetFromTransform(transform);
  317. interpolatingCameraState.SetFromTransform(transform);
  318. }
  319. /// <summary>
  320. /// 聚焦
  321. /// </summary>
  322. /// <param name="position">目标位置</param>
  323. /// <param name="rotation">目标旋转</param>
  324. /// <param name="duration">时长</param>
  325. public void Focus(Vector3 position, Vector3 rotation, float duration)
  326. {
  327. tween?.Kill();
  328. toggle = false;
  329. transform.DORotate(rotation, duration);
  330. tween = transform.DOMove(position, duration).Play()
  331. .OnKill(() =>
  332. {
  333. toggle = true;
  334. interpolatingCameraState.SetFromTransform(transform);
  335. targetCameraState.SetFromTransform(transform);
  336. tween = null;
  337. });
  338. }
  339. #endregion
  340. }