| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573 |
- //-----------------------------------------------------------------------------
- // TiledSprites.cs
- //
- // Microsoft XNA Community Game Platform
- // Copyright (C) Microsoft Corporation. All rights reserved.
- //-----------------------------------------------------------------------------
- using System;
- using System.Collections.Generic;
- using Microsoft.Xna.Framework;
- using Microsoft.Xna.Framework.Audio;
- using Microsoft.Xna.Framework.Graphics;
- using Microsoft.Xna.Framework.Input;
- using Microsoft.Xna.Framework.Content;
- using Microsoft.Xna.Framework.Media;
- namespace TiledSprites
- {
- /// <summary>
- /// This sample showcases a variety of common sprite operations
- /// using SpriteBatch
- /// </summary>
- public class TiledSpritesGame : Game
- {
- /// <summary>
- /// Some tiles based on included media
- /// </summary>
- public enum TileName : int
- {
- Empty = 0,
- Base = 1,
- Detail1 = 2,
- Detail2 = 3,
- Detail3 = 4,
- Detail4 = 5,
- SoftDetail1 = 6,
- SoftDetail2 = 7,
- SoftDetail3 = 8,
- SoftDetail4 = 9,
- Rocks1 = 10,
- Rocks2 = 11,
- Rocks3 = 12,
- Rocks4 = 13,
- Clouds = 14
- }
- private const float MovementRate = 500f;
- private const float ZoomRate = 0.5f;
- private const float RotationRate = 1.5f;
- private const int numTiles = 200;
- private const float animationTime = 0.1f;
- private static readonly Vector2 animatedSpriteScale = new Vector2(.3f, .3f);
- //utility types
- GraphicsDeviceManager graphics;
- //input state storage
- KeyboardState lastKeyboardState = new KeyboardState();
- GamePadState lastGamePadState = new GamePadState ();
- GamePadState currentGamePadState = new GamePadState ();
- KeyboardState currentKeyboardState = new KeyboardState();
- private Random rand;
- //2D camera abstraction
- private Camera2D camera;
- private Vector2 screenCenter;
- private SpriteFont font;
- //tile information
- private SpriteSheet groundSheet;
- private SpriteSheet cloudSheet;
- private SpriteBatch spriteBatch;
- private TileGrid rockLayer;
- private TileGrid groundLayer;
- private TileGrid cloudLayer;
- private TileGrid detailLayer;
- //animated sprite
- private SpriteSheet animatedSpriteSheet;
- private AnimatedSprite animatedSprite;
- private Vector2 animatedSpritePosition;
- private float accumulator;
- public TiledSpritesGame()
- {
- graphics = new GraphicsDeviceManager(this);
- Content.RootDirectory = "Content";
- graphics.PreferredBackBufferWidth = 640;
- graphics.PreferredBackBufferHeight = 480;
-
- rand = new Random();
- }
- /// <summary>
- /// Load the graphics content.
- /// </summary>
- protected override void LoadContent()
- {
- //When the backbuffer resolution changes, this part of the
- //LoadContent calback is used to reset the screen center
- screenCenter = new Vector2(
- (float)graphics.GraphicsDevice.Viewport.Width / 2f,
- (float)graphics.GraphicsDevice.Viewport.Height / 2f);
- font = Content.Load<SpriteFont>("font");
- // Load XNBs in Release mode
- var groundTexture = Content.Load<Texture2D>("ground");
- var cloudTexture = Content.Load<Texture2D>("clouds");
- spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
- //set up the tile sheets with source rectangles
- //for each of the different sprites
- cloudSheet = new SpriteSheet(cloudTexture);
- cloudSheet.AddSourceSprite((int)TileName.Clouds,
- new Rectangle(0, 0, 1024, 1024));
- groundSheet = new SpriteSheet(groundTexture);
- groundSheet.AddSourceSprite((int)TileName.Base,
- new Rectangle(0, 0, 510, 510));
- groundSheet.AddSourceSprite((int)TileName.Detail1,
- new Rectangle(514, 0, 255, 255));
- groundSheet.AddSourceSprite((int)TileName.Detail2,
- new Rectangle(769, 0, 255, 255));
- groundSheet.AddSourceSprite((int)TileName.Detail3,
- new Rectangle(514, 256, 255, 255));
- groundSheet.AddSourceSprite((int)TileName.Detail4,
- new Rectangle(769, 256, 255, 255));
- groundSheet.AddSourceSprite((int)TileName.SoftDetail1,
- new Rectangle(514, 514, 255, 255));
- groundSheet.AddSourceSprite((int)TileName.SoftDetail2,
- new Rectangle(769, 514, 255, 255));
- groundSheet.AddSourceSprite((int)TileName.SoftDetail3,
- new Rectangle(514, 769, 255, 255));
- groundSheet.AddSourceSprite((int)TileName.SoftDetail4,
- new Rectangle(769, 769, 255, 255));
- groundSheet.AddSourceSprite((int)TileName.Rocks1,
- new Rectangle(0, 514, 255, 255));
- groundSheet.AddSourceSprite((int)TileName.Rocks2,
- new Rectangle(256, 514, 255, 255));
- groundSheet.AddSourceSprite((int)TileName.Rocks3,
- new Rectangle(0, 769, 255, 255));
- groundSheet.AddSourceSprite((int)TileName.Rocks4,
- new Rectangle(256, 769, 255, 255));
- //Create the ground layer tile
- groundLayer = new TileGrid(510, 510, numTiles, numTiles,
- Vector2.Zero, groundSheet, graphics);
- //calculate the number of detial tiles, which are
- //half the size of the base tiles, so there are
- //twice as many (minus one since they are being offset)
- int numDetailTiles = (numTiles * 2 - 1);
- //add an offset to break up the pattern
- detailLayer = new TileGrid(255, 255, numDetailTiles, numDetailTiles,
- new Vector2(127, 127), groundSheet, graphics);
- rockLayer = new TileGrid(255, 255, numDetailTiles, numDetailTiles,
- new Vector2(0, 0), groundSheet, graphics);
- int numCloudTiles = numTiles / 6 + 1;
- cloudLayer = new TileGrid(1024, 1024, numCloudTiles, numCloudTiles,
- Vector2.Zero, cloudSheet, graphics);
- //These loops fill the datas with some appropriate data.
- //The clouds and ground clutter have been randomized.
- for (int i = 0; i < numTiles; i++)
- {
- for (int j = 0; j < numTiles; j++)
- {
- groundLayer.SetTile(i, j, 1);
- }
- }
- for (int i = 0; i < numDetailTiles; i++)
- {
- for (int j = 0; j < numDetailTiles; j++)
- {
- switch (rand.Next(20))
- {
- case 0:
- detailLayer.SetTile(i, j, (int)TileName.Detail1);
- break;
- case 1:
- detailLayer.SetTile(i, j, (int)TileName.Detail2);
- break;
- case 2:
- detailLayer.SetTile(i, j, (int)TileName.Detail3);
- break;
- case 3:
- detailLayer.SetTile(i, j, (int)TileName.Detail4);
- break;
- case 4:
- case 5:
- detailLayer.SetTile(i, j, (int)TileName.SoftDetail1);
- break;
- case 6:
- case 7:
- detailLayer.SetTile(i, j, (int)TileName.SoftDetail2);
- break;
- case 8:
- case 9:
- detailLayer.SetTile(i, j, (int)TileName.SoftDetail3);
- break;
- case 10:
- case 11:
- detailLayer.SetTile(i, j, (int)TileName.SoftDetail4);
- break;
- }
- }
- }
- for (int i = 0; i < numDetailTiles; i++)
- {
- for (int j = 0; j < numDetailTiles; j++)
- {
- switch (rand.Next(25))
- {
- case 0:
- rockLayer.SetTile(i, j, (int)TileName.Rocks1);
- break;
- case 1:
- rockLayer.SetTile(i, j, (int)TileName.Rocks2);
- break;
- case 2:
- rockLayer.SetTile(i, j, (int)TileName.Rocks3);
- break;
- case 3:
- case 4:
- rockLayer.SetTile(i, j, (int)TileName.Rocks4);
- break;
- }
- }
- }
- for (int i = 0; i < numCloudTiles; i++)
- {
- for (int j = 0; j < numCloudTiles; j++)
- {
- cloudLayer.SetTile(i, j, (int)TileName.Clouds);
- }
- }
- // Set up AnimatedSprite
- var ballTexture = Content.Load<Texture2D>("ball");
- animatedSpriteSheet = new SpriteSheet(ballTexture);
- animatedSprite = new AnimatedSprite(animatedSpriteSheet, 254, 254, 1,
- 4, 4, new Point(1, 0), 15);
- // Set Up a 2D Camera
- camera = new Camera2D();
- ResetToInitialPositions();
- //assuming a resolution change, need to update the sprite's "position"
- animatedSprite.Origin = camera.Position - animatedSpritePosition;
- animatedSprite.Position = screenCenter;
- }
- /// <summary>
- /// Reset the camera to the center of the tile grid
- /// and reset the position of the animted sprite
- /// </summary>
- private void ResetToInitialPositions()
- {
- //set up the 2D camera
- //set the initial position to the center of the
- //tile field
- camera.Position = new Vector2(numTiles * 255);
- camera.Rotation = 0f;
- camera.Zoom = 1f;
- camera.MoveUsingScreenAxis = true;
- //the animated sprite has no concept of a camera, so
- //making the sprite camera relative is the job
- //of the game program
- animatedSpritePosition = camera.Position;
- animatedSprite.ScaleValue = animatedSpriteScale;
- animatedSprite.Position = screenCenter;
- CameraChanged();
- }
- /// <summary>
- /// Update the game world.
- /// </summary>
- /// <param name="gameTime">Provides a snapshot of timing values.</param>
- protected override void Update(GameTime gameTime)
- {
- HandleInput();
- //Set the camera's state to Unchanged for this frame
- //this will save us from having to update visibility if the camera
- //does not move
- camera.ResetChanged();
- //Call sample-specific input handling function
- HandleKeyboardInput((float)gameTime.ElapsedGameTime.TotalSeconds);
- HandleGamePadInput ((float)gameTime.ElapsedGameTime.TotalSeconds);
- if (camera.IsChanged)
- {
- CameraChanged();
- }
- //thottle the animation update speed to the frame animation
- //time
- accumulator += (float)gameTime.ElapsedGameTime.TotalSeconds;
- if (accumulator > animationTime)
- {
- animatedSprite.IncrementAnimationFrame();
- accumulator -= animationTime;
- }
- base.Update(gameTime);
- }
- /// <summary>
- /// Handle Game Pad input during Update
- /// </summary>
- public void HandleGamePadInput (float elapsed)
- {
- if (currentGamePadState.IsConnected) {
- //the left thumbstick moves the animated sprite around the world
- if ((Math.Abs (currentGamePadState.ThumbSticks.Left.X) > .1f) ||
- (Math.Abs (currentGamePadState.ThumbSticks.Left.Y) > .1f)) {
- //Sprite movement is being updated relative to the camera
- //rotation
- animatedSpritePosition.X += (float)Math.Cos (-camera.Rotation) *
- currentGamePadState.ThumbSticks.Left.X * elapsed * MovementRate;
- animatedSpritePosition.Y += (float)Math.Sin (-camera.Rotation) *
- currentGamePadState.ThumbSticks.Left.X * elapsed * MovementRate;
- animatedSpritePosition.Y -= (float)Math.Cos (camera.Rotation) *
- currentGamePadState.ThumbSticks.Left.Y * elapsed * MovementRate;
- animatedSpritePosition.X -= (float)Math.Sin (camera.Rotation) *
- currentGamePadState.ThumbSticks.Left.Y * elapsed * MovementRate;
- //since the sprite position has changed, the Origin must be updated
- //on the animated sprite object
- animatedSprite.Origin = (camera.Position - animatedSpritePosition)
- / animatedSpriteScale.X;
- }
- //right thumbstick controls the camera position
- if ((Math.Abs (currentGamePadState.ThumbSticks.Right.X) > .1f) ||
- (Math.Abs (currentGamePadState.ThumbSticks.Right.Y) > .1f)) {
- float dX = currentGamePadState.ThumbSticks.Right.X * elapsed *
- MovementRate;
- float dY = currentGamePadState.ThumbSticks.Right.Y * elapsed *
- MovementRate;
- camera.MoveRight (ref dX);
- camera.MoveUp (ref dY);
- }
- //the triggers control rotation
- if ((Math.Abs (currentGamePadState.Triggers.Left) > .1f) ||
- (Math.Abs (currentGamePadState.Triggers.Right) > .1f)) {
- float dX = currentGamePadState.Triggers.Left *
- elapsed * RotationRate;
- dX += -currentGamePadState.Triggers.Right *
- elapsed * RotationRate;
- camera.Rotation += dX;
- }
- //the A and B buttons control zoom
- if ((currentGamePadState.Buttons.A == ButtonState.Pressed) ||
- (currentGamePadState.Buttons.B == ButtonState.Pressed)) {
- float delta = elapsed * ZoomRate;
- if (currentGamePadState.Buttons.B == ButtonState.Pressed)
- delta = -delta;
- camera.Zoom += delta;
- if (camera.Zoom < .5f)
- camera.Zoom = .5f;
- if (camera.Zoom > 2f)
- camera.Zoom = 2f;
- }
- if ((currentGamePadState.Buttons.RightStick == ButtonState.Pressed) &&
- (lastGamePadState.Buttons.RightStick == ButtonState.Released)) {
- ResetToInitialPositions ();
- }
- }
- }
- /// <summary>
- /// Handle Keyboard input during Update
- /// </summary>
- public void HandleKeyboardInput(float elapsed)
- {
- //check for camera movement
- float dX = ReadKeyboardAxis(currentKeyboardState, Keys.Left, Keys.Right) *
- elapsed * MovementRate;
- float dY = ReadKeyboardAxis(currentKeyboardState, Keys.Down, Keys.Up) *
- elapsed * MovementRate;
- camera.MoveRight(ref dX);
- camera.MoveUp(ref dY);
- //check for animted sprite movement
- animatedSpritePosition.X += (float)Math.Cos(-camera.Rotation) *
- ReadKeyboardAxis(currentKeyboardState, Keys.A, Keys.D) *
- elapsed * MovementRate;
- animatedSpritePosition.Y += (float)Math.Sin(-camera.Rotation) *
- ReadKeyboardAxis(currentKeyboardState, Keys.A, Keys.D) *
- elapsed * MovementRate;
- animatedSpritePosition.X -= (float)Math.Sin(camera.Rotation) *
- ReadKeyboardAxis(currentKeyboardState, Keys.S, Keys.W) *
- elapsed * MovementRate;
- animatedSpritePosition.Y -= (float)Math.Cos(camera.Rotation) *
- ReadKeyboardAxis(currentKeyboardState, Keys.S, Keys.W) *
- elapsed * MovementRate;
- //since the sprite position has changed, the Origin must be updated
- //on the animated sprite object
- animatedSprite.Origin = (camera.Position - animatedSpritePosition)
- / animatedSpriteScale.X;
- //check for camera rotation
- dX = ReadKeyboardAxis(currentKeyboardState, Keys.E, Keys.Q) *
- elapsed * RotationRate;
- camera.Rotation += dX;
- //check for camera zoom
- dX = ReadKeyboardAxis(currentKeyboardState, Keys.X, Keys.Z) *
- elapsed * ZoomRate;
- //limit the zoom
- camera.Zoom += dX;
- if (camera.Zoom < .5f)
- camera.Zoom = .5f;
- if (camera.Zoom > 2f)
- camera.Zoom = 2f;
- //check for camera reset
- if (currentKeyboardState.IsKeyDown(Keys.R) &&
- lastKeyboardState.IsKeyDown(Keys.R))
- {
- ResetToInitialPositions();
- }
- }
- /// <summary>
- /// This function is called when the camera's values have changed
- /// and is used to update the properties of the tiles and animated sprite
- /// </summary>
- public void CameraChanged()
- {
- //set rotation
- groundLayer.CameraRotation = detailLayer.CameraRotation =
- cloudLayer.CameraRotation = rockLayer.CameraRotation =
- animatedSprite.Rotation = camera.Rotation;
- //set zoom
- groundLayer.CameraZoom = detailLayer.CameraZoom =
- rockLayer.CameraZoom = camera.Zoom;
- animatedSprite.ScaleValue = animatedSpriteScale * camera.Zoom;
- cloudLayer.CameraZoom = camera.Zoom + 1.0f;
- //For an extra special effect, the camera zoom is figured into the cloud
- //alpha. The clouds will appear to fade out as camera zooms in.
- cloudLayer.Color = new Color(new Vector4(
- 1.0f, 1.0f, 1.0f, 2 / (2f * camera.Zoom + 1.0f)));
- //set position
- groundLayer.CameraPosition = camera.Position;
- detailLayer.CameraPosition = camera.Position;
- rockLayer.CameraPosition = camera.Position;
- //to acheive a paralax effect, scale down cloud movement
- cloudLayer.CameraPosition = camera.Position / 3.0f;
- //The animcated sprite's origin is set so that rotation
- //will occur around the camera center (accounting for scale)
- animatedSprite.Origin = (camera.Position - animatedSpritePosition)
- / animatedSpriteScale.X;
- //changes have been accounted for, reset the changed value so that this
- //function is not called unnecessarily
- camera.ResetChanged();
- }
- /// <summary>
- /// This is called when the game should draw itself.
- /// </summary>
- /// <param name="gameTime">Provides a snapshot of timing values.</param>
- protected override void Draw(GameTime gameTime)
- {
- //since we're drawing in order from back to front,
- //depth buffer is disabled
- // TODO graphics.GraphicsDevice.RenderState.DepthBufferEnable = false;
- graphics.GraphicsDevice.Clear(Color.MonoGameOrange);
- //draw the background layers
- groundLayer.Color = Color.LightGray;
- groundLayer.Draw(spriteBatch);
- detailLayer.Draw(spriteBatch);
- rockLayer.Draw(spriteBatch);
- animatedSprite.Draw(spriteBatch, Color.AntiqueWhite, BlendState.AlphaBlend);
- //draw the clouds
- cloudLayer.Draw(spriteBatch);
- spriteBatch.Begin();
- spriteBatch.DrawString(font, "Use the arrow keys to move the camera.", new Vector2(20, 20), Color.Black);
- spriteBatch.DrawString(font, "Use the WASD keys to move the animated sprite.", new Vector2(20, 40), Color.Black);
- spriteBatch.DrawString(font, "Use the Q and E keys to rotate the camera.", new Vector2(20, 60), Color.Black);
- spriteBatch.DrawString(font, "Use the Z and X keys to zoom in and out.", new Vector2(20, 80), Color.Black);
- spriteBatch.DrawString(font, "Press R to reset the camera.", new Vector2(20, 100), Color.Black);
- spriteBatch.DrawString(font, "Press Escape to exit.", new Vector2(20, 120), Color.Black);
- spriteBatch.DrawString(font, "Use the GamePad to control the camera and animated sprite.", new Vector2(20, 140), Color.Black);
- spriteBatch.DrawString(font, "Press Back on the GamePad to exit.", new Vector2(20, 160), Color.Black);
- spriteBatch.DrawString(font, "Camera Position: " + camera.Position.ToString(), new Vector2(20, 180), Color.Black);
- spriteBatch.DrawString(font, "Camera Rotation: " + camera.Rotation.ToString("F2"), new Vector2(20, 200), Color.Black);
- spriteBatch.DrawString(font, "Camera Zoom: " + camera.Zoom.ToString("F2"), new Vector2(20, 220), Color.Black);
- spriteBatch.End();
- base.Draw(gameTime);
- }
- /// <summary>
- /// Handles input for quitting the game.
- /// </summary>
- private void HandleInput()
- {
- lastKeyboardState = currentKeyboardState;
- lastGamePadState = currentGamePadState;
- currentKeyboardState = Keyboard.GetState();
- currentGamePadState = GamePad.GetState (PlayerIndex.One);
- // Check for exit.
- if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
- currentGamePadState.Buttons.Back == ButtonState.Pressed)
- {
- Exit();
- }
- }
- /// <summary>
- /// Uses a pair of keys to simulate a positive or negative axis input.
- /// </summary>
- private static float ReadKeyboardAxis(KeyboardState keyState, Keys downKey,
- Keys upKey)
- {
- float value = 0;
- if (keyState.IsKeyDown(downKey))
- value -= 1.0f;
- if (keyState.IsKeyDown(upKey))
- value += 1.0f;
- return value;
- }
- }
- }
|