The Game harness is the framework used in the “Old School Games”.
Currently this harness is being developed parallel to the Air War 1945 project and we are working on documenting the code so that it is easy to use for anyone interested in game development on the PS2.
"Old School Games" is:
Fredrik Safstrom ..... bamse
Gianluca
Girelli ..... g0blin
Game Harness Version 1.0
Table of Contents
I. Overview
II. main.cpp
III. ctexture.cpp
IV. csquaresprite.cpp
V. csprite.cpp
I. Overview
The game harness is designed with 2D games in mind; no 3D support is currently implemented.
The main.cpp is the actual harness; this is where you would add your code, it is composed of six major parts; two of them taken straight from H.S. Fortunas site. The major parts are main.cpp, ctexture.cpp, csquaresprite.cpp, csprite.cpp, pad.c and sps2wrap.cpp. The two files pad.c and sps2wrap.cpp files are not commented in this document, see http://www.hsfortuna.pwp.blueyonder.co.uk/ for more documentation.
Ctexture.cpp is the texture class, it will hold your bitmaps which can be uploaded to the GS and used as Textures. The class allocates its own memory for the bitmap and generate it’s own DMA tags for uploading the data to the GS.
Csquaresprite.cpp is a sprite class that is square and cannot be rotated or scaled. The benefit with a square sprite class is the fact that it looks exactly as the square taken from the bitmap. This may not be the case with a sprite. Keep in mind that the square sprite uses the upper left corner of the screen as position 0,0 for the sprite and the upper left corner of the sprite as position 0,0.
Csprite.cpp is a sprite class that can be rotated and scaled, if you want to you can even specify the four corners of the sprite manually enabling you to set pretty much any shape you want to. The sprite uses bi-linear scaling which means that the sprite will not look exactly as the bitmap but it will scale and rotate very well. The sprite class uses the middle of the screen as position 0,0 and the middle of the sprite as position 0,0 for the sprite.
II. main.cpp
This is the actual harness III. ctexture.cppwhere you will add your code.
First you will add your Textures and sprites, this is done by either initialize your sprites immediately or just allocating a pointer. If you have a lot of sprites in your game, I would suggest to dynamically allocate your sprites in arrays. See the Wobbly Sprites demo for one example with sprites in arrays.
CSprite planeSprite(0.0, 0.0, 160, 80, 90, 64) would create your sprite immediately while Csprite *planeSprite would allocate a pointer for your sprite. You later need to create the sprite with planeSprite = new CSprite(0.0, 0.0, 160, 80, 90, 64). When you exit the code you need to clean up the dynamically allocated sprites with delete planeSprite. In the harness the Texture are all allocated dynamically, they will serve as a good example on how to do this dynamically.
Once you have all your sprites and textures declared, you need to initilize them. This is done after int main(void). Leave the code as is until you reach the Textures. Here you would initilize your Textures and Sprites (if you have dynamic ones). Note that if you do not use the CsquareSprite class, CsquareSprite::setSpriteOffset(); can be removed.
After all your sprites and textures have been initilized, the game mode will be set to GAME_MODE_MENU and we will call the MenuInit() function.
You are now ready to enter the main loop, however there is not much here to edit. The only thing you should touch are the background colors, set those depending on what background color you want in the menu, gameplay or game over menu.
The game is divided into Menu, Gameplay and Game Over, the harness will call the appropriate function once per frame. The frame rate is 60 times per second for VESA/NTSC and 50 times per seconds for PAL.
The three main functions in the harness:
void GameMenu(void);
void GamePlaying(void);
void GameOver(void);
The game will always start with the Game Menu mode and it is up to you to switch between the modes.
To switch to game mode:
GameInit();
gameMode = GAME_MODE_PLAYING;
To switch to game over mode:
GameOverInit();
gameMode = GAME_MODE_GAMEOVER;
To switch to menu mode:
MenuInit();
gameMode = GAME_MODE_MENU;
I guess you already figured out that all initilialization for the different modes should be put in the GameInit(), GameOverInit() and MenuInit(). For example, you might want to initilize sprites and upload Textures in these functions.
In the different main functions, you will need to check the pad, print text, update the sprites and render them. If you discover that the game is over or the user pressed start you can also change the game mode.
Printing Text is easiest done with the sps2UPrintf(...) and sps2UPrintfRender() functions. See the SPS2 documentation for more information. Hint, sps2UPrintf(0,"\377808080\376(190,20)Hello"); will print "Hello" at position 192,20 with the color 0x808080, \377 is used to change color and \376(x,y) is used to specify a position.
Checking the pad can be done with the (pad[0].buttons & {Pad_Button}) functions, for example, to see if the left button was pressed, use if (pad[0].buttons & PAD_LEFT) { do_left_button_pressed_things }. The most common buttons are PAD_SELECT, PAD_START, PAD_UP, PAD_RIGHT, PAD_DOWN, PAD_LEFT, PAD_TRI, PAD_CIRCLE, PAD_CROSS and PAD_SQUARE.
Please see the PAD.C and PAD.H for more information on how to use the analog sticks and the rumblers.
Update your sprites, please see the Texture and Sprite sections later on how to do this.
Check to see if you your game is over or the player aborted the game and change to Menu or Game Over mode.
Once the game is over you need to clean up the dynamic allocated Textures and Sprites. This is done in the Cleanup section where the main loop exited. The harness will also shutdown the screen, the SPS2 and the Pads.
III. ctexture.cpp
This class has functionality for loading 24 bit Windows bitmaps, functionality is there to load 8 bit bitmaps as well, however it doesn’t seem to work at this time.
Due to the fact that the class needs a handle from SPS2 (iSPS2Desc) the class is normally dynamically allocated CTexture * TexMenu and later constructed when the handle is known TexMenu = new CTexture(iSPS2Desc).
If the class is not needed globally, the class can be created normally when the handle is known CTexture TexMenu(iSPS2Desc).
Once the Texture class is constructed, you can load the bitmap. Normally you do this by specifying the filename or the filename and a color to use as transparent.
For example, TexMenu->LoadBitmap("intro1945.bmp") or TexMenu->LoadBitmap("intro1945.bmp", 0, 0, 0).
When the bitmap is being loaded into memory the Texture class will allocate memory for the bitmap data and for the necessary DMA chain.
The Texture is now ready to be uploaded to the GS.
This is done with TexMenu->Upload(sps2UScreenGetFirstFreeGSPage(), false).
If the last parameter is specified true, this Texture is intended to be used by CSprite.
If the last parameter is specified false, this Texture is intended to be used by CSquareSprite.
When the Texture is uploaded to the GS, it is also configured to be the default Texture and all sprites used and uploaded to the GS will be using this Texture.
In order to use more than one Texture per frame you need to follow these steps,
Upload the first Texture to the GS with TexMenu1->Upload(sps2UScreenGetFirstFreeGSPage(), false).
Add all sprites used by this Texture to memory sprite1.addSpriteToMem(pPrimitiveMem, 200).
Upload the Sprites and wait for the DAM to finish Manager.FirePacketToGS((char *)Manager.GetPrim1Base(), pPrimitiveMem, (char *)Manager.GetPrim1DmaBase()).
Up load the second Texture to the GS with TexMenu2->Upload(sps2UScreenGetFirstFreeGSPage(), false).
Add all sprites used by this Texture to memory sprite2.addSpriteToMem(pPrimitiveMem, 200).
Upload the Sprites and wait for the DAM to finish Manager.FirePacketToGS((char *)Manager.GetPrim1Base(), pPrimitiveMem, (char *)Manager.GetPrim1DmaBase()).
Functions in the Class:
CTexture(int iSPS2Desc);
Constructor for the class, needs a valid SPS2 handle.
bool CTexture::LoadBitmap(const char * strFilename);
Load a 24-bit bitmap into memory, returns false if the load was unsuccessfull.
bool CTexture::LoadBitmap(const char * strFilename, bool bBlackTransparent, bool bRedAsAlpha);
Load a 24-bit bitmap into memory, returns false if the load was unsuccessfull.
If second parameter is set to true, black will be converted to 100% transparent (Alpha = 128).
If third parameter is set to true, red/2 will be used as the Alpha component.
bool CTexture::LoadBitmap(const char * strFilename, int iRed, int iGreen, int iBlue);
Load a 24-bit bitmap into memory, returns false if the load was unsuccessfull.
Here you specify the color that should be used for transparency.
bool CTexture::LoadBitmap(const char * strFilename, bool bBlackTransparent, bool bRedAsAlpha, int iRed, int iGreen, int iBlue);
Used internally for the previous functions.
void CTexture::Upload(uint32 uGSPage, bool bRotate = true);
Construct a DMA chain and upload the Texture to the memory position specified in the first parameter.
The second parameter specifies “Near Pixel” or “Bi-Linear” scaling/rotation. For CSprite use true and for CSquareSprite, use false.
Note that the DMA chain is only created if there previously was none or if the parameters have changed.
The upload will wait for the DMA to finish with the sps2WaitForDMA() function.
IV. csquaresprite.cpp
This sprite class is used for drawing square sprites that is neither scaled nor rotated. In other words, it is an exact copy of a part of the Texture bitmap. This is probably best used for games like Space Invaders or Frogger where the sprites are the same size and not rotated. Another good example would be to build backgrounds for a game like Pac-Man, Lode Runner or Lemmings where the background is built up of smaller building blocks.
In order to use the Sprite class you first need to make sure that your Texture is set for “Near-Pixel” by specifying the rotate flag to false when you upload your Texture. For example, TexMenu->Upload(sps2UScreenGetFirstFreeGSPage(), false).
Second you need to have a memory pointer for the memory used to store the sprites, to make things easier we use the memory manager in the sps2wrap.cpp.
Third, you add the sprite to the memory, the class will write one GIF tag and one sprite primitive data.
Fourth, after adding all sprites to memory, use the memory manager to fire off the packet to the GS.
Here is one example that will use the Texture TextureGame to draw the sprite gameSprite to the back buffer.
// Upload Texture.
TextureGame->Upload(sps2UScreenGetFirstFreeGSPage(), false);
// Get memory pointer.
pPrimitiveMem = (char *)Manager.GetPrim1Base();
// Add Sprite to memory. Giftag and primitive data.
gameSprite.addSpriteToMem(pPrimitiveMem, 100);
// Send off to GS.
Manager.FirePacketToGS((char *)Manager.GetPrim1Base(), pPrimitiveMem, (char *)Manager.GetPrim1DmaBase());
Functions in the Class:
CSquareSprite( int xTexture, int yTexture, int widthTexture, int heightTexture);
The constructor, this will specify the x and y pos of the Texture (0,0 is the upper left corner of the texture) and the width and height in Pixels. This is how you specify which part of the Texture to use for the sprite.
void CSquareSprite::setPos(int xPos, int yPos);
This is how you set the position of the sprite, note that the 0,0 position of the screen is the upper left corner and that the 0,0 position of the sprite is the upper left corner of the sprite. The screen resolution varies depending on which mode (VESA (640x480), NTSC (640x448) or PAL (640x512)) your screen is using.
Note that sprites drawn outside the visible area will be clipped automatically by the GS.
void CSquareSprite::setTexturePos(int xTexturePos, int yTexturePos);
This is used to animate sprites on the texture. Increasing the x position will move the position on the texture one sprite width to the right. Increasing the y position will move the position on the texture one sprite height down.
For example, if you have a sprite constructed as CSquareSprite gameSprite( 0, 0, 16, 24) and specify gameSprite.setTexturePos( 2, 3), the actual pixel position on the texture would be 2x16=32, 3x24=72.
void CSquareSprite::movePos(int xPos, int yPos);
Move the sprite by adding the two values to the x and y pixel position of the sprite.
void CSquareSprite::setVisible(bool isVisible);
This will specify if the sprite is being drawn on the screen, also a sprite that is not visible will never be able to collide with another sprite.
Note that invisible sprites are not added to the memory when the addSpriteToMem function is called.
void CSquareSprite::setColor(int Red, int Green, int Blue, int Alpha);
Set the color of the sprite; note that pure white is 0x80, 0x80, 0x80, 0x80.
Specifying values above 0x80 (128) will add to the texture color and the sprite will look brighter than on the texture.
bool CSquareSprite::getVisible(void);
Returns true if the sprite is visible.
int CSquareSprite::getXPos(void);
Returns the X position of the sprite.
int CSquareSprite::getYPos(void);
Returns the Y position of the sprite.
int CSquareSprite::getWidth(void);
Returns the Width of the sprite.
int CSquareSprite::getHeight(void);
Returns the Height of the sprite.
bool CSquareSprite::inside(CSquareSprite inSprite);
Simple collision test, this will test the bounding box of the sprite, not the actual pixel collision.
void CSquareSprite::addSpriteToGIFTag(char *& pMem, int Zvalue);
If you want to construct your own GIF tag, you can use this function to add only the primitive data without the GIF tag.
The Zvalue is the value used for the Z-buffer, sprites with higher Z values will be on top of those with lower Z values.
void CSquareSprite::addSpriteToMem(char *& pMem, int Zvalue);
Add the sprite to memory, this will write both GIF Tag and primitive data.
The Zvalue is the value used for the Z-buffer, sprites with higher Z values will be on top of those with lower Z values.
static void CSquareSprite::setSpriteOffset(void);
This is used to calculate the upper left corner of the screen after the screen is initialized and SPS2 knows the screen resolution.
Note that this is a static member and must be called after the screen is initialized (after sps2UScreenInit(0)), you call the function with CsquareSprite::setSpriteOffset().
void CSquareSprite::setCustomInt1(int value);
void CSquareSprite::setCustomInt2(int value);
void CSquareSprite::setCustomInt3(int value);
void CSquareSprite::setCustomInt4(int value);
void CSquareSprite::setCustomInt5(int value);
void CSquareSprite::setCustomFloat1(float value);
void CSquareSprite::setCustomFloat2(float value);
void CSquareSprite::setCustomFloat3(float value);
void CSquareSprite::setCustomFloat4(float value);
void CSquareSprite::setCustomFloat5(float value);
int CSquareSprite::getCustomInt1();
int CSquareSprite::getCustomInt2();
int CSquareSprite::getCustomInt3();
int CSquareSprite::getCustomInt4();
int CSquareSprite::getCustomInt5();
float CSquareSprite::getCustomFloat1();
float CSquareSprite::getCustomFloat2();
float CSquareSprite::getCustomFloat3();
float CSquareSprite::getCustomFloat4();
float CSquareSprite::getCustomFloat5();
Use these varibales to keep track of things related to the sprite, for example, if your UFO or Plane needs three hits to be shot down, use setCustomInt1( getCustomInt1 () + 1); to increase the numbers of hits. Once getCustomInt1() == 3, the UFO/Plane have been shot down.
Or use the setCustomFloat1(); to keep track of speed or distance traveled in a racing game...
This should be easier than keeping track of these in global variables.
void CsquareSprite::animationUpdate(void);
If you want to animat your sprites, call this function every frame.
void CSquareSprite::animationSetTextureDirection(int dir);
Which direction on the Texture is you animation. If dir = 0, it is left to right, if dir = 1, it is from up to down.
This is default to 0.
void CSquareSprite::animationSetFramesInAnimation(int frames);
Set the number of frames in the animation, you need at least two for the animation to work.
void CSquareSprite::animationSetFramePos(int pos);
Set the current position, this is default to 0.
void CSquareSprite::animationSetFrameRepeat(int loop);
If you want the animation to start all over, set this to 1. Otherwise, when the last frame is reached the porgram will set the numbers of frames in the animation to 0, this will end the animation at the last frame.
This is default to 1.
void CSquareSprite::animationSetFrameRepeatBackAndForth(int backAndForth);
This is how the animation will work, if backAndForth = 0, the sequence for a three frame animation will be 0,1,2,0,1,2,0,1,2...
If backAndForth = 1, the sequence for a three frame animation will be 0,1,2,1,0,1,2,1,0,1,2,1...
This is default to 1.
void CSquareSprite::animationSetFrameDelay(int delay);
Set the frame delay for the animation, for VGA and NTSC this is 60 per seconds and for PAL this is 50 per seconds.
For example, a delay of 30 will mean half a second between frames on a NTSC display. To have half a second elay for PAL, use 25.
V. csprite.cpp
This sprite class is used for drawing square sprites that can be either scaled or rotated. This is probably best used for games like Asteroids, Galaga or AirWar 1945 (coming soon to a PS2 near you). If you want to you can even specify the four corners of the sprite manually enabling you to set pretty much any square shape you want to. This functionality can be used in special effects like the wobbly sprite demo.
In order to use the Sprite class you first need to make sure that your Texture is set for “Bi-Linear” by specifying the rotate flag to true when you upload your Texture. For example, TexMenu->Upload(sps2UScreenGetFirstFreeGSPage(), true).
Second you need to have a memory pointer for the memory used to store the sprites, to make things easier we use the memory manager in the sps2wrap.cpp.
Third, you add the sprite to the memory, the class will write one GIF tag and one sprite primitive data.
Fourth, after adding all sprites to memory, use the memory manager to fire off the packet to the GS.
Here is one example that will use the Texture TextureGame to draw the sprite gameSprite to the back buffer.
// Upload Texture.
TextureGame->Upload(sps2UScreenGetFirstFreeGSPage(), false);
// Get memory pointer.
pPrimitiveMem = (char *)Manager.GetPrim1Base();
// Add Sprite to memory. Giftag and primitive data.
gameSprite.addSpriteToMem(pPrimitiveMem, 100);
// Send off to GS.
Manager.FirePacketToGS((char *)Manager.GetPrim1Base(), pPrimitiveMem, (char *)Manager.GetPrim1DmaBase());
Functions in the Class:
CSprite( float xPos, float yPos, int xTexture, int yTexture, int widthTexture, int heightTexture);
The constructor, this will specify the x and y pos on the screen, the x and y pos of the Texture (0,0 is the upper left corner of the texture) and the width and height in Pixels. This is how you specify which part of the Texture to use for the sprite. Note that the 0,0 position of the screen is in the middle of the screen and that the 0,0 position of the sprite is in the middle of the sprite.
void CSprite::setPos(float xPos, float yPos);
This is how you set the position of the sprite, note that the 0,0 position of the screen is in the middle of the screen and that the 0,0 position of the sprite is in the middle of the sprite. The screen resolution varies depending on which mode (VESA (640x480), NTSC (640x448) or PAL (640x512)) your screen is using.
Note that sprites drawn outside the visible area will be clipped automatically by the GS.
void CSprite::setAngle(float Angle);
This is the angle in radians of the sprite, 0.0 is north , pi/2 is east, pi is south and 3/2*pi is west.
void CSprite::addAngle(float Angle);
Add this amount of radians to the rotation.
void CSprite::setPosAll(int xPos1, int yPos1, int xPos2, int yPos2, int xPos3, int yPos3, int xPos4, int yPos4);
Here you can set all four corners independent of each outer for the sprite, see picture below:
Pos1 ------------- Pos2 + + + SPRITE + + + Pos3 ------------- Pos4
Note that position 0,0 is the middle of the screen.
void CSprite::setTexturePosST(float s1, float s2, float t1, float t2);
This is used to set the texture position in percentage units, 0.0, 0.0 is the upper left corner of the texture and 1.0, 1.0 is the lower right corner of the texture.
void CSprite::setTexturePosUV(int u1, int u2, int v1, int v2);
This is used to set the texture position in pixel units, 0, 0 is the upper left corner of the texture and 255, 255 is the lower right corner of the texture. Note that his only works for 256x256 textures in this release.
void CSprite::movePos(float xPos, float yPos);
Move the sprite by adding the two values to the x and y pixel position of the sprite.
void CSprite::moveDirection(float direction);
This will move the sprite in the direction of the Angle in units of pixels.
void CSprite::setVisible(bool isVisible);
This will specify if the sprite is being drawn on the screen, also a sprite that is not visible will never be able to collide with another sprite.
Note that invisible sprites are not added to the memory when the addSpriteToMem function is called.
void CSprite::setColor(int Red, int Green, int Blue, int Alpha);
Set the color of the sprite; note that pure white is 0x80, 0x80, 0x80, 0x80.
Specifying values above 0x80 (128) will add to the texture color and the sprite will look brighter than on the texture.
void CSprite::setTexturePos(int xTexturePos, int yTexturePos);
This is used to animate sprites on the texture. Increasing the x position will move the position on the texture one sprite width to the right. Increasing the y position will move the position on the texture one sprite height down.
For example, if you have a sprite constructed as CSquareSprite gameSprite( 0, 0, 16, 24) and specify gameSprite.setTexturePos( 2, 3), the actual pixel position on the texture would be 2x16=32, 3x24=72.
void CSprite::setScaleX(float scaleX);
Set the scale of the X axis of the sprite, note that the scaling rotates with the angle so that the sprite keeps its shape when rotating. A scale of 1.0 is the original size, 0.5 is half the size and 2.0 is twice as big.
void CSprite::setScaleY(float scaleY);
Set the scale of the Y axis of the sprite, note that the scaling rotates with the angle so that the sprite keeps its shape when rotating. A scale of 1.0 is the original size, 0.5 is half the size and 2.0 is twice as big.
bool CSprite::getVisible(void);
Returns true if the sprite is visible.
float CSprite::getXPos(void);
Returns the X position of the sprite.
float CSprite::getYPos(void);
Returns the Y position of the sprite.
float CSprite::getAngle(void);
Returns tha angle of the sprite.
float CSprite::getSize(void);
Returns the radius of the bounding circle of the sprite (Height/2+Width/2)/2.
float CSprite::getScaleX(void);
The X scale component.
float CSprite::getScaleY(void);
The Y scale component.
int CSprite::getXPos1(void);
int CSprite::getYPos1(void);
int CSprite::getXPos2(void);
int CSprite::getYPos2(void);
int CSprite::getXPos3(void);
int CSprite::getYPos3(void);
int CSprite::getXPos4(void);
int CSprite::getYPos4(void);
Get the positions of the four corners of the sprite.
bool CSprite::inside(CSquareSprite inSprite);
Simple collision test, this will test the bounding circle of the sprite, not the actual pixel collision.
This is pretty much OK if the sprite is square, but not so good if the sprite is oval.
void CSprite::addSpriteToMem(char *& pMem, int Zvalue);
Add the sprite to memory, this will write both GIF Tag and primitive data.
The Zvalue is the value used for the Z-buffer, sprites with higher Z values will be on top of those with lower Z values.
void CSprite::addSpriteToGIFTag(char *& pMem, int Zvalue);
If you want to construct your own GIF tag, you can use this function to add only the primitive data without the GIF tag.
The Zvalue is the value used for the Z-buffer, sprites with higher Z values will be on top of those with lower Z values.
Used internally.
void CSprite::AddGIFTag(char *& pMem, int numSprites);
Write the GIF tag to memory. Used internally.
void CSprite::setCustomInt1(int value);
void CSprite::setCustomInt2(int value);
void CSprite::setCustomInt3(int value);
void CSprite::setCustomInt4(int value);
void CSprite::setCustomInt5(int value);
void CSprite::setCustomFloat1(float value);
void CSprite::setCustomFloat2(float value);
void CSprite::setCustomFloat3(float value);
void CSprite::setCustomFloat4(float value);
void CSprite::setCustomFloat5(float value);
int CSprite::getCustomInt1();
int CSprite::getCustomInt2();
int CSprite::getCustomInt3();
int CSprite::getCustomInt4();
int CSprite::getCustomInt5();
float CSprite::getCustomFloat1();
float CSprite::getCustomFloat2();
float CSprite::getCustomFloat3();
float CSprite::getCustomFloat4();
float CSprite::getCustomFloat5();
Use these varibales to keep track of things related to the sprite, for example, if your UFO or Plane needs three hits to be shot down, use setCustomInt1( getCustomInt1 () + 1); to increase the numbers of hits. Once getCustomInt1() == 3, the UFO/Plane have been shot down.
Or use the setCustomFloat1(); to keep track of speed or distance traveled in a racing game...
This should be easier than keeping track of these in global variables.
void CSprite::animationUpdate(void);
If you want to animat your sprites, call this function every frame.
void CSprite::animationSetTextureDirection(int dir);
Which direction on the Texture is you animation. If dir = 0, it is left to right, if dir = 1, it is from up to down.
This is default to 0.
void CSprite::animationSetFramesInAnimation(int frames);
Set the number of frames in the animation, you need at least two for the animation to work.
void CSprite::animationSetFramePos(int pos);
Set the current position, this is default to 0.
void CSprite::animationSetFrameRepeat(int loop);
If you want the animation to start all over, set this to 1. Otherwise, when the last frame is reached the porgram will set the numbers of frames in the animation to 0, this will end the animation at the last frame.
This is default to 1.
void CSprite::animationSetFrameRepeatBackAndForth(int backAndForth);
This is how the animation will work, if backAndForth = 0, the sequence for a three frame animation will be 0,1,2,0,1,2,0,1,2...
If backAndForth = 1, the sequence for a three frame animation will be 0,1,2,1,0,1,2,1,0,1,2,1...
This is default to 1.
void CSprite::animationSetFrameDelay(int delay);
Set the frame delay for the animation, for VGA and NTSC this is 60 per seconds and for PAL this is 50 per seconds.
For example, a delay of 30 will mean half a second between frames on a NTSC display. To have half a second elay for PAL, use 25.