The Allegro Wiki is migrating to github at https://github.com/liballeg/allegro_wiki/wiki

Allegro game from zero

From Allegro Wiki
Jump to: navigation, search
This article is incomplete and needs some revision or has some pending TODOs. Please help Allegro by editing and finishing it. When the article becomes complete, you may remove this tag.


Making a game from scratch using Allegro and C++

DEBUG: This article is not finished, is a work in progress, please check it often to see updates


Goal

Our goal in this article is to build a simple game from the beginning using Allegro, C++ as our language and other development tools to build the multimedia data (sprites, sound, etc.) that we need. So, broadly our objetives are:

  • Make a full old-school space shooter game.
  • Use low or zero cost, freely available tools.
  • Produce a well designed, good documented source code.
  • Do all our media files by ourselves (sprites, sound, etc.)
  • Make it portable to at least two operating systems (Windows and GNU/Linux)
  • Learn to package it so we can give it to our friends.

Tools that I chose to use

Since one of our objetives is to do this with low cost, I chose to use tools that are available to us free.

This is only a guide, because there is lots of tools that can do the same tasks than the ones that are in this list, so feel free to choose and use whatever you want to do the task proposed.

Choosing the right tool(s) for the task that you want to do is a important step.

List of suggested tools for each task:

Tool Task Where to get it?
gcc We are going to use it as our C++ compiler You should download and install the one that is for your operating system, comes installed by default with most GNU/Linux systems. If you use Windows, you can get MinGW, Cygwin, or IDEs such as Dev-CPP
Allegro This is our beloved game library, we use it to render graphics on screen, get user input, etc You can get it at http://alleg.sf.net/
GIMP This is our image editor, with this we are going to do all the artwork for the game, such as sprites. Get it at http://www.gimp.org/
ASE If you don't want to use GIMP, you could use this small pixel art editor, named ASE (Allegro Sprite Editor) Get it at http://ase.sourceforge.net/
Audacity This is our sound editor, we are going to record and edit our sound effects with this Get it at http://audacity.sourceforge.net/
jEdit I use this for editing the source code files. You should choose your editor very carefully. A mature and well-designed programmer's text editor, get it at http://www.jedit.org/
Dia This is optional. I use it in the design phase to do my UML diagrams. There is lots of other UML design tools, but I found this one to be light and simple. Choose the one that you want more, or you can design "by hand" using pencil and paper too. Get it at http://www.gnome.org/projects/dia/ ; there is also a Windows version at http://dia-installer.sourceforge.net/

It is very important that you use a editor that you are comfortable with, so please take your time choosing it right. jEdit is, like any other tool mentioned, just a suggestion. Most programmers choose their editor with more care than they put into the purchase of a car.

About the graphics editor, choose the one that you like. I choose The GIMP because it can do almost anything and is free, but you can use whatever you want (Photoshop, Paint Shop Pro, ASE, Paint Brush, whatever you think!).

Knowledge that you need to do this tutorial

  • You need to know the basics of programming in C++, and also the basics of object oriented design and programming.
  • How to use your compiler of choice, as well as the command line of your operating system.
  • Knowledge about how to use your graphics editor (be it GIMP, Photoshop, or whatever you chose).
  • General knowledge about all the tools that you plan to use is very encouraged.

Getting started

First, we have to define what kind of game we want to do. Like I already said, I'm going to do a space shooter game. To be more specific, a rotational space shooter where you shoot lots of bad space dudes.

So, the game will look more or less like this (with better graphics, theoretically)

Image:mockup_game.jpg

So, lets go to the design phase!

Design phase

We should define a set of rules that will handle our game world. For this game, we can assume the following:

  • there is a single human player at all times.
  • the player has a single life with a good supply of "shields" or "energy", and when it runs out, is game over.
  • there are multiple enemies trying to kill the player, this amount of enemies is fixed for performance reasons.
  • everything can collide with everything. That means projectiles from enemies also kill enemies, and enemies can collide with each other and with you too.
  • each thing as a life, that decreases when colliding with something in the case of player or enemies, and with time or collisions in the case of a projectile.
  • the particles can't collide with anything, they are separated from the world objects, and are only for eye candy.
  • the world is infinite, you can fly in any 2D direction.
  • the game has no end, you play until your die trying to get the highest score (this is very old-school! :P )

