# Tiled Sprites Sample

This sample shows how to manage data for tiling, animation, visibility determination, and virtualization of a 2D camera.

# Sample Overview

A common technique for getting the most mileage out of sprites is to use grids of sprite "tiles" to reduce the amount of texture data needed at any one time to draw a scene. Tiles are individual sprites that are repeated to create a composite 2D image. Often, multiple source tiles are stored in a single texture, sometimes called a sprite sheet.

Although a game level might have tens of thousands of tiles in the game world, only those that are visible should be drawn. Otherwise, you see a severe performance hit for submitting more draw calls than are needed to render the scene.

To simplify interactions with the tiles in the game world, the sample provides a 2D Camera implementation. You will find a camera abstraction useful, particularly when rotating or zooming the world. This enables you to rapidly translate screen space to world space.

Sprite animation is a similar data management task to tiling, since several source sprites make up multiple frames of an animation. This sample shows a way to quickly generate a series of animation frames from a sprite sheet and step through the animation.

## Minimum Shader Profile

Vertex Shader Model 1.1
Pixel Shader Model 1.1

## Sample Controls

This sample uses the following keyboard and gamepad controls.

Action | Keyboard Control | Gamepad Control
--- | --- | ---
Move the background. | UP ARROW, DOWN ARROW, LEFT ARROW, and RIGHT ARROW | Right thumb stick
Move the ball. | W, A, S, and D | Left thumb stick
Rotate the background. | Q and E | Triggers
Zoom. | Z and X | A and B
Reset. | R | Right thumb stick press
Exit. | ESC or ALT+F4 | BACK

# How the Sample Works

TiledSprites creates a large grid of base, detail, rock, and cloud tiles and populates them with random data as appropriate to create a natural-looking overhead scene. It also loads an animation of a bouncing ball and plays it in a loop.

You control the camera by using the right analog pad, along with the A and B buttons for zoom and the triggers for rotation. Pressing the Y button resets the view. The left stick controls the location of the bouncing ball animation in world coordinates. The sample also support keyboard controls.

Input handling is fairly involved in this sample, since each element of the scene must be updated when the camera position changes. For TileGrid especially, update the camera properties only as necessary, to avoid extra calls to DetermineVisibility. With that in mind, the Camera class has the property IsChanged to indicate whether the camera has changed during the frame and visibility needs to be updated.

The cloud tile layer does not move or scale at the same rate as the rest of the elements in the scene, showing a subtle parallax effect. Also, as the camera zoom is increased, the alpha value (opacity) of the cloud layer is reduced, making them more transparent.

## World Coordinates vs. Screen Coordinates

The chief complexity in a tile-based game is how the world coordinates relate to the screen coordinates. SpriteBatch uses screen coordinates to draw sprites directly to the screen. However, all of the tiles in the game world are stored in world coordinates. When you must consider rotation and scaling, the problem becomes more complex. This sample shows one way of translating coordinates efficiently.

## SpriteSheet

SpriteSheet is a simple container class that holds a Texture2D and a Dictionary. For performance reasons, the keys are integers. The values in the dictionary are the rectangles that define the source for a given sprite.

During initialization in the TiledSpritesSample.LoadContent method, sprite sheets are created for the clouds, the ground tiles, and the animated ball. The sprite sheets are then updated with sprite source information specific to the texture being used. In the case of the animated ball, the AnimatedSprite constructor generates the sprite source entries since they correspond to identically sized frames of animation.

## Camera2D

The Camera2D class is a simple abstraction for a resolution-independent 2D camera. This camera has no information about the screen size, only its center point, zoom, and rotation values. This means that if the back buffer is resized, the camera will not automatically change its scale to compensate.

The camera has the ability to pan in world coordinates or screen coordinates. It uses the rotation value to pan in the screen-space direction.

## TileGrid

TileGrid is the most complex component of this sample. It defines a grid of uniformly sized tiles that you can rotate, scale, and reposition on the screen. The tiles are placed in world coordinates, and you can offset and scale the entire grid. It also contains camera properties to inform TileGrid how to draw the tiles to the screen.

The basic TileGrid contains a 2D array of integers that correspond to the source tile in each location. These integers are used as the key to a sprite sheet to get the source tile used when the grid is drawn.

Any time any of the position, scale, or rotation properties are changed, visibility must updated so that only the tiles visible on the screen are drawn. Since the tiles are arranged in a regular grid, it's fairly inexpensive to determine which tiles are visible on the screen. The DetermineVisibility function calculates a rectangular snapshot of all the tiles that are visible on the screen. The ranges are then stored.

When Draw is called to render the TileGrid, the method first determines whether visibility needs to be updated. Then it uses SpriteBatch to draw all of the tiles in the visible rectangle. The order of operations when calculating the SpriteBatch parameters is essential, because SpriteBatch has its own order of operations when transforming sprites into screen space.

## AnimatedSprite

The animated sprite has no concept of a camera, so it must be manually positioned into screen space by some calculations in SpriteOperations.cs. The pattern should look familiar: it follows the same order of operations used by TileGrid when drawing individual tiles.

# Extending the Sample

Although the TileGrid and AnimatedSprite implementations have been kept as discreet parts, there is no reason you cannot create animated tiles. A great extension to this sample would be to implement a TileGrid that supports animation. Another interesting challenge would be to create a tiling implementation that allows free placement of tiles, rather than in a rigid grid.