ChaseCamera.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. //-----------------------------------------------------------------------------
  2. // ChaseCamera.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using Microsoft.Xna.Framework;
  8. using Microsoft.Xna.Framework.Input;
  9. using System;
  10. using System.Collections.Generic;
  11. using RacingGame.GameLogic;
  12. using RacingGame.Graphics;
  13. using RacingGame.Helpers;
  14. using RacingGame.Properties;
  15. using Color = Microsoft.Xna.Framework.Color;
  16. using Model = RacingGame.Graphics.Model;
  17. namespace RacingGame.GameLogic
  18. {
  19. /// <summary>
  20. /// Chase camera for our car. We are always close behind it.
  21. /// The camera rotation is not the same as the current car rotation,
  22. /// we interpolate the values a bit, allowing the user to do small changes
  23. /// without rotating the camera frantically. Also feels more realistic in
  24. /// curves. Derived from the CarController class, which controls the car
  25. /// by the user input. This camera class is not controlled by the user,
  26. /// its all automatic!
  27. /// </summary>
  28. public class ChaseCamera : CarPhysics
  29. {
  30. /// <summary>
  31. /// Current camera position.
  32. /// </summary>
  33. protected Vector3 cameraPos;
  34. /// <summary>
  35. /// Distance of the camera to our car.
  36. /// </summary>
  37. private float cameraDistance;
  38. /// <summary>
  39. /// Look vector to the car. The car is our look at target. The up
  40. /// vector is the same as the one from the car, but the look vector is
  41. /// different because we slowly interpolate it.
  42. /// </summary>
  43. private Vector3 cameraLookVector;
  44. /// <summary>
  45. /// Camera modes
  46. /// </summary>
  47. public enum CameraMode
  48. {
  49. /// <summary>
  50. /// Default mode for game and menu, just chasing the car.
  51. /// </summary>
  52. Default,
  53. /// <summary>
  54. /// Free camera mode, allows to freely rotate and zoom around the car,
  55. /// much cooler than the Default mode for testing and stuff.
  56. /// Also used when we lose a game (fallen out of the track).
  57. /// </summary>
  58. FreeCamera,
  59. }
  60. /// <summary>
  61. /// Current camera mode.
  62. /// </summary>
  63. private CameraMode cameraMode = CameraMode.Default;//FreeCamera;
  64. /// <summary>
  65. /// Rotation matrix, used in UpdateViewMatrix.
  66. /// </summary>
  67. private Matrix rotMatrix = Matrix.Identity;
  68. /// <summary>
  69. /// Rotation matrix
  70. /// </summary>
  71. /// <returns>Matrix</returns>
  72. public Matrix RotationMatrix
  73. {
  74. get
  75. {
  76. return rotMatrix;
  77. }
  78. }
  79. /// <summary>
  80. /// Max. value for camera wobbel timeout.
  81. /// </summary>
  82. const int MaxCameraWobbelTimeoutMs = 700;
  83. /// <summary>
  84. /// Camera wobbel timeout.
  85. /// Used to shake camera after a collision.
  86. /// </summary>
  87. static float cameraWobbelTimeoutMs = 0;
  88. /// <summary>
  89. /// Camera wobbel factor.
  90. /// </summary>
  91. static float cameraWobbelFactor = 1.0f;
  92. /// <summary>
  93. /// Sets the camera to wobble which fades over time.
  94. /// </summary>
  95. /// <param name="factor">Factor</param>
  96. public static void WobbelCamera(float wobbelFactor)
  97. {
  98. cameraWobbelTimeoutMs = (int)
  99. //((0.75f + 0.5f * wobbelFactor) *
  100. (MaxCameraWobbelTimeoutMs);
  101. cameraWobbelFactor = wobbelFactor;
  102. }
  103. /// <summary>
  104. /// Camera position
  105. /// </summary>
  106. /// <returns>Vector 3</returns>
  107. public Vector3 CameraPosition
  108. {
  109. get
  110. {
  111. return cameraPos;
  112. }
  113. }
  114. /// <summary>
  115. /// Get current x axis with help of the current view matrix.
  116. /// </summary>
  117. /// <returns>Vector 3</returns>
  118. static public Vector3 XAxis
  119. {
  120. get
  121. {
  122. // Get x column
  123. return new Vector3(
  124. BaseGame.ViewMatrix.M11,
  125. BaseGame.ViewMatrix.M21,
  126. BaseGame.ViewMatrix.M31);
  127. }
  128. }
  129. /// <summary>
  130. /// Get current y axis with help of the current view matrix.
  131. /// </summary>
  132. /// <returns>Vector 3</returns>
  133. static public Vector3 YAxis
  134. {
  135. get
  136. {
  137. // Get y column
  138. return new Vector3(
  139. BaseGame.ViewMatrix.M12,
  140. BaseGame.ViewMatrix.M22,
  141. BaseGame.ViewMatrix.M32);
  142. }
  143. }
  144. /// <summary>
  145. /// Get current z axis with help of the current view matrix.
  146. /// </summary>
  147. /// <returns>Vector 3</returns>
  148. static public Vector3 ZAxis
  149. {
  150. get
  151. {
  152. // Get z column
  153. return new Vector3(
  154. BaseGame.ViewMatrix.M13,
  155. BaseGame.ViewMatrix.M23,
  156. BaseGame.ViewMatrix.M33);
  157. }
  158. }
  159. /// <summary>
  160. /// Free camera
  161. /// </summary>
  162. /// <returns>Bool</returns>
  163. public bool FreeCamera
  164. {
  165. get
  166. {
  167. return cameraMode == CameraMode.FreeCamera;
  168. }
  169. set
  170. {
  171. if (value == true)
  172. cameraMode = CameraMode.FreeCamera;
  173. else
  174. cameraMode = CameraMode.Default;
  175. }
  176. }
  177. /// <summary>
  178. /// Create chase camera. Sets the car position and the camera position,
  179. /// which is then used to rotate around the car.
  180. /// </summary>
  181. /// <param name="setCarPosition">Set car position</param>
  182. /// <param name="setDirection">Set direction</param>
  183. /// <param name="setUp">Set up</param>
  184. /// <param name="setCameraPos">Set camera pos</param>
  185. public ChaseCamera(Vector3 setCarPosition, Vector3 setDirection,
  186. Vector3 setUp, Vector3 setCameraPos)
  187. : base(setCarPosition, setDirection, setUp)
  188. {
  189. // Set camera position and calculate rotation from look pos
  190. SetCameraPosition(setCameraPos);
  191. }
  192. /// <summary>
  193. /// Create chase camera. Sets the car position and the camera position,
  194. /// which is then used to rotate around the car.
  195. /// </summary>
  196. /// <param name="setCarPosition">Set car position</param>
  197. /// <param name="setCameraPos">Set camera pos</param>
  198. public ChaseCamera(Vector3 setCarPosition, Vector3 setCameraPos)
  199. : base(setCarPosition)
  200. {
  201. // Set camera position and calculate rotation from look pos
  202. SetCameraPosition(setCameraPos);
  203. }
  204. /// <summary>
  205. /// Create chase camera. Just sets the car position.
  206. /// The chase camera is set behind it.
  207. /// </summary>
  208. /// <param name="setCarPosition">Set car position</param>
  209. public ChaseCamera(Vector3 setCarPosition)
  210. : base(setCarPosition)
  211. {
  212. // Set camera position and calculate rotation from look pos
  213. SetCameraPosition(
  214. //setCarPosition - new Vector3(0, 0.5f, 1.0f) * carDir);
  215. setCarPosition + new Vector3(0, 10.0f, 25.0f));
  216. }
  217. /// <summary>
  218. /// Set camera position
  219. /// </summary>
  220. /// <param name="setCameraPos">Set camera position</param>
  221. public void SetCameraPosition(Vector3 setCameraPos)
  222. {
  223. cameraPos = setCameraPos;
  224. cameraDistance = Vector3.Distance(LookAtPos, cameraPos);
  225. cameraLookVector = LookAtPos - cameraPos;
  226. wannaCameraDistance = cameraDistance;
  227. wannaCameraLookVector = cameraLookVector;
  228. // Build look at matrix
  229. rotMatrix = Matrix.CreateLookAt(cameraPos, LookAtPos, CarUpVector);
  230. }
  231. Vector3 wannaCameraLookVector = Vector3.Zero;
  232. float wannaCameraDistance = 0.0f;
  233. /// <summary>
  234. /// Interpolate camera position
  235. /// </summary>
  236. /// <param name="setInterpolatedCameraPos">Set interpolated camera
  237. /// position</param>
  238. public void InterpolateCameraPosition(Vector3 setInterpolatedCameraPos)
  239. {
  240. // Don't use for free camera
  241. if (FreeCamera)
  242. return;
  243. if (wannaCameraDistance == 0.0f)
  244. SetCameraPosition(setInterpolatedCameraPos);
  245. wannaCameraDistance =
  246. Vector3.Distance(LookAtPos, setInterpolatedCameraPos);
  247. wannaCameraLookVector = LookAtPos - setInterpolatedCameraPos;
  248. }
  249. /// <summary>
  250. /// Helper values to keep the free camera steady.
  251. /// </summary>
  252. private Vector3 freeCameraRot = new Vector3(
  253. MathHelper.Pi, 0, -MathHelper.Pi / 2);
  254. /// <summary>
  255. /// Wanna have camera rotation
  256. /// </summary>
  257. Vector3 wannaHaveCameraRotation = Vector3.Zero;
  258. /// <summary>
  259. /// Handle free camera, only used for unit tests.
  260. /// </summary>
  261. private void HandleFreeCamera()
  262. {
  263. // Don't control the camera in the menu or game, only in unit tests!
  264. if (cameraMode != CameraMode.FreeCamera)
  265. return;
  266. float rotationFactor = 0.0075f;
  267. float gamePadRotFactor = 5.0f * BaseGame.MoveFactorPerSecond;
  268. // We don't use lookDistance or cameraRotation here, so we have
  269. // to calculate this values here.
  270. cameraDistance = cameraLookVector.Length();
  271. if (wannaHaveCameraRotation.Equals(Vector3.Zero))
  272. wannaHaveCameraRotation = freeCameraRot;
  273. Vector3 rot = wannaHaveCameraRotation;
  274. float addRotX =
  275. // Allow mouse input
  276. -Input.MouseXMovement * rotationFactor +
  277. // And gamepad input
  278. Input.GamePad.ThumbSticks.Left.X * gamePadRotFactor;
  279. // Also allow gamepad and keyboard cursors
  280. if (addRotX == 0)
  281. {
  282. if (Input.GamePadLeftPressed ||
  283. Input.KeyboardLeftPressed)
  284. addRotX = -gamePadRotFactor;
  285. if (Input.GamePadRightPressed ||
  286. Input.KeyboardRightPressed)
  287. addRotX = +gamePadRotFactor;
  288. }
  289. float addRotY =
  290. // Allow mouse input
  291. -Input.MouseYMovement * rotationFactor +
  292. // And gamepad input
  293. Input.GamePad.ThumbSticks.Left.Y * gamePadRotFactor;
  294. // Also allow gamepad and keyboard cursors
  295. if (addRotY == 0)
  296. {
  297. if (Input.GamePadUpPressed ||
  298. Input.KeyboardUpPressed)
  299. addRotY = -gamePadRotFactor;
  300. if (Input.GamePadDownPressed ||
  301. Input.KeyboardDownPressed)
  302. addRotY = +gamePadRotFactor;
  303. }
  304. wannaHaveCameraRotation = new Vector3(
  305. rot.X,
  306. rot.Y + addRotY,
  307. rot.Z + addRotX);
  308. // Mix camera rotation slowly to wanna have rotation
  309. freeCameraRot = Vector3.Lerp(
  310. freeCameraRot, wannaHaveCameraRotation, 0.5f);
  311. // Substract a very small value to make sure we never reach PI,
  312. // this causes the z rotation to mess everything else up ...
  313. float minRotationRange = BaseGame.Epsilon;
  314. float maxRotationRange = (float)Math.PI - BaseGame.Epsilon;
  315. if (freeCameraRot.X < minRotationRange)
  316. {
  317. freeCameraRot.X = minRotationRange;
  318. }
  319. else if (freeCameraRot.X > maxRotationRange)
  320. {
  321. freeCameraRot.X = maxRotationRange;
  322. }
  323. // Calculate cameraPos like in HandleLookPosCamera()
  324. cameraLookVector = new Vector3(0, 0, cameraDistance);
  325. cameraLookVector = Vector3.TransformNormal(cameraLookVector,
  326. Matrix.CreateRotationX(freeCameraRot.X) *
  327. Matrix.CreateRotationY(freeCameraRot.Y) *
  328. Matrix.CreateRotationZ(freeCameraRot.Z));
  329. float moveFactor =
  330. (Input.Keyboard.IsKeyDown(Keys.LeftShift) ? 20.0f : 40.0f) *
  331. BaseGame.MoveFactorPerSecond;
  332. float smallMoveFactor = moveFactor / 4.0f;
  333. float lookDistanceChange = 0.0f;
  334. // Page up/down or Home/End to zoom in and out.
  335. if (Input.Keyboard.IsKeyDown(Keys.PageUp))
  336. lookDistanceChange += moveFactor * 0.05f;
  337. if (Input.Keyboard.IsKeyDown(Keys.PageDown))
  338. lookDistanceChange -= moveFactor * 0.05f;
  339. if (Input.Keyboard.IsKeyDown(Keys.Home))
  340. lookDistanceChange += smallMoveFactor * 0.05f;
  341. if (Input.Keyboard.IsKeyDown(Keys.End))
  342. lookDistanceChange -= smallMoveFactor * 0.05f;
  343. // Also allow mouse wheel to zoom
  344. if (Input.MouseWheelDelta != 0)
  345. {
  346. lookDistanceChange =
  347. Input.MouseWheelDelta * BaseGame.MoveFactorPerSecond / 16.0f;
  348. }
  349. // Also allow gamepad to zoom
  350. if (Input.GamePad.ThumbSticks.Right.Y != 0)
  351. {
  352. lookDistanceChange =
  353. Input.GamePad.ThumbSticks.Right.Y * BaseGame.MoveFactorPerSecond;
  354. }
  355. if (lookDistanceChange != 0)
  356. {
  357. // Half zoom effect if shift is pressed
  358. if (Input.Keyboard.IsKeyDown(Keys.LeftShift))
  359. lookDistanceChange /= 2.0f;
  360. cameraDistance *= 1.0f - lookDistanceChange;
  361. if (cameraDistance < 1.0f)
  362. cameraDistance = 1.0f;
  363. // Calculate cameraPos like in HandleLookPosCamera()
  364. cameraLookVector = Vector3.TransformNormal(
  365. new Vector3(0, 0, cameraDistance),
  366. Matrix.CreateRotationX(freeCameraRot.X) *
  367. Matrix.CreateRotationY(freeCameraRot.Y) *
  368. Matrix.CreateRotationZ(freeCameraRot.Z));
  369. }
  370. // Make sure we use these new values and don't interpolate them back.
  371. wannaCameraDistance = cameraDistance;
  372. wannaCameraLookVector = cameraLookVector;
  373. }
  374. Vector3 lastCameraWobble = Vector3.Zero;
  375. /// <summary>
  376. /// Update view matrix
  377. /// </summary>
  378. private void UpdateViewMatrix()
  379. {
  380. cameraDistance = cameraDistance * 0.9f + wannaCameraDistance * 0.1f;
  381. // Better interpolation formula, not good for slow framerates,
  382. // but looks much better on high frame rates this way.
  383. cameraLookVector =
  384. (cameraLookVector * 0.9f) +
  385. (wannaCameraLookVector * 0.1f);
  386. // Update camera pos based on the current lookPos and cameraDistance
  387. cameraPos = LookAtPos + cameraLookVector;
  388. // Build look at matrix
  389. rotMatrix = Matrix.CreateLookAt(cameraPos, LookAtPos, CarUpVector);
  390. // Is camera wobbeling?
  391. if (cameraWobbelTimeoutMs > 0)
  392. {
  393. cameraWobbelTimeoutMs -= BaseGame.ElapsedTimeThisFrameInMilliseconds;
  394. if (cameraWobbelTimeoutMs < 0)
  395. cameraWobbelTimeoutMs = 0;
  396. }
  397. // Add camera shake if camera wobbel effect is on
  398. if (cameraWobbelTimeoutMs > 0 &&
  399. // But only if not zooming in and if in game.
  400. ZoomInTime <= StartGameZoomTimeMilliseconds)
  401. {
  402. float effectStrength = 1.5f * cameraWobbelFactor *
  403. (cameraWobbelTimeoutMs / (float)MaxCameraWobbelTimeoutMs);
  404. // Interpolate, make wobbleing more smoooth than in Rocket Commander
  405. lastCameraWobble =
  406. lastCameraWobble * 0.9f +
  407. RandomHelper.GetRandomVector3(
  408. -effectStrength, +effectStrength) * 0.1f;
  409. rotMatrix *= Matrix.CreateTranslation(lastCameraWobble);
  410. }
  411. // Just set view matrix
  412. BaseGame.ViewMatrix = rotMatrix;
  413. }
  414. /// <summary>
  415. /// Resets just the camera wobbel factor here.
  416. /// </summary>
  417. public override void Reset()
  418. {
  419. base.Reset();
  420. cameraWobbelFactor = 0;
  421. }
  422. /// <summary>
  423. /// Clear variables for game over
  424. /// </summary>
  425. public override void ClearVariablesForGameOver()
  426. {
  427. base.ClearVariablesForGameOver();
  428. cameraWobbelFactor = 0;
  429. }
  430. /// <summary>
  431. /// Update camera, should be called every frame to handle all the input.
  432. /// </summary>
  433. public override void Update()
  434. {
  435. base.Update();
  436. // Only allow control when zooming is finished.
  437. HandleFreeCamera();
  438. UpdateViewMatrix();
  439. }
  440. }
  441. }