Introduction

This lesson will get us loading the map and scrolling around it using the cursors.

Structure

Despite all good programming practices, most games will end up with loads of global variables. Inevitabily they will be referenced in a dozen or so files, all using the 'extern' statement.
To avoid having extern statements everywhere in our code and to make it easy to find globals, we've added a file 'game_vars.cpp'. All the global variables for the game will now be declared in this file (in the previous lesson they were in the game_transam.cpp file), and in the game_transam.h we've put an 'extern' in for every global. Because we link to this header file, we have full access to the globals.
This is done so that adding a source file and having access to all our functions and globals within this source file is a simple matter of '#include "game_transam.h" at the top of the file.
Edit the game_vars.cpp and the game_transam.h and you'll see references to the ones we created in the previous lesson, e.g. GameFramework, DisplayPanel.
If you are manually creating the project make file then use the one from lesson 1 and additionally you will need to add the files game_vars.cpp and mappyal.c (from the libraries directory).

Mappy

I guess now would be a time to talk about Mappy. Mappy is a tile mapping library and the playback library (the API) is contained within the file mappyal.c. Various versions exist, we are using the Allegro version (hence the 'al' at the end). We will be discovering it's joys throughout the articles.
Our map is contained in the file 'tranzam.fmp'. If you wish to read up more on the file format then visit the website and download the full version which includes tutorials, etc. However, these lessons should be enough.
Hopefully you will have downloaded the mappy editor as described in the first lesson. Run this and open up the tranzam.fmp file. This is our map. Have a play and rummage around, but remember to keep a backup in case you break anything.
mappy screenshot As you can see from the map (and the terrible graphics), it is composed of a grid of equally sized tiles. Our tiles are 32x32 pixels and in 16-bit colour. The default colour depth for AXL (what is set currently in the XML configuration) is 32 bit. So at some point we'll either increase the graphics, decrease the colour depth, or let allegro convert them automatically to 32-bit.
The actual map is to the left (zoomed out), and the tiles to the right. There are a total of 100 tiles. The mappy API consists of a small number of core functions to draw the map and set/get tile values. If you double click on one of the tiles a dialog box appears. This contains the custom values associated with that tile. Ignore the names (well, most of them), they are meaningless and you should just think of them as a number of flags that are integer and boolean based. They will be explained later.
Mappy can store each tile at a different level, they are nomimally called BG, FG1, FG2, FG3. Within the API map drawing functions you specify which levels to draw. For example, in Tranz-Am our drawing order will be BG, then the car sprites, then FG1. This will be discussed in more detail at a later lesson.

game_transam.cpp source file

There isn't much change here:
  1. We will be displaying the map to the right of the display panel (lesson 1) so we calculate a few variables ScreenXOffset/ScreenYOffset and ShownMapWidth/ShownMapHeight. These will give us the position and size of the map that we can display. The 'Configuration' class that comes with AXL (we defined it in lesson 1 as 'GameConfiguration') has a number of values associated with it (they are structs), that describe various things:
    • CapsGraphics: details on the screen, depth, window mode, etc.
    • CapsSystem: details on how allegro is set to work
    • CapsSound: details on how sound/music is to be used
    • CapsActualSystem: what was set after allegro initialisation
    So in the code we use this to get the screen size, GameConfiguration->CapsGraphics.ScrWidth will get us the screen width.
  2. In the EndGame() function (what is called when we are about to exit the game) we've added the following call, MapFreeMem(). This is a Mappy function and lets it clear all it's allocated memory and tidy up generally.

Configuration

If you open up the config.xml file you will see lots of values, these are mostly self-describing but there is (should be) at the bottom, a full explanation. It is here that the width/height of the screen is set. 'Configuration' is the class that looks after the config.xml file and initialises allegro. It reads the data from the XML, stores it in structs and has methods for initialising, etc. Refer to the AXL documention for more details, but 'CapsGraphics' as mentioned above is one such struct and contains the size of the screen. 'GameConfiguration' was described in lesson 1 and is our pointer to the Configuration class.
With lesson 1, have a play with some of the values of config.xml (make a backup first) and see what effect it has on the screen. A full log of what occurred during the reading of config.xml and the initialisation of Allegro can be found in the log files created after running the executable.

game_game.cpp source file

Most of the changes are in this file, and are:
  1. Update our TransAmGameLogic(). Remember this is the function that is called once every frame so that we can update the game. In lesson 1, it simply waited for the ESCape key to be pressed. In this lesson we will use it to handle the cursor keys and scroll the map.
  2. Update our TransAmGameDrawing(). In lesson 1, this simply cleared the drawing buffer and drew the panel. In this lesson we will additionally draw the map.
  3. Add a function, NewLevel(), that will load up and initialise our map

Scrolling