Well, so, with our set of rules, we can go further into our design phase, and start doing some UML graphs that will help us to code the C++ "engine" that is going to power our game.

Please read this web page about UML if you want to know more about it.

Please read this web page about object oriented design to know more about it.

There is lot of other material about UML and object oriented design online, search for UML and object oriented design

DEBUG: From here, random stuff that is going to take "shape"

The Class Diagram (UML)

WARNING : This class diagram is not finished yet, and this design will certainly change/evolve in the future

The class diagram shows the classes of the system, their interrelationships (including inheritance, aggregation, and association), and the operations and attributes of the classes.

Here is my current class diagram, (big image, sorry :D)

/pub/Main/AllegroGameFromZero/tutorial_allegro_class_diagram-06-dec-2005.png

I did my class diagram using Dia and then exported it to a png file (I mentioned Dia earlier, remember?), but you can even use paper if you like. Alternatively, don't draw them at all and just use another method of your choice.

The cool thing about using Dia is that you can use Dia2Code to convert the diagram to source code.

Dia2Code basically reads a Dia diagram file that contains an UML class diagram and creates files in the language of choice that contain the bare bones of the classes represented in the diagram.

Check http://www.agilemodeling.com/artifacts/classDiagram.htm, http://www.developer.com/design/article.php/2206791, http://www.smartdraw.com/tutorials/software-uml/uml2.htm or search using a search engine to learn more about class diagrams and their interpretation.

The "Thing" class

Each enemy, bullet, or player is a subclass of the superclass Thing, and each one implements behaviour proper of the subclass that we are modelling (i.e enemies will try to kill the player, player will respond to human input, etc).

