2
0

AimingGame.cs 14 KB

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