Framework

Overview

AXL_FRAMS provides a system for creating and running a game without the need for any Allegro initialisation, setup or game loop/timer creation (i.e. you write the game, the framework does the rest), contains a series of helper functions/variables to do all the mundane stuff you'd rather not write, handles animation, graphics loading and configuration of allegro and custom values.

If all you require is graphics and animation handling, then do not use this library, instead use AXL_GRANI (Animation library). Similarly if want to control the game yourself and just want to automate the initialisation of Allegro (without the creation of buffers, simplified sound routines, etc) then use AXL_CONCUR (Configurator library). If you want to use the facilities of AXL_CONCUR but also want animation/graphic file handling then use AXL_GRANI and AXL_CONCUR.

However, if you wish to write minimal code to initialise AXL_CONCUR and/or AXL_GRANI, want to use additional modules such as the advanced sound control, set up a game/program using paging, triple buffering, or double buffering, then AXL_FRAMS (Framework library) is required. Note that the Framework library can be run without the game management (the FPS handler), i.e. you want everything set up but write your own timer/game loop).

The framework is contained in a single class, Framework. You instantiate a variable of this type and start using its data/functions. What follows is a list of the methods and data available to the Framework. The Framework can also handle graphics, animation and configuration values (allegro and custom values). For instructions on using the library, refer afterwards to the 'Usage' section and the tutorials in tests\framework directory - this section is a reference guide.

Instructions for using the Animation and Configurator libraries can be found in their own sections via the menu and are described in brief within this section. When using the Framework to set up and run Animation or Cofigurator there is no need for creating instances of the class, etc. as that is what the Framework does - it gives you fully instantiated and working Animation and Configurator objects.

Reading the below might not make sense on first reading, but the 'usage' section should make things clearer.

Methods/Data

Core

Although a large amount of functions, realistically you only need to use the first constructor, GetConfiguration()/GetGraphicsLibrary().
Methods Details
Framework(string config, string animation, bool InitialiseSystem,(*bitmapcreator)(string))Construct the Framework. Pass in the name of an XML file or an XML string for animation or config (animation can be blank and signifies you do not want to load any graphics or animations.) The XML file can also be XML data as a string. Refer to the documentation of AnimationLibrary or Configuration for more details. If 'InitialiseSystem' is set (defaults to true) then the constructor fully initialises Allegro, including all the buffers, sound, etc. and leaves you in a graphics state ready to either call the gameloop or to just do your own coding. If not, then you need to call 'InitialiseFramework' afterwards. This is useful to perform any additional tasks before starting the system, e.g. overriding any values a user might change in the XML file.

bitmapcreator is a callback function (defaults to NULL, i.e. not used) for when you wish to use your own function for returning back a bitmap for each of the bitmaps referenced in the animation XML 'bmp_file' attribute. This is useful for custom bitmap loaders other than the default (which will simply assume a bitmap filename that will be loaded). See 'Custom Bitmap Loader' section below.
void InitialiseFramework()This function is called automatically by the constructor and should ONLY be called when the 'InitialiseFramework' variable is set to false.
void DestroyFramework()The destructor (when you delete your Framework class object) normally calls this. This method destroys all loaded graphics and buffers, free up all memory and leaves the system in text mode. Don't call it unless you have reason for not destroying the Framework class object.
bool RestartSystem(void)This function closes all animations/graphics, frees up memory, then reloads the configuration XML and animation (if set) XML files and starts allegro. This function is of use if you wish to change any core allegro values but carry on with the program (e.g. give the user the option to change to triple/paging/double buffering, windowed/fullscreen, etc. This function can only be called if you initialised the framework using a config XML file. Returns false on any errors.
Configuration* GetConfiguration()
AnimationLibrary* GetGraphicsLibrary()
All of the animations and graphics you load are handled by the AXL_GRANI library, and all the configuration values are handled by the AXL_CONCUR library. In order to use all the features of the configurator/animation libraries these two methods return a pointer to the actual library. You can then refer to the Animator/Configurator help file for guidance on their usage. Just remember that you can ignore all the constructors, destructors, etc. as they have already been called. Returns NULL if not set (e.g. no animation object)
BITMAP** GetGraphicBuffers()This can be left alone as is not required if you are letting the Framework handle the drawing for you. If you are lettig the Framework initialise the system but you want to use your own game loop this method returns an array of BITMAP* variables, e.g. if you call 'BITMAP* array = F->GetGraphicsBuffers()' then array[0], array[1], array[2] each point to a BITMAP* the size of the screen. If using double buffering, only element 0 is populated, if using paging, elements 0 and 1 are populated and for triple buffering, all elements are populated. NULL if not set.

Game Control

After initialising the system via the constructor or constructor/InitialiseFramework() you are in a graphics state and everything is set up for you. This section contains the data/methods for letting the Framework control your game in a timer based FPS loop. The FPS timer based loop controls the game speed using a target FPS specified in the XML file/constrcutor (e.g. 60 fps) ). The loop is a standard FPS based timer loop you may have seen before. There are two parts to any FPS based loop: first is the logic - this is where you update your system knowing that it will be called at your FPS (hence ensuring the correct game speed). Second is the drawing - this is where you draw your stuff to the drawing buffer.