To model our world "actors" (player, enemies, and bullets/shoots), we have to choose a few rules to start doing our design.

  • The collision detection method is going to be "bounding spheres", that means, we check collision as if each object were a circle (since we are on 2D, they are circles, not spheres).
  • When one object collides another, we must model the damage, so we substract the "damage power" of each one from the other's life.
  • We determine the position by the way of a (x,y) pair of coordinates
  • We move them by a speed vector, that has angle and speed stored on polar coordinates (see http://www.google.com/search?q=polar+coordinates polar coordinates] for more info).
  • The sprite will rotate acording to speed vector's angle, to simulate that the thing moves in that direction.

The Thing concept looks like this:

Image:thing.jpg

Thing class has the following properties :

  • x
  • y
  • speed vector
  • radius
  • collision damage power
  • life
  • sprite

Coding phase

Well here we are, in the coding phase. In this phase, we are going to write the source code of our game based on our design. Sometimes (well mostly) during this phase we also produce some early artwork to test the code that we are doing. We also sometimes need to refactor/refurbish the design to add new cool stuff that we may think of while we are coding, or to fix design errors (very bad things happens if you have design flaws, or so I was told at college campus... :P).

I'm not going to paste all the code for this game here. I will prefer to upload a compressed file with all the game files when its done. For now, I'm going to paste snippets of the most relevant source code and refer you to the proper files.

Setting up our working space

We are going to start to write our source code, so we need to set up a space on our computer where we can store all the files that we are going to produce.

I chose to use this directory tree, and I created it on my hard drive in a special place that I have reserved for my development projects ("C:\My programs\" or "~/my_programs/" guess? ;) )

+- AllegroGameFromZero
+--- bin
+---+- data
    +---- sound
    +---- sprites
+--- doc
+--- include
+--- obj
+--- src

I took this directory tree from my project, AlMake, that is a generic Allegro build system and template project tree.

AlMake is a group of scripts and files that helps the developer and end-user to configure, update, and build Allegro and AllegroGL projects, on many platforms, and with different compilers.

You may want to use it too, because it has a simple makefile to help you compile the source code.

Now, I will let you know what I put on each directory.

In AllegroGameFromZero, I have the makefile that came with AlMake, as well as the README, INSTALL, TODO, LICENSE, etc files.

If you plan to distribute your game, you should write at least a README file.

I encourage you to open the makefile with a text editor, and fine tune it until you are satisfied. Or better yet, write your own makefile, just to learn how to do it. Just remember to don't convert tabs to spaces on a makefile, I know why I say it.

In bin, will go the binary file (executable file), and the data files produced after compiling all sprites+sound+etc using the grabber, or, if you want to say it other way, the binary files needed to play the game.

When you get AlMake package it comes with a simple Allegro C example. I erased the .c and .h files from include and src, to let the structure "clean" for this C++ project. I also created the data, sound, sprites directory, to put all the datafiles, such as sounds, sprites, Allegro's grabber files, etc.

In doc , I will put all the extra documentation, manuals, etc.

In include, I will put all the header .h files that I will write for this project.

In obj goes all the intermediate object files that the compiler makes while compiling the source code to a binary form.

In src, I will put all the C++ code, .cpp files that I will write for this project.

Now that we have our directory tree ready, we fire up our favorite text editor (the one that you choose before starting reading this, remember), and we start coding...

Coding the program entry point

Well, any program should have a entry point.

In our case, we coded on it the Allegro and hardware startup, as well as parameters checking.

This will also get a instance from Main class (singleton pattern), and start the real game, after we have all the hardware properly set up (keyboard, mouse, screen, sound, etc).

Here is the source code so far.

File : main.cpp

To be sincere, we should name main.cpp this file, and Main.cpp, Main.h the file that has the Main class; I named MainC.cpp and MainC.h to try to have compatibility with systems that don't distinguish between upper and lower case, like DOS.

<highlightSyntax language="cpp">/* Making a game from scratch using Allegro and C++

By Kronoman In loving memory of my father. Made in Argentina.


This is the code for starting up the hardware, as well as Allegro library.

Released under the MIT license.


Code started Wed Nov 30 03:51:44 GMT-03:00 2005 @327 /Internet Time/

  • /
  1. include <allegro.h>
  1. include <string.h>
  2. include <stdio.h>

// our error handling routine

  1. include "gerror.h"

// our Main class

  1. include "MainC.h"

int main(int argc, char *argv[]) {

  // parameters config
  int depth = -1; // color depth, by default -1 means to get the color depth from user's desktop
  int vid_m = GFX_AUTODETECT; // screen mode
  int vid_w = 640; // screen resolution width
  int vid_h = 480; // screen resolution height
  
  bool want_sound = true; // do you want sound?
  
  // check command line parameters
  for (int i = 1; i < argc; i++)
  {
     if (!stricmp(argv[i], "-wn") ||
        !stricmp(argv[i], "-w") ||
        !stricmp(argv[i], "-win") ||
        !stricmp(argv[i], "-windowed"))
        vid_m = GFX_AUTODETECT_WINDOWED;


     if (!stricmp(argv[i], "-nosound") ||
        !stricmp(argv[i], "-silent") ||
        !stricmp(argv[i], "-ns"))
        want_sound = false;
     if (!stricmp(argv[i], "-bpp16"))
        depth = 16;
     if (!stricmp(argv[i], "-bpp15"))
        depth = 15;
     if (!stricmp(argv[i], "-bpp32"))
        depth = 32;
     if (!stricmp(argv[i], "-bpp24"))
        depth = 24;
     if (!stricmp(argv[i], "-bpp8"))
        raise_error("main() : Sorry, this program don't support 8 bpp displays.\nThis program needs a true color display at %3d x %3d resolution.\nTip: Try removing the -bpp8 switch from the command line invocation.", vid_w, vid_h);
  }


  if (allegro_init())
     raise_error("main() : Allegro failed to start!");
  srand(time(NULL)); // init random numbers
  if (install_timer())
     raise_error("main() : can't install timer driver!");
  if (install_keyboard())
     raise_error("main() : can't install keyboard driver!");
  if (install_mouse() < 0)

// raise_error("main() : can't install mouse driver! a mouse is needed to play this game! sorry...");

  ; // in this game, the mouse is not required, if you want to require it, uncomment the error message
  if (depth < 16)
     depth = desktop_color_depth(); // get desktop color depth
  // safe check
  if (depth < 16)
     depth = 16;
  set_color_depth(depth);
  // try to set up a valid GFX mode
  if ( set_gfx_mode(vid_m, vid_w, vid_h, 0, 0) )
  {
     set_color_depth(16);
     if ( set_gfx_mode(vid_m, vid_w, vid_h, 0, 0) )
     {
        set_color_depth(15);
        if ( set_gfx_mode(vid_m, vid_w, vid_h, 0, 0) )
        {
           set_color_depth(32);
           if ( set_gfx_mode(vid_m, vid_w, vid_h, 0, 0) )
           {
              set_color_depth(24);
              if ( set_gfx_mode(vid_m, vid_w, vid_h, 0, 0) )
              {
                 raise_error("main() : I can't set the graphics mode (%3d x %3d @ %2d bpp)\nI also tried with 16 bpp, 15 bpp, 32 bpp and 24 bpp\n", vid_w, vid_h, depth);
              }
           }
        }
     }
  }
  set_color_conversion(COLORCONV_TOTAL | COLORCONV_KEEP_TRANS);
  // sound
  if (want_sound)
  {
     reserve_voices(8, 0);
     set_volume_per_voice(2);
     install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL);
     set_volume(255, -1);
  }
  set_window_title("Allegro Game From Zero Tutorial"); // windows title
  // run game, Main is a singleton, that's why we use this way to get it
  Main *mainObj = null;
  
  mainObj = Main::getInstance();
  
  mainObj.Start();
  
  Main::releaseInstance();
     
  return 0; // this is the end my only friend, the end...

} END_OF_MAIN(); </highlightSyntax>


Error messages and log system

Any good program should be able to end gracefully, even if some error occurs.

Our game will do extensive check for errors.

For that, I designed a simple function that can go out of graphics mode, and show a message to the user in case of a fatal error (a error that makes impossible to continue playing the game).

Here is the code, this may evolutionate in the future.

gerror.h

<highlightSyntax language="cpp">#ifndef GERROR_H

  1. define GERROR_H

/* Simple error message system.

  • /
  1. include <allegro.h> // I use some stuff from here too, mainly, unicode strings ;)
  2. include <stdarg.h> // for the variable argument list
  3. include <stdlib.h> // for use of malloc

/// Goes back to text mode, shows the message and ends the program. Printf style. void raise_error(AL_CONST char *msg, ...);

  1. endif
</highlightSyntax>

gerror.cpp

<highlightSyntax language="cpp">#include "gerror.h"

/* Goes back to text mode, shows the message and ends the program. Has printf like format for msg.

  • /

void raise_error(AL_CONST char *msg, ...) {

  char *buf;
  /* exits the graphics mode */
  set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
  /* creates the buffer */
  buf = (char *)malloc(4096);
  if (buf == NULL)
  {
      allegro_message("raise_error(): There is a error, and I'm out of memory to show the error message. This sucks. :^(\n");   }
  else
  {
     /* parse the variable parameters */
     va_list ap;
     va_start(ap, msg);
     uvszprintf(buf, 4096, msg, ap);
     va_end(ap);
     allegro_message("%s\n", buf);
     free(buf);
  }
  exit( -1); /* abort the program */

}

</highlightSyntax>

TODO : make a log system, for debug purposes.

TODO : make a simple console system too.


Drawing the sprites

For drawing the sprites, I used The Gimp.

This is not really a sprite drawing tutorial, so I'm going fast on this topic, just showing how I did one ship.

  • First, I created a empty 64x64 image, filled with "magic pink", the transparent color for Allegro (maximum red and blue, zero green).
  • Then, using the Pencil, and a 1x1 brush (pixel brush), I drew the half outline of a ship on a layer on top of the pink background.
    Image:ship1_step1.png
  • After that, I duplicated the layer with the half outline, and using the Flip tool, I did a symmetric ship (you could do a non-symmetric ship too if you like it, just avoid this step and draw all the ship by yourself). Then, I merged down the layers, and produced a single layer with the black outline.
    Image:ship1_step2.png
  • Then, I duplicated the outline layer, and filled it with colors. To do this easy, I used masks with the Magic Wand tool so I could paint by areas.
    Image:ship1_step3.png
  • Finally, I shaded the color layer, using the Dodge/Burn tool, and selections using the Magic Wand. You do like this: select area using the Magic Wand, shade at pleasure using the Dodge/Burn tool, then repeat with another area. I also rotated the sprite, because zero degrees in our game is pointing to the left.
    Image:ship1_final.png

Well, that's all for now. I really encourage you to draw a lot, and find your own art style. Know very well your drawing program, they usually have a lot of tools that combined together in the right way can produce very good results.

Roadmap, remove this section when article is finished

Planned road map :

  • design phase
  • coding phase
  • artwork
  • sound
  • testing
  • tweaking
  • package and distribute it
  • further reading, discussions, links, bibliography maybe.


-- Article started 31 Oct 2005 by Main.kronoman