AimingGame.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // Game.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using Microsoft.Xna.Framework;
  11. using Microsoft.Xna.Framework.Content;
  12. using Microsoft.Xna.Framework.Graphics;
  13. using Microsoft.Xna.Framework.Input;
  14. using System;
  15. #endregion
  16. namespace Aiming
  17. {
  18. /// <summary>
  19. /// this sample showing how to aim one object towards another. In this sample, a
  20. /// spotlight turns to aim towards a cat that the player controls.
  21. /// </summary>
  22. public class AimingGame : Microsoft.Xna.Framework.Game
  23. {
  24. #region Constants
  25. // how fast can the cat move? this is in terms of pixels per frame.
  26. const float CatSpeed = 10.0f;
  27. // how fast can the spot light turn? this is in terms of radians per frame.
  28. const float SpotlightTurnSpeed = 0.025f;
  29. #endregion
  30. #region Fields
  31. GraphicsDeviceManager graphics;
  32. // we'll need a spriteBatch to draw the spotlight and cat.
  33. SpriteBatch spriteBatch;
  34. // these four values control the spotlight and how it draws.
  35. // first is the actual sprite that we'll draw to represent the spotlight.
  36. Texture2D spotlightTexture;
  37. // next is the position of the spotlight on the screen.
  38. Vector2 spotlightPosition = new Vector2();
  39. // the origin of the spotlightTexture. The spotlight will rotate around this
  40. // point.
  41. Vector2 spotlightOrigin = new Vector2();
  42. // the angle that the spotlight is currently facing. this is in radians. a value
  43. // of 0 points to the right.
  44. float spotlightAngle = 0.0f;
  45. // these next three variables control the cat. catTexture is the sprite that
  46. // represents the cat...
  47. Texture2D catTexture;
  48. // ...catPosition is the cat's position on the screen...
  49. Vector2 catPosition = new Vector2();
  50. // ...and catOrigin is the origin of catTexture. the sprite will be drawn
  51. // centered around this value.
  52. Vector2 catOrigin = new Vector2();
  53. #endregion
  54. #region Initialization
  55. public AimingGame()
  56. {
  57. graphics = new GraphicsDeviceManager(this);
  58. graphics.SupportedOrientations = DisplayOrientation.Portrait;
  59. Content.RootDirectory = "Content";
  60. #if WINDOWS_PHONE
  61. graphics.PreferredBackBufferWidth = 480;
  62. graphics.PreferredBackBufferHeight = 800;
  63. TargetElapsedTime = TimeSpan.FromTicks(333333);
  64. graphics.IsFullScreen = true;
  65. #else
  66. graphics.PreferredBackBufferWidth = 853;
  67. graphics.PreferredBackBufferHeight = 480;
  68. #endif
  69. }
  70. protected override void Initialize()
  71. {
  72. base.Initialize();
  73. // once base.Initialize has finished, the GraphicsDevice will have been
  74. // created, and we'll know how big the Viewport is. We want the spotlight
  75. // to be centered in the middle of the screen, so we'll use the viewport
  76. // to calculate where that is.
  77. Viewport vp = graphics.GraphicsDevice.Viewport;
  78. spotlightPosition.X = vp.X + vp.Width / 2;
  79. spotlightPosition.Y = vp.Y + vp.Height / 2;
  80. // we'll use the viewport size again, this time to put the cat on the
  81. // screen. He goes 1/4 of the way across and halfway down.
  82. catPosition.X = vp.X + vp.Width / 4;
  83. catPosition.Y = vp.Y + vp.Height / 2;
  84. }
  85. /// <summary>
  86. /// Load your graphics content.
  87. /// </summary>
  88. protected override void LoadContent()
  89. {
  90. // load our textures, and create a sprite batch...
  91. spotlightTexture = Content.Load<Texture2D>("spotlight");
  92. catTexture = Content.Load<Texture2D>("cat");
  93. spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
  94. // now that we've loaded our textures, we can use them to calculate some
  95. // values that we'll use when drawing them. When we draw the spotlight,
  96. // it needs to rotate around the "source" of the light. since
  97. // spriteBatch.Draw will rotate sprites around the "origin" parameter,
  98. // we need spotlightOrigin to be the "source" of the light. Since I drew
  99. // spotlight.png myself, I happen to know that the source is halfway
  100. // down the left hand side of the texture.
  101. spotlightOrigin.X = 0;
  102. spotlightOrigin.Y = spotlightTexture.Height / 2;
  103. // Next, we want spriteBatch to draw the cat texture centered on the
  104. // "catPosition" vector. SpriteBatch.Draw will center the sprite on the
  105. // "origin" parameter, so we'll just calculate that to be the middle of
  106. // the texture.
  107. catOrigin.X = catTexture.Width / 2;
  108. catOrigin.Y = catTexture.Height / 2;
  109. }
  110. #endregion
  111. #region Update and Draw
  112. /// <summary>
  113. /// Allows the game to run logic.
  114. /// </summary>
  115. protected override void Update(GameTime gameTime)
  116. {
  117. HandleInput();
  118. // clamp the cat's position so that it stays on the screen.
  119. Viewport vp = graphics.GraphicsDevice.Viewport;
  120. catPosition.X = MathHelper.Clamp(catPosition.X, vp.X, vp.X + vp.Width);
  121. catPosition.Y = MathHelper.Clamp(catPosition.Y, vp.Y, vp.Y + vp.Height);
  122. // use the TurnToFace function to update the spotlightAngle to face
  123. // towards the cat.
  124. spotlightAngle = TurnToFace(spotlightPosition, catPosition, spotlightAngle,
  125. SpotlightTurnSpeed);
  126. base.Update(gameTime);
  127. }
  128. /// <summary>
  129. /// Calculates the angle that an object should face, given its position, its
  130. /// target's position, its current angle, and its maximum turning speed.
  131. /// </summary>
  132. private static float TurnToFace(Vector2 position, Vector2 faceThis,
  133. float currentAngle, float turnSpeed)
  134. {
  135. // consider this diagram:
  136. // C
  137. // /|
  138. // / |
  139. // / | y
  140. // / o |
  141. // S--------
  142. // x
  143. //
  144. // where S is the position of the spot light, C is the position of the cat,
  145. // and "o" is the angle that the spot light should be facing in order to
  146. // point at the cat. we need to know what o is. using trig, we know that
  147. // tan(theta) = opposite / adjacent
  148. // tan(o) = y / x
  149. // if we take the arctan of both sides of this equation...
  150. // arctan( tan(o) ) = arctan( y / x )
  151. // o = arctan( y / x )
  152. // so, we can use x and y to find o, our "desiredAngle."
  153. // x and y are just the differences in position between the two objects.
  154. float x = faceThis.X - position.X;
  155. float y = faceThis.Y - position.Y;
  156. // we'll use the Atan2 function. Atan will calculates the arc tangent of
  157. // y / x for us, and has the added benefit that it will use the signs of x
  158. // and y to determine what cartesian quadrant to put the result in.
  159. // http://msdn2.microsoft.com/en-us/library/system.math.atan2.aspx
  160. float desiredAngle = (float)Math.Atan2(y, x);
  161. // so now we know where we WANT to be facing, and where we ARE facing...
  162. // if we weren't constrained by turnSpeed, this would be easy: we'd just
  163. // return desiredAngle.
  164. // instead, we have to calculate how much we WANT to turn, and then make
  165. // sure that's not more than turnSpeed.
  166. // first, figure out how much we want to turn, using WrapAngle to get our
  167. // result from -Pi to Pi ( -180 degrees to 180 degrees )
  168. float difference = WrapAngle(desiredAngle - currentAngle);
  169. // clamp that between -turnSpeed and turnSpeed.
  170. difference = MathHelper.Clamp(difference, -turnSpeed, turnSpeed);
  171. // so, the closest we can get to our target is currentAngle + difference.
  172. // return that, using WrapAngle again.
  173. return WrapAngle(currentAngle + difference);
  174. }
  175. /// <summary>
  176. /// Returns the angle expressed in radians between -Pi and Pi.
  177. /// </summary>
  178. private static float WrapAngle(float radians)
  179. {
  180. while (radians < -MathHelper.Pi)
  181. {
  182. radians += MathHelper.TwoPi;
  183. }
  184. while (radians > MathHelper.Pi)
  185. {
  186. radians -= MathHelper.TwoPi;
  187. }
  188. return radians;
  189. }
  190. /// <summary>
  191. /// This is called when the game should draw itself.
  192. /// </summary>
  193. protected override void Draw(GameTime gameTime)
  194. {
  195. GraphicsDevice device = graphics.GraphicsDevice;
  196. device.Clear(Color.Black);
  197. // draw the cat.
  198. spriteBatch.Begin();
  199. spriteBatch.Draw(catTexture, catPosition, null, Color.White,
  200. 0.0f, catOrigin, 1.0f, SpriteEffects.None, 0.0f);
  201. spriteBatch.End();
  202. // Start sprite batch with additive blending, and draw the spotlight.
  203. // Additive blending works very well for effects like lights and fire.
  204. spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.Additive);
  205. spriteBatch.Draw(spotlightTexture, spotlightPosition, null, Color.White,
  206. spotlightAngle, spotlightOrigin, 1.0f, SpriteEffects.None, 0.0f);
  207. spriteBatch.End();
  208. base.Draw(gameTime);
  209. }
  210. #endregion
  211. #region Handle Input
  212. /// <summary>
  213. /// Handles input for quitting the game.
  214. /// </summary>
  215. void HandleInput()
  216. {
  217. #if WINDOWS_PHONE
  218. KeyboardState currentKeyboardState = new KeyboardState();
  219. #else
  220. KeyboardState currentKeyboardState = Keyboard.GetState();
  221. #endif
  222. GamePadState currentGamePadState = GamePad.GetState(PlayerIndex.One);
  223. MouseState currentMouseState = Mouse.GetState();
  224. // Check for exit.
  225. if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
  226. currentGamePadState.Buttons.Back == ButtonState.Pressed)
  227. {
  228. Exit();
  229. }
  230. // check to see if the user wants to move the cat. we'll create a vector
  231. // called catMovement, which will store the sum of all the user's inputs.
  232. Vector2 catMovement = currentGamePadState.ThumbSticks.Left;
  233. // flip y: on the thumbsticks, down is -1, but on the screen, down is bigger
  234. // numbers.
  235. catMovement.Y *= -1;
  236. if (currentKeyboardState.IsKeyDown(Keys.Left) ||
  237. currentGamePadState.DPad.Left == ButtonState.Pressed)
  238. {
  239. catMovement.X -= 1.0f;
  240. }
  241. if (currentKeyboardState.IsKeyDown(Keys.Right) ||
  242. currentGamePadState.DPad.Right == ButtonState.Pressed)
  243. {
  244. catMovement.X += 1.0f;
  245. }
  246. if (currentKeyboardState.IsKeyDown(Keys.Up) ||
  247. currentGamePadState.DPad.Up == ButtonState.Pressed)
  248. {
  249. catMovement.Y -= 1.0f;
  250. }
  251. if (currentKeyboardState.IsKeyDown(Keys.Down) ||
  252. currentGamePadState.DPad.Down == ButtonState.Pressed)
  253. {
  254. catMovement.Y += 1.0f;
  255. }
  256. //Move toward the touch point. We slow down the cat when it gets within a distance of CatSpeed to the touch point.
  257. float smoothStop = 1;
  258. Vector2 mousePosition = new Vector2(currentMouseState.X, currentMouseState.Y);
  259. if (currentMouseState.LeftButton == ButtonState.Pressed && mousePosition != catPosition)
  260. {
  261. catMovement = mousePosition - catPosition;
  262. float delta = CatSpeed - MathHelper.Clamp(catMovement.Length(), 0, CatSpeed);
  263. smoothStop = 1 - delta / CatSpeed;
  264. }
  265. // normalize the user's input, so the cat can never be going faster than
  266. // CatSpeed.
  267. if (catMovement != Vector2.Zero)
  268. {
  269. catMovement.Normalize();
  270. }
  271. catPosition += catMovement * CatSpeed * smoothStop;
  272. }
  273. #endregion
  274. }
  275. #region Entry Point
  276. #if WINDOWS || XBOX
  277. /// <summary>
  278. /// The main entry point for the application.
  279. /// </summary>
  280. static class Program
  281. {
  282. static void Main()
  283. {
  284. using (AimingGame game = new AimingGame())
  285. {
  286. game.Run();
  287. }
  288. }
  289. }
  290. #endif
  291. #endregion
  292. }