#region File Description //----------------------------------------------------------------------------- // TileGrid.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using System.Collections.Generic; #if IPHONE using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Storage; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Media; #else using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Storage; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Media; #endif #endregion namespace TiledSprites { /// /// EDUCATIONAL: Class used to align tiles to a regular grid. /// This represents a tiling "layer" in this sample /// public class TileGrid { #region Fields private int[][] grid; private GraphicsDeviceManager graphics; private SpriteSheet sheet; private int width; private int height; private int cellWidth; private int cellHeight; private Vector2 worldOffset; private bool visibilityChanged; private Rectangle visibleTiles; //drawing parameters private Vector2 cameraPostionValue; private float zoomValue; private Vector2 scaleValue; private float rotationValue; private Matrix rotationMatrix; private Vector2 displaySize; private Color layerColor = Color.White; #endregion #region Initialization public TileGrid (int tileWidth,int tileHeight,int numXTiles,int numYTiles, Vector2 offset,SpriteSheet tileSheet, GraphicsDeviceManager graphicsComponent) { if (graphicsComponent == null) { throw new ArgumentNullException ("graphicsComponent"); } graphics = graphicsComponent; sheet = tileSheet; width = numXTiles; height = numYTiles; cellWidth = tileWidth; cellHeight = tileHeight; worldOffset = offset; visibleTiles = new Rectangle (0, 0, width, height); grid = new int[width][]; for (int i = 0; i < width; i++) { grid [i] = new int[height]; for (int j = 0; j < height; j++) { grid [i] [j] = 0; } } scaleValue = Vector2.One; zoomValue = 1.0f; CameraPosition = Vector2.Zero; graphicsComponent.DeviceReset += delegate(object sender, EventArgs e) { displaySize.X = 320; // TODO graphics.GraphicsDevice.PresentationParameters.BackBufferWidth; displaySize.Y = 480; // TODO graphics.GraphicsDevice.PresentationParameters.BackBufferHeight; visibilityChanged = true; }; // graphicsComponent.DeviceReset += // new EventHandler (OnGraphicsComponentDeviceReset); // // OnGraphicsComponentDeviceReset (this, new EventArgs ()); // } // void OnGraphicsComponentDeviceReset (object sender, EventArgs e) // { // displaySize.X = 320; // // TODO graphics.GraphicsDevice.PresentationParameters.BackBufferWidth; // // displaySize.Y = 480; // // TODO graphics.GraphicsDevice.PresentationParameters.BackBufferHeight; // // visibilityChanged = true; // } } #endregion #region Public Accessors public Vector2 CameraPosition { set { cameraPostionValue = value; visibilityChanged = true; } get { return cameraPostionValue; } } public float CameraRotation { set { rotationValue = value; rotationMatrix = Matrix.CreateRotationZ (rotationValue); visibilityChanged = true; } get { return rotationValue; } } public float CameraZoom { set { zoomValue = value; visibilityChanged = true; } get { return zoomValue; } } public Color Color { set { layerColor = value; } get { return layerColor ; } } public Vector2 TileScale { set { scaleValue = value; visibilityChanged = true; } get { return scaleValue; } } public Vector2 Position { set { worldOffset = value; visibilityChanged = true; } get { return worldOffset; } } #endregion #region Methods public void SetTile (int xIndex, int yIndex, int tile) { grid [xIndex] [yIndex] = tile; } /// /// This function determines which tiles are visible on the screen, /// given the current camera position, rotation, zoom, and tile scale /// private void DetermineVisibility () { //create the view rectangle Vector2 upperLeft = Vector2.Zero; Vector2 upperRight = Vector2.Zero; Vector2 lowerLeft = Vector2.Zero; Vector2 lowerRight = Vector2.Zero; lowerRight.X = ((displaySize.X / 2) / zoomValue); lowerRight.Y = ((displaySize.Y / 2) / zoomValue); upperRight.X = lowerRight.X; upperRight.Y = -lowerRight.Y; lowerLeft.X = -lowerRight.X; lowerLeft.Y = lowerRight.Y; upperLeft.X = -lowerRight.X; upperLeft.Y = -lowerRight.Y; //rotate the view rectangle appropriately Vector2.Transform (ref upperLeft, ref rotationMatrix, out upperLeft); Vector2.Transform (ref lowerRight, ref rotationMatrix, out lowerRight); Vector2.Transform (ref upperRight, ref rotationMatrix, out upperRight); Vector2.Transform (ref lowerLeft, ref rotationMatrix, out lowerLeft); lowerLeft += (cameraPostionValue); lowerRight += (cameraPostionValue); upperRight += (cameraPostionValue); upperLeft += (cameraPostionValue); //the idea here is to figure out the smallest square //(in tile space) that contains tiles //the offset is calculated before scaling float top = MathHelper.Min ( MathHelper.Min (upperLeft.Y, lowerRight.Y), MathHelper.Min (upperRight.Y, lowerLeft.Y)) - worldOffset.Y; float bottom = MathHelper.Max ( MathHelper.Max (upperLeft.Y, lowerRight.Y), MathHelper.Max (upperRight.Y, lowerLeft.Y)) - worldOffset.Y; float right = MathHelper.Max ( MathHelper.Max (upperLeft.X, lowerRight.X), MathHelper.Max (upperRight.X, lowerLeft.X)) - worldOffset.X; float left = MathHelper.Min ( MathHelper.Min (upperLeft.X, lowerRight.X), MathHelper.Min (upperRight.X, lowerLeft.X)) - worldOffset.X; //now figure out where we are in the tile sheet float scaledTileWidth = (float)cellWidth * scaleValue.X; float scaledTileHeight = (float)cellHeight * scaleValue.Y; //get the visible tiles visibleTiles.X = (int)(left / (scaledTileWidth)); visibleTiles.Y = (int)(top / (scaledTileWidth)); //get the number of visible tiles visibleTiles.Height = (int)((bottom) / (scaledTileHeight)) - visibleTiles.Y + 1; visibleTiles.Width = (int)((right) / (scaledTileWidth)) - visibleTiles.X + 1; //clamp the "upper left" values to 0 if (visibleTiles.X < 0) visibleTiles.X = 0; if (visibleTiles.X > (width - 1)) visibleTiles.X = width; if (visibleTiles.Y < 0) visibleTiles.Y = 0; if (visibleTiles.Y > (height - 1)) visibleTiles.Y = height; //clamp the "lower right" values to the gameboard size if (visibleTiles.Right > (width - 1)) visibleTiles.Width = (width - visibleTiles.X); if (visibleTiles.Right < 0) visibleTiles.Width = 0; if (visibleTiles.Bottom > (height - 1)) visibleTiles.Height = (height - visibleTiles.Y); if (visibleTiles.Bottom < 0) visibleTiles.Height = 0; visibilityChanged = false; } public void Draw (SpriteBatch batch) { if (visibilityChanged) DetermineVisibility (); float scaledTileWidth = (float)cellWidth * scaleValue.X; float scaledTileHeight = (float)cellHeight * scaleValue.Y; Vector2 screenCenter = new Vector2 ( (displaySize.X / 2), (displaySize.Y / 2)); //begin a batch of sprites to be drawn all at once batch.Begin (SpriteSortMode.Deferred, BlendState.AlphaBlend); Rectangle sourceRect = new Rectangle (); Vector2 scale = Vector2.One; for (int x = visibleTiles.Left; x < visibleTiles.Right; x++) { for (int y = visibleTiles.Top; y < visibleTiles.Bottom; y++) { if (grid [x] [y] != 0) { //Get the tile's position from the grid //in this section we're using reference methods //for the high frequency math functions Vector2 position = Vector2.Zero; position.X = (float)x * scaledTileWidth; position.Y = (float)y * scaledTileHeight; //offset the positions by the word position of the tile grid //this is the actual position of the tile in world coordinates Vector2.Add (ref position, ref worldOffset, out position); //Now, we get the camera position relative to the tile's position Vector2.Subtract (ref cameraPostionValue, ref position, out position); //get the tile's final size (note that scaling is done after //determining the position) Vector2.Multiply (ref scaleValue, zoomValue, out scale); //get the source rectnagle that defines the tile sheet.GetRectangle (ref grid [x] [y], out sourceRect); //Draw the tile. Notice that position is used as the offset and //the screen center is used as a position. This is required to //enable scaling and rotation about the center of the screen by //drawing tiles as an offset from the center coordinate batch.Draw (sheet.Texture, screenCenter, sourceRect, layerColor, rotationValue, position, scale, SpriteEffects.None, 0.0f); } } } batch.End (); } #endregion } }