You write your logic/drawing as if they were called once per frame, where a frame takes up 1/FPS (e.g. 60 FPS means being called 60 times per second). The Animation object is linked to the Frameworks FPS so all updates are handled automatically (as you can specify animations are updated in milliseconds).
Methods/Data Details
bool StartGameLoop(
bool (*logiclogo)(void),
bool (*drawinglogo)(void),
bool (*logicmenu)(void),
bool (*drawingmenu)(void),
bool (*logicgame)(void),
bool (*drawinggame)(void),
bool (*logicexit)(void),
bool (*drawingexit)(void),
bool bUseEscToExit=true );
Calling this method invokes the game control. This function allows you to pass in up to four logic/drawing pairs of functions (you just pass in the name of the function). There are four so that you can separate your code into different parts. The parameter names suggest a typical logo, menu, game, exit series of functions. These names are just arbitrary, it is in code that you determine which to call. You can pass in NULL to show that you aren't using anything for a pair. The first non-null pair from the list is the first logic/drawing functions used in the game loop, i.e. every frame this logic/drawing function you supplied will be called.

When you need to change to another logic/drawing pair, a function is available and is described below. Of course, there is nothing stopping you from simply supplying one logic/drawing pair and doing all the code within. This approach will be similar to a typical FPS based loop, but you lose the ability to separate your code and make it easier to manage.

