2
0

TiledSprites.cs 18 KB

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