The Top-Down Tiled Game Prototype
A Basic Tiled Top-Down Shooter
Loading...
Searching...
No Matches

1. Introduction

This game is similar to the Top Down Game with the addition of a tiled background that is read from a text map, and multiple levels with multiple guards. Fig. 1 shows one of the levels. Notice that we are re-using the player and guard sprites from The Top Down Game, but rendering them smaller.

Fig. 1: A screenshot of the Top Down Tiled Game.

The remainder of this page is divided into four sections. Section 2 lists the controls and their corresponding actions, Section 3 tells you how to build it, Section 4 gives you a list of actions to take in the game to see some of its important features, Section 5 gives a breakdown of the code, and Section 6 addresses the question "what next?".

2. Controls

Keyboard
Controller
Mouse
Action
F1
-
-
Help (this document)
F2
-
-
Toggle frame rate display
F3
-
-
Toggle show bounds
F4
-
-
Toggle show NPC state
-
Right thumb Move left* Rotate counterclockwise
-
Right thumb Move right* Rotate clockwise
Space
Right button Left button Shoot
A
Digital pad left
-
Strafe left
D
Digital pad right
-
Strafe right
G
-
-
Toggle God mode
S
Digital pad down
-
Retreat
W
-
Right trigger Move forwards
Backspace
-
-
Restart game
P
-
-
Save screenshot to a file
Esc
-
-
Quit game and close the window

* Note that the mouse cursor must be inside the game window for player rotation to occur.

3. Building the Game

This code uses SAGE. Make sure that you have followed the SAGE Installation Instructions. Navigate to the folder 2. Top Down Tiled Game in your copy of the sage-games repository. Run checkenv.bat to verify that you have set the environment variables correctly. Open Top Down Tiled Game.sln with Visual Studio and build the Release configuration. The Release executable file Top Down Tiled Game.exe will appear. Alternatively, run Build.bat to build both Release and Debug configurations.

4. Game Play

Run Top Down Tiled Game.exe and do the following:

  1. Use the controls (see Section 2) to attack and kill the guards on each of the three levels provided. Use God mode (toggled with G) if you keep getting killed.

  2. Repeat the previous step with bounding shapes and NPC state drawn (toggled with F3 and F4, respectively). Try to break the collision detection and response by doing unexpected thing such as running into corners or shooting into walls.

5. Code Breakdown

Open Top Down Tiled Game.sln in Visual Studio and examine the code in the editor while you read the rest of this section. This section assumes that you have read and understood the documentation from SAGE, The Blank Game, and The Top Down Game.

The most important change from The Top Down Game is the use of a tile manager to read and manage a background image drawn with repeating tiles and the initial placement of game objects. The tile manager CTileManager is declared in TileManager.h and defined in TileManager.cpp. It is derived from Sage::CSettings and CCommon. The tile manager header file TileManager.h also includes the a declaration for an object descriptor CObjectDesc that will be used to encapsulate properties of a game object interpreted from the map file for transfer to the object manager.

The tile manager has three main responsibilities: reading in a map from a text file in Media\Maps (Section 5.1), drawing the tiled background (Section 5.2), and performing collision detection and visibility tests between game objects the walls specified in the map file (Section 5.3),

5.1 Map Files

The map files consist of upper and lower case letters in which each letter represents a background tile and/or the initial placement for a game object, and each line represents a row of tiles and/or objects. The letters are interpreted as follows. W represents a wall tile. F represents a floor tile. Upper case characters U, D, L, or R represent the player character oriented with its gun pointing up, down, left, or right, respectively. There should be only one of these in the file. Lower case characters u, d, l, or r represent a guard character oriented with its gun pointing up, down, left, or right, respectively. There may be more than one of these in the file. The player and guard characters are assumed to be on a floor tile. For example, the file Media\Maps\small.txt contains the specification for the map shown in Fig. 1, as follows:

WWWWWWWWWWWWWWWW
WFFFFFFWFFFFFFFW
WFFFFFFWFFFFFdFW
WFFFFFFFFFFFFFFW
WFFFFFFFFFFFFFFW
WFFFFFFWFFFFFFFW
WFFFFFFWFFFFFFFW
WWWFFWWWWWWWFFFW
WFFFFFFFFFFWFFFW
WFFFFFFFFFFWFFFW
WFFFFFFFFFFWFFFW
WFFFFFFFFFFWFFFW
WFFFFFFFFFFWFFFW
WFFFUFFFFFFFFFFW
WFFFFFFFFFFFFFFW
WWWWWWWWWWWWWWWW

CGame::BeginGame reads the map for the current level into a 2D array of characters CTileManager::m_chMap by calling CTileManager::LoadMap with the text file name as the parameter. In the next subsection we will see how the tile manager uses CTileManager::m_chMap to draw the tiled background in CTileManager::Draw.

5.2 Drawing Tiles

Fig. 2: The tiles.