To quit out of the GameLoop (i.e. exit completely and return to the code that invoked StartGameLoop() return a value of true in the drawing/logic. To continue on inside the loop, return false.

The final parameter bUseEscToExit defaults to true if not set. If set to true it will automatically return true from the logic function when the ESCape key is pressed, to save it being coded in your logic code.
ChangeTimerType(TimerType activeTimerTypeWhen you want to switch the timer loop to another set of functions call this. The parameter can be one of GAME_INTRO, GAME_MENU, GAME_GAME, GAME_EXIT. These constants correspond to the function name pairs you specify in StartGameLoop()
SetAutoGameLoopOverride( bool (*logic)(void), bool (*drawing)(void) );Sometimes within a logic/drawing function you might want to change to a piece of code but it still part of the current logic/drawing loop. For example during a game you have the option to select pause/options. Instead of writing conditional statements and such in the logic/drawing, simply call this function passing in a new function pointer for the logic/drawing. When you wish to return back to the function that called it, you return 'true' from either function.
BITMAP* DrawingSurfaceThis represents the graphics buffer that you draw to. The system ensures it is the correct one every frame and displays it accordingly (i.e. It can be a video, system or memory bitmap and can be show by paging, triple or double buffering. You will need to clear it yourself if need be though.
int TimerCurrentFPS This variable holds the FPS the game is currently achieving. For display purposes only. NOTE: This variable is not part of the Framework class, so to use it in your code it needs to be declared as an 'extern', e.g. "extern volatile int TimerCurrentFPS;"

Helper Methods

Methods Details
DatLoad (DATAFILE** dat, string file, bool stop) Load a datafile from file and assign it to dat. If stop is passed in as true then the program aborts if it fails to load. example:

DATAFILE* myDat; myAllegro->DatLoad(&myDat,"file.dat",false);

MsgBox(const char* msg1,
const char* msg2,
BITMAP* bmp=screen,
bool waitforspace=true);
Outputs a message to either a bitmap if in graphics mode or to the standard output if not. Msg1/msg2 are the two lines of text that can be shown, bmp is where to display it and waitforspace will wait for the SPACE key to be pressed. Defaults are as parameters. Be careful when using paging/triple buffers as you may encounter problems using the 'screen' object. Because you can get it to wait for a key press, if you are drawing to a buffer and not the screen, nothing will be shown!
MsgBox(
const char* line1,
const char* line2,
BITMAP* bmp,
int w,int h,
int transcolour,
int forecolour,
bool transparent,
bool waitforspace);
If more control is required (such as the colours) then use this function call. These two functions are really there as a backup and normally you will either write your own display code or modify this function in the allegro_core.cpp file. Remember that these functions are used by some functions (e.g. AbortSystem).
WaitForKey(int keyp=KEY_SPACE); Flushes the keyboard and waits for a keypress. Defaults to SPACE if nothing passed in.
AbortSystem(string error)This function exits the whole system (freeing all framework resources) and aborting with a message of 'error'. This should only be called on emergencies as it may lead to memory faults such as not freeing up your allocated memory.

Audio Methods

The Framework uses the more advanced voice controls available with Allegro. These methods are available to make things easy. Because the Framework uses the Configurator calling these methods without any proper sound/music devices is fine as full checks are performed.
Methods Details
SoundPlayMidi(MIDI* tune, int vol=-1,bool loopflag=false); Plays a midi tune. Volume can be between 0 and 255 (vol) and can be looped (loopflag). If vol is set to -1 then the default volume is used.
SoundStopMidi(); Stop the current midi from playing. Only one can be playing at once.
SoundPlayDigi(DATAFILE* df, int samplenum,int vol=-1, int loopflag=false); Play a wave file from the passed in datafile, the location of the file within the data file is samplenum parameter. Others as midi parameters. Returns the id of the sample so that you can use it with the other Allegro voice functions.
SoundPlayDigi(SAMPLE* samp, int vol=-1, int loopflag=false); As above but for a sound derived from a SAMPLE object (e.g. loaded from a file).
SoundStopDigi(int voice,bool force); Stop a sample playing, voice parameter is the id as returned by the playdigi method. The sound will be allowed to finish playing if the parameter force is passed in as false
SoundPurgeDigi(bool killit=false); This clears all sound buffers and deallocates the voice. Only a certain number of voices are allowed at once (defaults to 32, but up to 255). This will loop through all voices and purge any that have stopped and can be released - they are not released automatically. If using the framework then this is not required as it is called every 5 seconds.
SoundKillDigi(); Stops all sounds playing, forcing them to stop immediately.
SoundDigiPos(int voice); Returns the position of a sample.
SoundSetStandardVolumeDigi (int val); SoundSetStandardVolumeMusic (int val); Set the default volume of midi and wave files.

Optimisation

Sample Code

The framework has two samples. In the tests\framework\example1 directory is the file main.cpp. This is a mainly textual output simply showing how to use the game control system. In the tests\Animation\example3 directory is main.cpp, this shows the Framework using the Animation system and is a complete example on using the Framework system (except the helper functions for sound). This example is the same as the example1 and example2 in the same directory but shows how it is done using the Framework. Refer to Animation section for more details.

The code walkthrough section disects the main points of these examples and shows how to use the Framework without using the timer loop.

Usage

In your source code you need to include the main header file and use the AXL namespace:

#include "axl_framework.h"; using namespace AXL_Projects;

There is one class, Framework. All constructors create a set of base values. Normally you will create a Framework by passing in the name of an XML file (or string of XML data) for the config file, (optionally) the name of an XML file for the graphics and animation (use "" for no animation file), and whether you want the system to be initialise immediately e.g.

Framework* MyFramework; new Framework("config.xml","animations.xml",true);

The defaults (i.e. if not set) are as the function call above. This will create a Configurator object, an Animation Library object (so you can then use animations/graphics and the Configuration file), initialise Allegro according to the config.xml file and leave the program in a graphics state. It also creates the drawing buffers according to the config.xml file (e.g. triple buffering, double buffering). If you wish to do the Allegro initialisation (e.g. stop people from overwriting width/height) then use the following:

Framework* MyFramework = new Framework("config.xml","animations.xml",false); Configuration* MyConfig=Framework->GetConfiguration(); MyConfig->CapsGraphics.ScrWidth=800; MyConfig->CapsGraphics.ScrHeight=600; MyFramework->InitialiseFramework();

Again, the animation/graphics file can be a blank string to show no graphics are being loaded.

The next thing you'll do is retrieve pointers to your Configuration and Animation objects, e.g.

//these will probably be globals Configuration* MyConfig=GetConfiguration(); AnimationLibrary* MyAnimation= GetGraphicsLibrary();

After this you have everything you need. If you are using the timer based game control they you will call StartGameLoop, which is described in the methods above and in the code walkthrough below.

XML Data

Instead of using filenames to read from, any of the config or animation parameters can be actual XML data. Refer to the Animation and Configuration section for more details.

Using Configurations and Animations

Configurations

To use the configuration file yourself you need a pointer to the Configuration file generated when the Framework was initialised (assuming you used XML - otherwise the pointer returned will be NULL).

Configuration* MyConfig=MyFramework->GetConfiguration();

This then gives you full access to all the data and methods in the Configurator class. Obviously because the Framework has already created and populated a configuration object and initialised Allegro, you can ignore the Configurator constructors, destructors and AllegroStart methods. Similarly in the 'Usage' section you already have the object so you do not need to create a new one. The config filename you pass in to the Framework constructor is the same filename as the one mentioned in the Configurator constructor. Refer to the 'Configurator' main menu for information on using the Configurator.

Animations

To use the graphics and animations you need a pointer to the Animation Library generated when the Framework was initialised (assuming you passed in a XML file for graphics - otherwise the pointer returned will be NULL).

AnimationLibrary* MyAnimation=MyFramework->GetGraphicsLibrary();

Because the Framework has already created and populated the Animation Library, you can ignore the Animation constructor, and the LoadAnimations and DestroyAll methods. Similarly in the 'Usage' section you already have the object so you do not need to create a new one. The graphics filename you pass in to the Framework constructor is the same filename as the one mentioned in the Animation Library constructor. Refer to the 'Animations' main menu for information on using the Animation Library.

Examples

These are found in the tests\framework directory (.\example1 and .\example2). VS2005 and Dev-C++ projects have already been created. If not using either of these two IDE's then follow the installation section to make your own makefile, i.e. include tinyXML library file (or tinyXML source files), the main.cpp in the example directory and axl_framework.cpp. Additionally, example 2 requires axl_animations.cpp and axl_config.cpp.

Internals

This is not an exhaustive list of what and how the Framework works, for that you need to read the code/help file. This provides a list of what the Constructors, Initialise and Game timer loop does, so that you know what happens within allegro.

Custom Bitmap Loader

When graphics are initially loaded they are created using the load_bmp function. After this they are transformed into the correct format (e.g. video, system, memory). If you wish to use another file format, e.g. png, or maybe retrieve the graphic from a different location (e.g. a dat file, a custom file system), or if you wish to apply some kind of transformation (e.g. rotation, lighting, etc) then you can pass in the name of a function you write to the Animation Library constructor. This will receive a std::string as the entry name (which is the XML attribute 'bmp_file') and should return a BITMAP* on exit. Or if it fails, passing back NULL will let the system know that an error occurred. Animation example 4 shows an example of this pointer, but assuming you called the constructor like this:

Framework* fr=new Framework("config.xml","animations.xml", true, MyBitmapCallback);

And in your code you created MyBitmapCallback as:

BITMAP* MyBitmapCallback(const std::string& filename) { BITMAP* tmp=load_bmp(filename.c_str(),NULL); if(tmp) { BITMAP* newtmp=create_bitmap(tmp->w,tmp->h); clear_to_color(newtmp,makecol(255,0,255)); draw_sprite_v_flip(newtmp,tmp,0,0); //bonny lighting! //set_trans_blender(255,0,0,255); //draw_gouraud_sprite(newtmp,tmp,0,0,255,64,192,32); destroy_bitmap(tmp); return newtmp; } else return NULL; }

It will simply pass back a flipped (or gouraud shaded) version. You could easily change this to interrogate the string name and appy different things, e.g. simply return back a bitmap created using load_bmp for most, but for those ending in ROTATExxx (where xxx is a number), you could rotate the bitmap by xxx degrees. You could then simply pass in the same bitmap for each graphic item and let the code do the work.

NOTE: the callback will only be called for graphics that are actually loaded. i.e. it will not be called for subbitmaps (those with the 'issheetgraphic' set to 1) or those that are duplicates (have the same bmp_file entry). Also note, that you should ALWAYS return back a memory bitmap as the transformation to the correct type will be made by the system.

Code Walkthrough

Framework with Game Timer Loop only

We will now walk through the code in tests\framework\example1\main.cpp

This example uses only the Framework with no animations and no interaction with the Configuration object (other than using it to initialise Allegro).

The first 36 lines are simply the header file, namespace (as mentioned earlier) and the declarations for the functions used (as we are not using a separate header file).

The Framework is initialised as per the Usage section: GameFramework=new Framework("config.xml","");

Note that we have not passed in any graphics file. Next follows some error checking (this was omitted in the usage section simply to make the code easier to read):

if(Configuration::GlobalErrorString!="") { delete GameFramework; allegro_message(Configuration::GlobalErrorString.c_str()); } else {

Throughout the framework, configuration, animation the GlobalErrorString is only populated in case of serious errors. So this code deletes the framework object and exits if there was an error.

GameStartup();

This calls a function created later in the code. Reading this function you can see that all it is doing is initialising a few variables and clearing the current drawing buffer.

if( !GameFramework->StartGameLoop( LogicIntro,DrawingIntro, LogicMenu,DrawingMenu, LogicGame,DrawingGame, LogicExit,DrawingExit,true) ) { //GameFramework->DestroyFramework(); delete GameFramework; allegro_message("Failed to run game loop. Check config.log"); } else {

Next the timer based loop is called. If it fails then the framework is deleted. As you can see every function pair is populated. This means that the timer loop once it starts will run at the FPS in the config file and will initially call 'LogicIntro' and 'DrawingIntro'. The code for 'LogicIntro' is as follows:

bool LogicIntro() { if(key[KEY_1]) { GameFramework->ChangeTimerType(GAME_MENU); position+=10; } return false; }

If nothing happens during this loop then false is returned signifying the game should not exit. If the '1' key is pressed then the timer loop function is changed to whatever was set in the 'GAME_MENU'. In this case, it is 'LogicMenu' and 'DrawingMenu'. So on the next loop the timer will call these functions.

Following on you can see that each logic function chains the code along to another piece. During the MENU section, instead of pressing '2' as shown, pressing X will restart the system. During the GAME section, the 'overload' method is called to show what happens when you set a temporary logic/drawing pair. On exiting the override, the GAME code continues as normally. Finally, during the last bit of code, EXIT, the function returns true to signify that the game loop should end. Control then passed back to the main() function which continues on with:

delete GameFramework; allegro_message("Termination of Program ok. Check config.log");

Which deletes the framework. This deletion clears up memory and exits a graphics state.

Framework using Animations

This walkthrough relates to tests\animation\example3\main.cpp and assumes you have read and understood the previous code walkthrough. It also does not mention the animation code in detail as this is fully described in the Animation section. The code is the example as shown in tests\animation\example1 and exampl2. The difference being example3 uses the Framework to control the system. The code within main() is the same as the previous example. Indeed if using the Framework and the timer game loop then the code will usually be the same as this.

GameFramework=new Framework("config.xml","animations.xml");

This uses an animation this time. So on initialisation of the Framework, all graphics and animations are loaded too.

SetupGame();

This initialises and retrieves the configuration and animation/graphics pointers, i.e.

GameLibrary=GameFramework->GetGraphicsLibrary(); GameConfiguration=GameFramework->GetConfiguration();

Using the animation object it then retrieves the first animation, ufo,

MainAnimation=new Animation(GameLibrary,"ufo");

After this, the game timer loop is invoked:

if( !GameFramework->StartGameLoop( NULL,NULL, NULL,NULL, LogicGame,DrawingGame, NULL,NULL,true) )

As you can see only one function pair is called, as there is no need for any other as it is a simple program. The 'ChangeTimerType' method has not been called at any point. This is because the Framework will use the first non-null function pointer it finds as it's first calling point. The LogicGame is just a copy/paste from the code in the previous animation examples and processes key presses. The drawing code, similarly, is the same, except all drawing is sent to the GameFramework->DrawingSurface object.