Imagine the picture below is a zoomed out image of our map, the red rectangle representing an area 800x600 pixels (i.e. enough to fill a screen) and the whole picture is 8000x6000 pixels (obviously that's rather too large to handle comfortably, but we are talking theoretically) and effectively represents an area of 10 screens by 10 screens. map If we represent the location of the top-left corner of the red rectangle by the variables VIEW_X and VIEW_Y and it's size being VIEW_W, VIEW_H we can show how scrolling is performed.
Every time our drawing code is invoked we grab a portion of the main map image at location VIEW_X/VIEW_Y and of size VIEW_W/VIEW_H and send it to the drawing buffer/screen. If we increase VIEW_X/VIEW_Y by one every time we press cursor right, for example, we have scrolling. If we increase VIEW_X/VIEW_Y by VIEW_W or VIEW_H every time then we are scrolling by one whole screen at a time (i.e. we have screen flipping rather than scrolling). If the map is relatively small (e.g. a few screens wide/high) then almost all computers will be able to cope with this sized image, if scrolling a bitmap is all that is required.
Now, we are using a tile map and we do not have a 8000x6000 pixel bitmap. But the same applies, the only difference is how we obtain the bitmap representing the image at VIEW_X/VIEW_Y. If we were implementing our own tile map, we would have some kind of array of integers (e.g. TileMap[][]), each representing one tile. Each tile would be of the same size (e.g. 32x32 pixels). Because we know the size of each tile and the graphic for each tile we would locate the starting tile (top-left of our view window) in the array as something like 'VIEW_X/TILE_SIZE' and 'VIEW_Y/TILE_SIZE'. We know how many tiles there are to draw (VIEW_W/TILE_SIZE and VIEW_H/TILE_SIZE) and so a simple loop (e.g.
for(int y=VIEW_Y/TILE_SIZE; y<VIEW_H/TILE_SIZE;y++) for(int x=VIEW_X/TILE_SIZE; x<VIEW_W/TILE_SIZE;x++) { int Tile=TileMap[y][x]; BITMAP* TileGfx=TileImages[Tile]; draw_sprite(TileGfx,ScreenBuffer,x*TILE_SIZE,y*TILE_SIZE); }
Obviously this is not optimised and doesn't take into account partially visible tiles or include any error checking for invalid locations (i.e. the above loop/calculations will draw and snap to whole tiles only). To get round this problem of scrolling by a number of pixels smaller than the tile size, you could do various things, including increasing the size of the tile area retrieved (e.g. in the 'y' loop above change it to 'int y=(VIEW_Y/TILE_SIZE)-1') and then offsetting this using the VIEW_X/VIEW_Y.
That's the theory (in simplistic terms). In this game, as mentioned earlier, we will be implementing a scrolling tile map using the Mappy library.

Drawing a Map

Drawing and scrolling our map is rather simple with Mappy (and the principle applies to any scrolling). At the start of the game we have (curtesy of Mappy) variables that tell us how big our map is, both in pixels and in tiles. We have also calculated (as above) how big our map on the screen will be (effectively the whole screen starting from the edge of the display panel).
What we now need are some variables to hold the position that we want to be at within the map (in pixels). We will start with the centre point of the map.
To make the map scroll, whenever we move the cursors, we increase or decrease these variables. In our drawing code it is then simply a matter of finding the start point of the tile map, offsetting it by our new amount and asking Mappy nicely to populate our drawing buffer with the map, starting from this point and being the size of our display area.

Code Walkthrough (game_game.cpp)

First up we create our two variables to hold where on the map we want to display to the screen. These are mapXoff/mapYoff. Next, within TransAmGameLogic, We load up the map (described later) if we are on a new level, and set the two mapXoff/mapYoff variables to be the mid-point of the map. Mappy defines four variables: mapblockwidth/mapblockheight and mapwidth/mapheight that show the size of each tile and the number of tiles in map.
Next we will check for cursor movement and update our two variables mapXoff/mapYoff accordingly.
Next we will call MapUpdateAnims(). This is a Mappy function to update any animations it has. Mappy lets you define animated tiles, and in our map we have defined a couple of tiles as being animated (twinkling water, reflecting gently from the sun's rays). To view these animations, right-click within the tile window.
When you set up a map within Mappy, each tile can be given a level: BG, FG1, FG2, FG3. When using Mappy to draw the tilemap you tell it which of these levels you wish to draw. This allows you to layer stuff, for example BG first, then your player (not defined within Mappy, but a standard sprite), then your FG1 tiles on top.
These layers are also useful for when your map has lots of different BG tiles (e.g. in TranzAm we have grass, sand, water) and you want to place a similar graphic on top of these ground tiles.
Drawing the BG tiles is done via the MapDrawBGT() function, and drawing the FGx tiles is done via the MapDrawFG() function.
Using all that was said above, we call the two Mappy drawing functions using the variables we have calculated.
Note: in the mappy.txt file in the 'code/libraries' directory is a summary of the variables and functions that can be called.
Loading and setting up Mappy is simple enough and defined in the function NewLevel(). To load a map you use MapLoad ("transam.fmp") or MapLoadVRAM ("transam.fmp"). The difference is that the first loads using standard Allegro bitmaps, and the second uses Video memory. Our code checks what mode has been set (using the Configurator class) and calls the relevant one.
Mappy is then initialised and we call two additional Mappy functions: Compile and run. You should see something like below. screenshot At this point, don't worry about any jerky scrolling, it will be fixed.

Next

In the next lesson, we'll be placing the car on the map/screen and the car will be doing the scrolling of the map.