TiledSprites.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. //-----------------------------------------------------------------------------
  2. // TiledSprites.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using System.Collections.Generic;
  9. using Microsoft.Xna.Framework;
  10. using Microsoft.Xna.Framework.Audio;
  11. using Microsoft.Xna.Framework.Graphics;
  12. using Microsoft.Xna.Framework.Input;
  13. using Microsoft.Xna.Framework.Content;
  14. using Microsoft.Xna.Framework.Media;
  15. namespace TiledSprites
  16. {
  17. /// <summary>
  18. /// This sample showcases a variety of common sprite operations
  19. /// using SpriteBatch
  20. /// </summary>
  21. public class TiledSpritesGame : Game
  22. {
  23. /// <summary>
  24. /// Some tiles based on included media
  25. /// </summary>
  26. public enum TileName : int
  27. {
  28. Empty = 0,
  29. Base = 1,
  30. Detail1 = 2,
  31. Detail2 = 3,
  32. Detail3 = 4,
  33. Detail4 = 5,
  34. SoftDetail1 = 6,
  35. SoftDetail2 = 7,
  36. SoftDetail3 = 8,
  37. SoftDetail4 = 9,
  38. Rocks1 = 10,
  39. Rocks2 = 11,
  40. Rocks3 = 12,
  41. Rocks4 = 13,
  42. Clouds = 14
  43. }
  44. private const float MovementRate = 500f;
  45. private const float ZoomRate = 0.5f;
  46. private const float RotationRate = 1.5f;
  47. private const int numTiles = 200;
  48. private const float animationTime = 0.1f;
  49. private static readonly Vector2 animatedSpriteScale = new Vector2(.3f, .3f);
  50. //utility types
  51. GraphicsDeviceManager graphics;
  52. //input state storage
  53. KeyboardState lastKeyboardState = new KeyboardState();
  54. GamePadState lastGamePadState = new GamePadState ();
  55. GamePadState currentGamePadState = new GamePadState ();
  56. KeyboardState currentKeyboardState = new KeyboardState();
  57. private Random rand;
  58. //2D camera abstraction
  59. private Camera2D camera;
  60. private Vector2 screenCenter;
  61. private SpriteFont font;
  62. //tile information
  63. private SpriteSheet groundSheet;
  64. private SpriteSheet cloudSheet;
  65. private SpriteBatch spriteBatch;
  66. private TileGrid rockLayer;
  67. private TileGrid groundLayer;
  68. private TileGrid cloudLayer;
  69. private TileGrid detailLayer;
  70. //animated sprite
  71. private SpriteSheet animatedSpriteSheet;
  72. private AnimatedSprite animatedSprite;
  73. private Vector2 animatedSpritePosition;
  74. private float accumulator;
  75. public TiledSpritesGame()
  76. {
  77. graphics = new GraphicsDeviceManager(this);
  78. Content.RootDirectory = "Content";
  79. graphics.PreferredBackBufferWidth = 640;
  80. graphics.PreferredBackBufferHeight = 480;
  81. rand = new Random();
  82. }
  83. /// <summary>
  84. /// Load the graphics content.
  85. /// </summary>
  86. protected override void LoadContent()
  87. {
  88. //When the backbuffer resolution changes, this part of the
  89. //LoadContent calback is used to reset the screen center
  90. screenCenter = new Vector2(
  91. (float)graphics.GraphicsDevice.Viewport.Width / 2f,
  92. (float)graphics.GraphicsDevice.Viewport.Height / 2f);
  93. font = Content.Load<SpriteFont>("font");
  94. // Load XNBs in Release mode
  95. var groundTexture = Content.Load<Texture2D>("ground");
  96. var cloudTexture = Content.Load<Texture2D>("clouds");
  97. spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
  98. //set up the tile sheets with source rectangles
  99. //for each of the different sprites
  100. cloudSheet = new SpriteSheet(cloudTexture);
  101. cloudSheet.AddSourceSprite((int)TileName.Clouds,
  102. new Rectangle(0, 0, 1024, 1024));
  103. groundSheet = new SpriteSheet(groundTexture);
  104. groundSheet.AddSourceSprite((int)TileName.Base,
  105. new Rectangle(0, 0, 510, 510));
  106. groundSheet.AddSourceSprite((int)TileName.Detail1,
  107. new Rectangle(514, 0, 255, 255));
  108. groundSheet.AddSourceSprite((int)TileName.Detail2,
  109. new Rectangle(769, 0, 255, 255));
  110. groundSheet.AddSourceSprite((int)TileName.Detail3,
  111. new Rectangle(514, 256, 255, 255));
  112. groundSheet.AddSourceSprite((int)TileName.Detail4,
  113. new Rectangle(769, 256, 255, 255));
  114. groundSheet.AddSourceSprite((int)TileName.SoftDetail1,
  115. new Rectangle(514, 514, 255, 255));
  116. groundSheet.AddSourceSprite((int)TileName.SoftDetail2,
  117. new Rectangle(769, 514, 255, 255));
  118. groundSheet.AddSourceSprite((int)TileName.SoftDetail3,
  119. new Rectangle(514, 769, 255, 255));
  120. groundSheet.AddSourceSprite((int)TileName.SoftDetail4,
  121. new Rectangle(769, 769, 255, 255));
  122. groundSheet.AddSourceSprite((int)TileName.Rocks1,
  123. new Rectangle(0, 514, 255, 255));
  124. groundSheet.AddSourceSprite((int)TileName.Rocks2,
  125. new Rectangle(256, 514, 255, 255));
  126. groundSheet.AddSourceSprite((int)TileName.Rocks3,
  127. new Rectangle(0, 769, 255, 255));
  128. groundSheet.AddSourceSprite((int)TileName.Rocks4,
  129. new Rectangle(256, 769, 255, 255));
  130. //Create the ground layer tile
  131. groundLayer = new TileGrid(510, 510, numTiles, numTiles,
  132. Vector2.Zero, groundSheet, graphics);
  133. //calculate the number of detial tiles, which are
  134. //half the size of the base tiles, so there are
  135. //twice as many (minus one since they are being offset)
  136. int numDetailTiles = (numTiles * 2 - 1);
  137. //add an offset to break up the pattern
  138. detailLayer = new TileGrid(255, 255, numDetailTiles, numDetailTiles,
  139. new Vector2(127, 127), groundSheet, graphics);
  140. rockLayer = new TileGrid(255, 255, numDetailTiles, numDetailTiles,
  141. new Vector2(0, 0), groundSheet, graphics);
  142. int numCloudTiles = numTiles / 6 + 1;
  143. cloudLayer = new TileGrid(1024, 1024, numCloudTiles, numCloudTiles,
  144. Vector2.Zero, cloudSheet, graphics);
  145. //These loops fill the datas with some appropriate data.
  146. //The clouds and ground clutter have been randomized.
  147. for (int i = 0; i < numTiles; i++)
  148. {
  149. for (int j = 0; j < numTiles; j++)
  150. {
  151. groundLayer.SetTile(i, j, 1);
  152. }
  153. }
  154. for (int i = 0; i < numDetailTiles; i++)
  155. {
  156. for (int j = 0; j < numDetailTiles; j++)
  157. {
  158. switch (rand.Next(20))
  159. {
  160. case 0:
  161. detailLayer.SetTile(i, j, (int)TileName.Detail1);
  162. break;
  163. case 1:
  164. detailLayer.SetTile(i, j, (int)TileName.Detail2);
  165. break;
  166. case 2:
  167. detailLayer.SetTile(i, j, (int)TileName.Detail3);
  168. break;
  169. case 3:
  170. detailLayer.SetTile(i, j, (int)TileName.Detail4);
  171. break;
  172. case 4:
  173. case 5:
  174. detailLayer.SetTile(i, j, (int)TileName.SoftDetail1);
  175. break;
  176. case 6:
  177. case 7:
  178. detailLayer.SetTile(i, j, (int)TileName.SoftDetail2);
  179. break;
  180. case 8:
  181. case 9:
  182. detailLayer.SetTile(i, j, (int)TileName.SoftDetail3);
  183. break;
  184. case 10:
  185. case 11:
  186. detailLayer.SetTile(i, j, (int)TileName.SoftDetail4);
  187. break;
  188. }
  189. }
  190. }
  191. for (int i = 0; i < numDetailTiles; i++)
  192. {
  193. for (int j = 0; j < numDetailTiles; j++)
  194. {
  195. switch (rand.Next(25))
  196. {
  197. case 0:
  198. rockLayer.SetTile(i, j, (int)TileName.Rocks1);
  199. break;
  200. case 1:
  201. rockLayer.SetTile(i, j, (int)TileName.Rocks2);
  202. break;
  203. case 2:
  204. rockLayer.SetTile(i, j, (int)TileName.Rocks3);
  205. break;
  206. case 3:
  207. case 4:
  208. rockLayer.SetTile(i, j, (int)TileName.Rocks4);
  209. break;
  210. }
  211. }
  212. }
  213. for (int i = 0; i < numCloudTiles; i++)
  214. {
  215. for (int j = 0; j < numCloudTiles; j++)
  216. {
  217. cloudLayer.SetTile(i, j, (int)TileName.Clouds);
  218. }
  219. }
  220. // Set up AnimatedSprite
  221. var ballTexture = Content.Load<Texture2D>("ball");
  222. animatedSpriteSheet = new SpriteSheet(ballTexture);
  223. animatedSprite = new AnimatedSprite(animatedSpriteSheet, 254, 254, 1,
  224. 4, 4, new Point(1, 0), 15);
  225. // Set Up a 2D Camera
  226. camera = new Camera2D();
  227. ResetToInitialPositions();
  228. //assuming a resolution change, need to update the sprite's "position"
  229. animatedSprite.Origin = camera.Position - animatedSpritePosition;
  230. animatedSprite.Position = screenCenter;
  231. }
  232. /// <summary>
  233. /// Reset the camera to the center of the tile grid
  234. /// and reset the position of the animted sprite
  235. /// </summary>
  236. private void ResetToInitialPositions()
  237. {
  238. //set up the 2D camera
  239. //set the initial position to the center of the
  240. //tile field
  241. camera.Position = new Vector2(numTiles * 255);
  242. camera.Rotation = 0f;
  243. camera.Zoom = 1f;
  244. camera.MoveUsingScreenAxis = true;
  245. //the animated sprite has no concept of a camera, so
  246. //making the sprite camera relative is the job
  247. //of the game program
  248. animatedSpritePosition = camera.Position;
  249. animatedSprite.ScaleValue = animatedSpriteScale;
  250. animatedSprite.Position = screenCenter;
  251. CameraChanged();
  252. }
  253. /// <summary>
  254. /// Update the game world.
  255. /// </summary>
  256. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  257. protected override void Update(GameTime gameTime)
  258. {
  259. HandleInput();
  260. //Set the camera's state to Unchanged for this frame
  261. //this will save us from having to update visibility if the camera
  262. //does not move
  263. camera.ResetChanged();
  264. //Call sample-specific input handling function
  265. HandleKeyboardInput((float)gameTime.ElapsedGameTime.TotalSeconds);
  266. HandleGamePadInput ((float)gameTime.ElapsedGameTime.TotalSeconds);
  267. if (camera.IsChanged)
  268. {
  269. CameraChanged();
  270. }
  271. //thottle the animation update speed to the frame animation
  272. //time
  273. accumulator += (float)gameTime.ElapsedGameTime.TotalSeconds;
  274. if (accumulator > animationTime)
  275. {
  276. animatedSprite.IncrementAnimationFrame();
  277. accumulator -= animationTime;
  278. }
  279. base.Update(gameTime);
  280. }
  281. /// <summary>
  282. /// Handle Game Pad input during Update
  283. /// </summary>
  284. public void HandleGamePadInput (float elapsed)
  285. {
  286. if (currentGamePadState.IsConnected) {
  287. //the left thumbstick moves the animated sprite around the world
  288. if ((Math.Abs (currentGamePadState.ThumbSticks.Left.X) > .1f) ||
  289. (Math.Abs (currentGamePadState.ThumbSticks.Left.Y) > .1f)) {
  290. //Sprite movement is being updated relative to the camera
  291. //rotation
  292. animatedSpritePosition.X += (float)Math.Cos (-camera.Rotation) *
  293. currentGamePadState.ThumbSticks.Left.X * elapsed * MovementRate;
  294. animatedSpritePosition.Y += (float)Math.Sin (-camera.Rotation) *
  295. currentGamePadState.ThumbSticks.Left.X * elapsed * MovementRate;
  296. animatedSpritePosition.Y -= (float)Math.Cos (camera.Rotation) *
  297. currentGamePadState.ThumbSticks.Left.Y * elapsed * MovementRate;
  298. animatedSpritePosition.X -= (float)Math.Sin (camera.Rotation) *
  299. currentGamePadState.ThumbSticks.Left.Y * elapsed * MovementRate;
  300. //since the sprite position has changed, the Origin must be updated
  301. //on the animated sprite object
  302. animatedSprite.Origin = (camera.Position - animatedSpritePosition)
  303. / animatedSpriteScale.X;
  304. }
  305. //right thumbstick controls the camera position
  306. if ((Math.Abs (currentGamePadState.ThumbSticks.Right.X) > .1f) ||
  307. (Math.Abs (currentGamePadState.ThumbSticks.Right.Y) > .1f)) {
  308. float dX = currentGamePadState.ThumbSticks.Right.X * elapsed *
  309. MovementRate;
  310. float dY = currentGamePadState.ThumbSticks.Right.Y * elapsed *
  311. MovementRate;
  312. camera.MoveRight (ref dX);
  313. camera.MoveUp (ref dY);
  314. }
  315. //the triggers control rotation
  316. if ((Math.Abs (currentGamePadState.Triggers.Left) > .1f) ||
  317. (Math.Abs (currentGamePadState.Triggers.Right) > .1f)) {
  318. float dX = currentGamePadState.Triggers.Left *
  319. elapsed * RotationRate;
  320. dX += -currentGamePadState.Triggers.Right *
  321. elapsed * RotationRate;
  322. camera.Rotation += dX;
  323. }
  324. //the A and B buttons control zoom
  325. if ((currentGamePadState.Buttons.A == ButtonState.Pressed) ||
  326. (currentGamePadState.Buttons.B == ButtonState.Pressed)) {
  327. float delta = elapsed * ZoomRate;
  328. if (currentGamePadState.Buttons.B == ButtonState.Pressed)
  329. delta = -delta;
  330. camera.Zoom += delta;
  331. if (camera.Zoom < .5f)
  332. camera.Zoom = .5f;
  333. if (camera.Zoom > 2f)
  334. camera.Zoom = 2f;
  335. }
  336. if ((currentGamePadState.Buttons.RightStick == ButtonState.Pressed) &&
  337. (lastGamePadState.Buttons.RightStick == ButtonState.Released)) {
  338. ResetToInitialPositions ();
  339. }
  340. }
  341. }
  342. /// <summary>
  343. /// Handle Keyboard input during Update
  344. /// </summary>
  345. public void HandleKeyboardInput(float elapsed)
  346. {
  347. //check for camera movement
  348. float dX = ReadKeyboardAxis(currentKeyboardState, Keys.Left, Keys.Right) *
  349. elapsed * MovementRate;
  350. float dY = ReadKeyboardAxis(currentKeyboardState, Keys.Down, Keys.Up) *
  351. elapsed * MovementRate;
  352. camera.MoveRight(ref dX);
  353. camera.MoveUp(ref dY);
  354. //check for animted sprite movement
  355. animatedSpritePosition.X += (float)Math.Cos(-camera.Rotation) *
  356. ReadKeyboardAxis(currentKeyboardState, Keys.A, Keys.D) *
  357. elapsed * MovementRate;
  358. animatedSpritePosition.Y += (float)Math.Sin(-camera.Rotation) *
  359. ReadKeyboardAxis(currentKeyboardState, Keys.A, Keys.D) *
  360. elapsed * MovementRate;
  361. animatedSpritePosition.X -= (float)Math.Sin(camera.Rotation) *
  362. ReadKeyboardAxis(currentKeyboardState, Keys.S, Keys.W) *
  363. elapsed * MovementRate;
  364. animatedSpritePosition.Y -= (float)Math.Cos(camera.Rotation) *
  365. ReadKeyboardAxis(currentKeyboardState, Keys.S, Keys.W) *
  366. elapsed * MovementRate;
  367. //since the sprite position has changed, the Origin must be updated
  368. //on the animated sprite object
  369. animatedSprite.Origin = (camera.Position - animatedSpritePosition)
  370. / animatedSpriteScale.X;
  371. //check for camera rotation
  372. dX = ReadKeyboardAxis(currentKeyboardState, Keys.E, Keys.Q) *
  373. elapsed * RotationRate;
  374. camera.Rotation += dX;
  375. //check for camera zoom
  376. dX = ReadKeyboardAxis(currentKeyboardState, Keys.X, Keys.Z) *
  377. elapsed * ZoomRate;
  378. //limit the zoom
  379. camera.Zoom += dX;
  380. if (camera.Zoom < .5f)
  381. camera.Zoom = .5f;
  382. if (camera.Zoom > 2f)
  383. camera.Zoom = 2f;
  384. //check for camera reset
  385. if (currentKeyboardState.IsKeyDown(Keys.R) &&
  386. lastKeyboardState.IsKeyDown(Keys.R))
  387. {
  388. ResetToInitialPositions();
  389. }
  390. }
  391. /// <summary>
  392. /// This function is called when the camera's values have changed
  393. /// and is used to update the properties of the tiles and animated sprite
  394. /// </summary>
  395. public void CameraChanged()
  396. {
  397. //set rotation
  398. groundLayer.CameraRotation = detailLayer.CameraRotation =
  399. cloudLayer.CameraRotation = rockLayer.CameraRotation =
  400. animatedSprite.Rotation = camera.Rotation;
  401. //set zoom
  402. groundLayer.CameraZoom = detailLayer.CameraZoom =
  403. rockLayer.CameraZoom = camera.Zoom;
  404. animatedSprite.ScaleValue = animatedSpriteScale * camera.Zoom;
  405. cloudLayer.CameraZoom = camera.Zoom + 1.0f;
  406. //For an extra special effect, the camera zoom is figured into the cloud
  407. //alpha. The clouds will appear to fade out as camera zooms in.
  408. cloudLayer.Color = new Color(new Vector4(
  409. 1.0f, 1.0f, 1.0f, 2 / (2f * camera.Zoom + 1.0f)));
  410. //set position
  411. groundLayer.CameraPosition = camera.Position;
  412. detailLayer.CameraPosition = camera.Position;
  413. rockLayer.CameraPosition = camera.Position;
  414. //to acheive a paralax effect, scale down cloud movement
  415. cloudLayer.CameraPosition = camera.Position / 3.0f;
  416. //The animcated sprite's origin is set so that rotation
  417. //will occur around the camera center (accounting for scale)
  418. animatedSprite.Origin = (camera.Position - animatedSpritePosition)
  419. / animatedSpriteScale.X;
  420. //changes have been accounted for, reset the changed value so that this
  421. //function is not called unnecessarily
  422. camera.ResetChanged();
  423. }
  424. /// <summary>
  425. /// This is called when the game should draw itself.
  426. /// </summary>
  427. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  428. protected override void Draw(GameTime gameTime)
  429. {
  430. //since we're drawing in order from back to front,
  431. //depth buffer is disabled
  432. // TODO graphics.GraphicsDevice.RenderState.DepthBufferEnable = false;
  433. graphics.GraphicsDevice.Clear(Color.MonoGameOrange);
  434. //draw the background layers
  435. groundLayer.Color = Color.LightGray;
  436. groundLayer.Draw(spriteBatch);
  437. detailLayer.Draw(spriteBatch);
  438. rockLayer.Draw(spriteBatch);
  439. animatedSprite.Draw(spriteBatch, Color.AntiqueWhite, BlendState.AlphaBlend);
  440. //draw the clouds
  441. cloudLayer.Draw(spriteBatch);
  442. spriteBatch.Begin();
  443. spriteBatch.DrawString(font, "Use the arrow keys to move the camera.", new Vector2(20, 20), Color.Black);
  444. spriteBatch.DrawString(font, "Use the WASD keys to move the animated sprite.", new Vector2(20, 40), Color.Black);
  445. spriteBatch.DrawString(font, "Use the Q and E keys to rotate the camera.", new Vector2(20, 60), Color.Black);
  446. spriteBatch.DrawString(font, "Use the Z and X keys to zoom in and out.", new Vector2(20, 80), Color.Black);
  447. spriteBatch.DrawString(font, "Press R to reset the camera.", new Vector2(20, 100), Color.Black);
  448. spriteBatch.DrawString(font, "Press Escape to exit.", new Vector2(20, 120), Color.Black);
  449. spriteBatch.DrawString(font, "Use the GamePad to control the camera and animated sprite.", new Vector2(20, 140), Color.Black);
  450. spriteBatch.DrawString(font, "Press Back on the GamePad to exit.", new Vector2(20, 160), Color.Black);
  451. spriteBatch.DrawString(font, "Camera Position: " + camera.Position.ToString(), new Vector2(20, 180), Color.Black);
  452. spriteBatch.DrawString(font, "Camera Rotation: " + camera.Rotation.ToString("F2"), new Vector2(20, 200), Color.Black);
  453. spriteBatch.DrawString(font, "Camera Zoom: " + camera.Zoom.ToString("F2"), new Vector2(20, 220), Color.Black);
  454. spriteBatch.End();
  455. base.Draw(gameTime);
  456. }
  457. /// <summary>
  458. /// Handles input for quitting the game.
  459. /// </summary>
  460. private void HandleInput()
  461. {
  462. lastKeyboardState = currentKeyboardState;
  463. lastGamePadState = currentGamePadState;
  464. currentKeyboardState = Keyboard.GetState();
  465. currentGamePadState = GamePad.GetState (PlayerIndex.One);
  466. // Check for exit.
  467. if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
  468. currentGamePadState.Buttons.Back == ButtonState.Pressed)
  469. {
  470. Exit();
  471. }
  472. }
  473. /// <summary>
  474. /// Uses a pair of keys to simulate a positive or negative axis input.
  475. /// </summary>
  476. private static float ReadKeyboardAxis(KeyboardState keyState, Keys downKey,
  477. Keys upKey)
  478. {
  479. float value = 0;
  480. if (keyState.IsKeyDown(downKey))
  481. value -= 1.0f;
  482. if (keyState.IsKeyDown(upKey))
  483. value += 1.0f;
  484. return value;
  485. }
  486. }
  487. }