// Represents different types of elements that can be positioned in the game world
struct SMovingObject {
int x, z; // Coordinates in tile map
SVector2 PositionDeltas; // The position inside the tile
float Direction; // rotation angle in radians
float Speed; // cells/second
};
// Inherits members from SMovingObject for storing position and physics information
struct SCharacter : public SMovingObject
{ // Inheritance prevents duplicating code and facilitate higher-level constructs
SCharacterPoints MaxPoints; // the maximum game points that can be set to a particular character
SCharacterPoints CurrentPoints; // the current value of the character points, updated on game events
};
// Also inherits members from SMovingObject for storing position and physics information
struct SShot : public SMovingObject
{
int Damage; // Value that will be substracted from the character's health points
};This makes all members declared in SMovingObject to be part of SCharacter and SShot:
SShot myShot;
myShot.Speed = 10; // Speed is originally from SMovingObject
SCharacter myChar;
myChar.Direction = 0.0f; // the same as DirectionWe can now add a new member into our game struct in order to store the shot list:
struct SGame
{
SMap Map;
SCharacter Player;
std::vector<SCharacter> Enemies;
std::vector<SShot> Shots; // Declare the shot list
};We're going to need some code to setup a new shot and release it into the game world:
void shoot( SGame* gameObject, SPoint2 origin, float direction )
{
SShot newShot;
newShot.Damage = 10; // 10 HP
newShot.Speed = 10.0f; // 10 tiles per second
newShot.PositionDeltas = origin; // the position where the shot was generated
newShot.Direction = direction; // the direction of the shot trajectory
newShot.x = newShot.z = 0; // reset this so it's updated with refreshPosFromDeltas()
refreshPosFromDeltas( &newShot ); // update tile coordinates
gameObject->Shots.push_back( newShot ); // copy as new element at the end of the shot list
}The update() step will also require a new function for updating position of the world shots, handle hits to enemies and remove shots that get outside the map. In order to remove the shots from the list, we're going to use the std::vector::iterator which is like a cursor of the vector. I would never use std::vector but it's good for beginners. The code for removing a shot from the list would look something like the one below:
std::vector<SShot>::iterator iShot = gameObject->Shots.begin(); // get iterator at the begin of the list
int indexShot = 0; // we still need to keep track of the index
while( iShot != gameObject->Shots.end() ) // while the iterator didn't reach the end
{
SShot* currentShot = &gameObject->Shots[indexShot];
// remove shot if reached the end of the screen if( currentShot->x < 0 || currentShot->z < 0
|| currentShot->x >= gameObject->Map.Width || currentShot->z >= gameObject->Map.Depth
)
{
iShot = gameObject->Shots.erase( iShot ); // get valid iterator
continue; // keep at the current index
} iShot++; // move iterator to the next position
indexShot++; // next position means next index }
We can also use similar code to remove the shot if it hit an enemy:
if( gameObject->Map.EnemyCells[currentShot->z][currentShot->x] != INVALID_ENEMY )
{ // damage enemy and remove shot
gameObject->Enemy[gameObject->Map.EnemyCells[currentShot->z][currentShot->x]].CurrentPoints.HP -= iShot->Damage;
gameObject->Player.CurrentPoints.XP += iShot->Damage;
iShot = gameObject->Shots.erase( iShot );
continue; // keep at the current position/index
}or to remove the enemy when it reached 0 health:
if( currentEnemy->CurrentPoints.HP <= 0 )
{ // remove enemy if zero health
gameObject->Player.CurrentPoints.XP += iEnemy->MaxPoints.HP;
iEnemy = gameObject->Enemy.erase( iEnemy );
continue; // keep at the current enemy index
}
Now we're only missing to update the shot position from the direction and speed and time. For this we define a basis vector (x=1, y=0) and rotate it by the angle to get the final direction:
// Calculate direction vector
SVector2 dirVector = SVector2(1, 0).Rotate( currentShot->Direction ); // integrate speed*time*direction shotDeltas->x += currentShot->Speed*fLastFrameTime*dirVector.x;
shotDeltas->y += currentShot->Speed*fLastFrameTime*dirVector.y;
// refresh cell coordinates now that we have accumulated the distance
refreshPosFromDeltas( currentShot );
I also decided we can change the display if the player win or lost and I took as reference the player health and the amount of enemies to not overcomplicate things, but the right way of doing it is to have a variable to keeps track of the game states, so you must NOT do things like this:
// Use this function to draw our game data
void draw( const SGame* gameObject ){
printf("- draw() called.\n");
if( gameObject->Player.CurrentPoints.HP <= 0 )
drawGameOver(false); // lose
else if( gameObject->Enemy.size() == 0 )
drawGameOver(true); // win
else
{
drawASCIIMap( gameObject );
}
drawASCIIGameInfo( gameObject );
};You can find the complete working code for this tutorial and other tutorials in the repository at google code. I hope you liked the game we just wrote -a program which can be written in one or two days if you're experienced in programming-. In the following tutorial series I will be explaining how to create a proper Windows window and add some nice 2D sprite animations to this game.
No hay comentarios:
Publicar un comentario