There are three different type of tile sprite shown in Fig.2, the floor tile (left), the wall tile (middle), and the error tile (right). The error tile should never appear in your game - if it does then you have an error in your text map. These three tiles are input from files tile0.png, tile1.png, and tile2.png into the three-frame sprite eSprite::Tile, as indicated by the following line in Gamesettings.xml.

<sprite name="tile" file="tile" ext ="png" frames="3"/>

We can then load the sprite from this description with the line

media.Insert(eSprite::Tile, "tile");

in CGame::LoadSprites using the name field from the <sprite> tag as the second parameter. This is how you load sprites from different files numbered 0, 1, 2, etc., as opposed to the animated sprite for the guard, which is loaded from a sprite sheet. The floor and wall tiles should roughly line up without any gap, for example, Fig. 3. shows that the wall tile at the center lines up with the four tiles that it touches.

Fig. 3: A 3x3 tiling using wall tiles.

Enumerated type member eSprite::Tile is passed to CTileManager::Draw, which draws the tiled background from CTileManager::m_chMap using the first frame of the tile sprite as the floor, the second frame as the walls, and the third frame in case of error (see Fig. 2). CTileManager::Draw is called from CObjectManager::Draw, which is called from CGame::RenderFrame.

5.3 Collision Detection and Player Visibility

Collision detection and response for objects colliding with walls is done with axially aligned bounding boxes (AABBs for short), which are boxes with their edges aligned with the principal axes. Although we are in a 2D game, we will use the 3D bounding box DirectX::BoundingBox for convenience, with all Z-coordinates set to zero. CTileManager has a list of AABBs for the walls in a private member variable of type std::vector<DirectX::BoundingBox> called CTileManager::m_vecWalls. It is tempting to use an AABB for each wall tile, but that would mean that even small maps like the one shown in Fig. 1 would need 78 AABBs, whereas these can be collapsed into just 9 overlapping AABBs. Hit F3 in Top Down Tiled Game.exe to show the bounding shapes as shown in Fig. 4.

Fig. 4: Bounding shapes.

These large AABBs are created using function CTileManager::MakeBoundingBoxes, which is called from CTileManager::LoadMap after the map has been loaded. CTileManager::MakeBoundingBoxes consists of three phases, CTileManager::MakeBoundingBoxesH for horizontal bounding boxes that consist of at least two tiles, CTileManager::MakeBoundingBoxesV for vertical bounding boxes that consist of at least two tiles, and CTileManager::MakeBoundingBoxes1 for bounding boxes that consist of a single tile.

5.3.1 Collision Detection

In Fig. 4 we see that the player now has two bounding circles, a small one for collision with bullets as in The Top-Down Game, and a larger one that encompasses the whole sprite for collision with walls. Since the guard does not move, they only have the small circle. The player's large bounding circle has radius stored in CObject::m_fCollisionRadius, which is only used in CPlayer but can be used in other objects if desired. CObject::m_fCollisionRadius is set to CObject::m_fScale multiplied by the maximum of the player sprite's width and height, divided by two.

CObjectManager::BroadPhase handles collision detection and response for the objects against walls. For each instance of CObject in its object list, it constructs an instance DirectX::BoundingSphere at the object's position with a Z-coordinate of zero and a radius equal to the object's CObject::m_fCollisionRadius. Collision of the bounding sphere and a wall is detected by passing it to CTileManager::CollideWithWall, which uses DirectX::BoundingSphere::Intersects for each AABB in the tile manager's AABB list CTileManager::m_vecWalls.

Fig. 5: Collision normals and overlap distances.

If the call to DirectX::BoundingSphere::Intersects in CTileManager::CollideWithWall detects that the bounding sphere and a wall AABB overlap, then it computes the collision normal and the overlap distance, returning them in call-by-reference parameters norm and d respectively. Fig. 5 shows the collision normal \(\hat{n}\) and overlap distance \(d \) for the cases when the bounding sphere overlaps a single vertex (left) or a single edge (right) of the bounding box. See CTileManager::CollideWithWall for more details.

5.3.2 Player Visibility

Fig. 6: Line of sight from guard to player.

Notice that the guard is unaware of the player in In Fig. 4, since there is a wall between them. In Fig. 6 there is a clear line of sight from the center of the guard's head (i.e. m_vPos) to the center of the player's head, so the guard has rotated to face the player and begins to shoot. CTileManager::Visible is used in CGuard::Move to detect whether a line can be drawn between the position m_vPos of the player and that of the guard without intersecting a wall bounding box. This makes use of DirectX::BoundingBox::Intersect, which detects intersection of a DirectX::BoundingBox with a triangle. The triangle in question is a 3D triangle with one vertex at the guard's m_vPos with a Z-coordinate of zero, one vertex at the player's m_vPos also with a Z-coordinate of zero, and one vertex player's m_vPos with a Z-coordinate of 1.0f.

6. What Next?

Next, take a look at the Tiled Platformer.