BasicCameraExample.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. using System;
  2. using System.Diagnostics;
  3. using Microsoft.Xna.Framework;
  4. using Microsoft.Xna.Framework.Audio;
  5. using Microsoft.Xna.Framework.Graphics;
  6. using Microsoft.Xna.Framework.Input;
  7. namespace BasicCameraExample;
  8. public class BasicCameraExample : Game
  9. {
  10. enum CameraMode { Fixed, Tracking, FirstPerson, ThirdPerson, TopDownFixed, TopDownCentred };
  11. private GraphicsDeviceManager _graphics;
  12. private SpriteBatch _spriteBatch;
  13. #region Content
  14. // Drawing Text font
  15. private SpriteFont spriteFont;
  16. // Set the 3D player model to draw.
  17. private Model myModel;
  18. // Set the 3D ground model so that we get a sense of movement
  19. private Model groundModel;
  20. // Sound effects
  21. private SoundEffect engineSoundEffect;
  22. private SoundEffectInstance engineSound;
  23. private SoundEffect hyperspaceSoundEffect;
  24. #endregion Content
  25. #region Model controller properties
  26. // Set the velocity of the model, applied each frame to the model's position.
  27. private Vector3 modelVelocity = Vector3.Zero;
  28. // Set the position of the model in world space, and set the rotation.
  29. private Vector3 modelPosition = new Vector3(0.0f, 350.0f, 0.0f);
  30. // The current rotation of the model
  31. private float modelRotation = 0.0f;
  32. // The positional matrix for the model
  33. private Matrix modelWorldPosition;
  34. // Drag Co-Efficient
  35. float Drag = 0.97f;
  36. #endregion Model controller properties
  37. #region Camera Variables
  38. // Matrices required to correctly display our scene
  39. private Matrix currentCameraView;
  40. private Matrix currentCameraProjection;
  41. // Set the position of the camera in world space, for the fixed camera view matrix.
  42. private Vector3 cameraFixedPosition = new Vector3(0.0f, 1550.0f, 5000.0f);
  43. // 1st Person camera position relative to player model
  44. private Vector3 cameraFirstPersonPosition = new Vector3(0.0f, 50.0f, 500.0f);
  45. // 3rd Person camera position relative to player model
  46. private Vector3 cameraThirdPersonPosition = new Vector3(0.0f, 1550.0f, 5000.0f);
  47. // Top Down camera position relative to player model
  48. private Vector3 cameraTopDownPosition = new Vector3(0.0f, 25000.0f, 1.0f);
  49. // The aspect ratio determines how to scale 3d to 2d projection.
  50. private float aspectRatio;
  51. #endregion Camera Variables
  52. #region Camera Settings
  53. //Distance from the camera of the near and far clipping planes
  54. private float nearClip = 10.0f;
  55. private float farClip = 100000.0f;
  56. // Field of view of the camera in radians (pi/4 is 45 degrees).
  57. private float fieldOfView = MathHelper.ToRadians(45.0f);
  58. private CameraMode currentCameraMode = CameraMode.Fixed;
  59. // Camera Physics
  60. private float cameraStiffness = 1800.0f;
  61. private float cameraDamping = 600.0f;
  62. private float cameraMass = 50.0f;
  63. private Vector3 cameraVelocity = Vector3.Zero;
  64. private Vector3 currentCameraPosition = Vector3.Zero;
  65. private bool cameraSpringEnabled = true;
  66. #endregion Camera Settings
  67. public BasicCameraExample()
  68. {
  69. _graphics = new GraphicsDeviceManager(this);
  70. Content.RootDirectory = "Content";
  71. IsMouseVisible = true;
  72. }
  73. protected override void Initialize()
  74. {
  75. Debug.WriteLine("BasicCameraSample Initialize");
  76. aspectRatio = (float)GraphicsDevice.Viewport.Width / GraphicsDevice.Viewport.Height;
  77. currentCameraProjection = Matrix.CreatePerspectiveFieldOfView(fieldOfView, aspectRatio, nearClip, farClip);
  78. base.Initialize();
  79. }
  80. protected override void LoadContent()
  81. {
  82. Debug.WriteLine("BasicCameraSample LoadContent");
  83. _spriteBatch = new SpriteBatch(GraphicsDevice);
  84. groundModel = Content.Load<Model>("Models/Ground");
  85. myModel = Content.Load<Model>("Models/p1_wedge");
  86. spriteFont = Content.Load<SpriteFont>("Fonts/Tahoma");
  87. engineSoundEffect = Content.Load<SoundEffect>("Audio/Engine_2");
  88. engineSound = engineSoundEffect.CreateInstance();
  89. hyperspaceSoundEffect = Content.Load<SoundEffect>("Audio/hyperspace_activate");
  90. }
  91. protected override void Update(GameTime gameTime)
  92. {
  93. InputManager.Update();
  94. if (InputManager.IsButtonPressed(Buttons.Back) || InputManager.IsKeyPressed(Keys.Escape))
  95. Exit();
  96. // Get some input.
  97. UpdateInput(gameTime);
  98. // Add velocity to the current position.
  99. modelPosition += modelVelocity;
  100. // Bleed off velocity over time.
  101. modelVelocity *= Drag;
  102. // Update the ships world position based on input
  103. modelWorldPosition = Matrix.CreateRotationY(modelRotation) * Matrix.CreateTranslation(modelPosition);
  104. switch (currentCameraMode)
  105. {
  106. case CameraMode.Fixed:
  107. UpdateFixedCamera();
  108. break;
  109. case CameraMode.Tracking:
  110. UpdateTrackingCamera();
  111. break;
  112. case CameraMode.FirstPerson:
  113. UpdateFirstPersonCamera();
  114. break;
  115. case CameraMode.ThirdPerson:
  116. UpdateThirdPersonCamera((float)gameTime.ElapsedGameTime.TotalSeconds);
  117. break;
  118. case CameraMode.TopDownFixed:
  119. UpdateTopDownFixedCamera();
  120. break;
  121. case CameraMode.TopDownCentred:
  122. UpdateTopDownCenteredCamera();
  123. break;
  124. }
  125. base.Update(gameTime);
  126. }
  127. protected override void Draw(GameTime gameTime)
  128. {
  129. GraphicsDevice.Clear(Color.CornflowerBlue);
  130. // Crucial when drawing 3D models to set the GraphicsDevice to the correct state.
  131. // Especially when drawing both 3D and 2D/Text in the same scene.
  132. SetCameraDrawingState();
  133. // Ground drawn from the center of the scene
  134. groundModel.Draw(Matrix.Identity, currentCameraView, currentCameraProjection);
  135. // Ship model drawn from its current position / rotation
  136. if (currentCameraMode != CameraMode.FirstPerson) // Comment out this line if you want to see the inside of the ship :)
  137. myModel.Draw(modelWorldPosition, currentCameraView, currentCameraProjection);
  138. // Draw Help Text and other HUD stuff
  139. DrawHUD();
  140. base.Draw(gameTime);
  141. }
  142. private void SetCameraDrawingState()
  143. {
  144. GraphicsDevice.BlendState = BlendState.Opaque;
  145. GraphicsDevice.RasterizerState = RasterizerState.CullNone;
  146. GraphicsDevice.DepthStencilState = DepthStencilState.Default;
  147. GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
  148. }
  149. protected void UpdateInput(GameTime aGameTime)
  150. {
  151. if (InputManager.IsKeyDown(Keys.Left))
  152. {
  153. // Rotate Left
  154. modelRotation += (float)(aGameTime.ElapsedGameTime.TotalMilliseconds * MathHelper.ToRadians(0.1f));
  155. }
  156. else if (InputManager.IsKeyDown(Keys.Right))
  157. {
  158. // Rotate Right
  159. modelRotation -= (float)(aGameTime.ElapsedGameTime.TotalMilliseconds * MathHelper.ToRadians(0.1f));
  160. }
  161. else
  162. {
  163. // Rotate the model using the left thumbstick, and scale it down.
  164. modelRotation -= InputManager.LeftThumbStick.X * 0.10f;
  165. }
  166. // Create some velocity if the right trigger is down.
  167. Vector3 modelVelocityAdd = Vector3.Zero;
  168. // Find out what direction we should be thrusting, using rotation.
  169. modelVelocityAdd.X = -(float)Math.Sin(modelRotation);
  170. modelVelocityAdd.Z = -(float)Math.Cos(modelRotation);
  171. // Now scale our direction by how hard the trigger is down.
  172. if (InputManager.IsKeyDown(Keys.Up))
  173. {
  174. modelVelocityAdd /= (float)(aGameTime.ElapsedGameTime.TotalMilliseconds * MathHelper.ToRadians(0.1f));
  175. }
  176. else
  177. {
  178. modelVelocityAdd *= InputManager.RightTriggerValue * 10;
  179. }
  180. // Finally, add this vector to our velocity.
  181. modelVelocity += modelVelocityAdd;
  182. GamePad.SetVibration(PlayerIndex.One, InputManager.RightTriggerValue,
  183. InputManager.RightTriggerValue);
  184. // Set some audio based on whether we're pressing a trigger.
  185. if ((InputManager.RightTriggerValue > 0) || InputManager.IsKeyDown(Keys.Up))
  186. {
  187. if (engineSound.State == SoundState.Stopped)
  188. {
  189. engineSound = engineSoundEffect.CreateInstance();
  190. engineSound.IsLooped = true;
  191. engineSound.Play();
  192. }
  193. else if (engineSound.State == SoundState.Paused)
  194. {
  195. engineSound.Resume();
  196. }
  197. }
  198. else
  199. {
  200. if (engineSound.State == SoundState.Playing)
  201. {
  202. engineSound.Pause();
  203. }
  204. }
  205. // In case you get lost, press A to warp back to the center.
  206. if (InputManager.IsButtonPressed(Buttons.A) || InputManager.IsKeyPressed(Keys.Space))
  207. {
  208. modelPosition = new Vector3(0.0f, 350.0f, 0.0f);
  209. modelVelocity = Vector3.Zero;
  210. modelRotation = 0.0f;
  211. // Make a sound when we warp.
  212. hyperspaceSoundEffect.Play();
  213. }
  214. // Toggle the state of the camera.
  215. if (InputManager.IsButtonPressed(Buttons.LeftShoulder) || InputManager.IsKeyPressed(Keys.Tab))
  216. {
  217. currentCameraMode++;
  218. currentCameraMode = (CameraMode)((int)currentCameraMode % 6);
  219. }
  220. // Pressing the A button or key toggles the spring behavior on and off
  221. if (InputManager.IsButtonPressed(Buttons.A) || InputManager.IsKeyPressed(Keys.A))
  222. {
  223. cameraSpringEnabled = !cameraSpringEnabled;
  224. }
  225. }
  226. #region Camera Modes
  227. /// <summary>
  228. /// The following methods update the camera view matrix based on the current camera mode.
  229. /// </summary>
  230. /// <summary>
  231. /// Helper method to update the current camera view matrix.
  232. /// </summary>
  233. void UpdateCameraView(Vector3 aCameraPosition, Vector3 aCameraTarget)
  234. {
  235. currentCameraView = Matrix.CreateLookAt(aCameraPosition, aCameraTarget, Vector3.Up);
  236. }
  237. void UpdateFixedCamera()
  238. {
  239. // Fixed view, the camera is always in the same position and looking the same way, no updates.
  240. UpdateCameraView(cameraFixedPosition, Vector3.Zero);
  241. }
  242. void UpdateTrackingCamera()
  243. {
  244. // Tracking view, the camera is always in the same position but changes the view matrix to "look" towards a target.
  245. // Set up our world matrix, view matrix and projection matrix.
  246. UpdateCameraView(cameraFixedPosition, modelPosition);
  247. }
  248. void UpdateFirstPersonCamera()
  249. {
  250. // First person view, the camera moves based on the Model's position (which is moved by input) and the view matrix is updated to always look "forward" from the model.
  251. Matrix rotationMatrix = Matrix.CreateRotationY(modelRotation);
  252. // Create a vector pointing the direction the camera is facing.
  253. Vector3 transformedReference = Vector3.Transform(cameraFirstPersonPosition, rotationMatrix);
  254. // Calculate the position the camera is looking from.
  255. currentCameraPosition = transformedReference + modelPosition;
  256. // Set up our world matrix, view matrix and projection matrix.
  257. UpdateCameraView(currentCameraPosition, modelPosition);
  258. }
  259. void UpdateThirdPersonCamera(float aElapsed)
  260. {
  261. // In Third person view the camera is offset behind and above the model and moves with it,the view matrix is updated to always look "forward" from the model.
  262. // It also includes an optional spring physics system to smooth out the camera movement.
  263. Matrix rotationMatrix = Matrix.CreateRotationY(modelRotation);
  264. // Create a vector pointing the direction the camera is facing.
  265. Vector3 transformedReference = Vector3.Transform(cameraThirdPersonPosition, rotationMatrix);
  266. // If camera spring is enabled, update the position and rotation of the camera over several frames
  267. if (cameraSpringEnabled)
  268. {
  269. // Calculate the position where we would like the camera to be looking from.
  270. Vector3 desiredPosition = transformedReference + modelPosition;
  271. // Calculate spring force
  272. Vector3 stretch = currentCameraPosition - desiredPosition;
  273. Vector3 force = -cameraStiffness * stretch - cameraDamping * cameraVelocity;
  274. // Apply acceleration
  275. Vector3 acceleration = force / cameraMass;
  276. cameraVelocity += acceleration * aElapsed;
  277. // Apply velocity
  278. currentCameraPosition += cameraVelocity * aElapsed;
  279. }
  280. else
  281. {
  282. // Calculate the position the camera is looking from.
  283. currentCameraPosition = transformedReference + modelPosition;
  284. }
  285. // Set up our world matrix, view matrix and projection matrix.
  286. UpdateCameraView(currentCameraPosition, modelPosition);
  287. }
  288. void UpdateTopDownFixedCamera()
  289. {
  290. // A Top-Down fixed view, the camera is always in the same position and looking down onto the game scene.
  291. // Note, there are no boundaries to prevent the model from moving out of view.
  292. // Set up our world matrix, view matrix and projection matrix.
  293. UpdateCameraView(cameraTopDownPosition, Vector3.Zero);
  294. }
  295. void UpdateTopDownCenteredCamera()
  296. {
  297. // A Top-Down view that moves according to two dimensional position of the model, looking down onto the model.
  298. Matrix rotationMatrix = Matrix.CreateRotationY(modelRotation);
  299. // Create a vector pointing the direction the camera is facing.
  300. Vector3 transformedReference = Vector3.Transform(cameraTopDownPosition, rotationMatrix);
  301. // Calculate the position the camera is looking from.
  302. currentCameraPosition = transformedReference + modelPosition;
  303. // Set up our world matrix, view matrix and projection matrix.
  304. UpdateCameraView(currentCameraPosition, modelPosition);
  305. }
  306. #endregion Camera Modes
  307. #region Draw Methods
  308. void DrawHUD()
  309. {
  310. _spriteBatch.Begin();
  311. string Helptext = "Toggle Camera Modes ( " + currentCameraMode.ToString() + " ) = Tab or LeftShoulder Button\n" +
  312. "Steer = Left & Right Arrow keys or Left Thumbstick\n" +
  313. "Accelerate = Up Arrow key or Right Trigger\n" +
  314. "Reset = A Button or Spacebar";
  315. // Draw the string twice to create a drop shadow, first colored black
  316. // and offset one pixel to the bottom right, then again in white at the
  317. // intended position. This makes text easier to read over the background.
  318. _spriteBatch.DrawString(spriteFont, Helptext, new Vector2(20, 20), Color.Black);
  319. _spriteBatch.DrawString(spriteFont, Helptext, new Vector2(19, 19), Color.White);
  320. if (currentCameraMode == CameraMode.FirstPerson)
  321. {
  322. string HudText = "Velocity :" + modelVelocity.ToString() + "\n" +
  323. "Position :" + modelPosition.ToString() + "\n" +
  324. "Rotation :" + modelRotation.ToString() + "\n";
  325. _spriteBatch.DrawString(spriteFont, HudText, new Vector2(20, 400), Color.Blue);
  326. }
  327. _spriteBatch.End();
  328. }
  329. #endregion Draw Methods
  330. }