float mySpeed = 4.5678f; // units per second By taking advantage of the frame time information received at the update() function, we can move our character the fraction corresponding to that time instead of moving it an entire cell.
if( GetKeyState(VK_UP) )
player->y -= player->Speed*fLastFrameTime; // decrease by speed*time as we learned in high schoolThe problem with our previous implementation is that our x and y coordinates are declared as integer types for using as array indices, which causes all the information after the decimal point to get lost:
float mySpeed = 4.5678f; // units per second int myIntegerSpeed = mySpeed; // myIntegerSpeed will store only the integer value 4 and the decimal information such as 0.5678 will be lost. So in order to circumvent this we will add a new structure for holding continuous space coordinates like this one:
struct SPoint2D
{
float x, y;
};typedef SPoint2D SVector2D; // Gives our structure another name (SVector2D)We can now use this structure inside our character structure to store the distance between the cell origin (x=0 , y=0) to the player position, which will always be less than 1 because if we reached 1 of one cell it means that we are in the 0 of the next cell (because 1 is the size of a cell). The structure will then look similar to this one:
struct SCharacter // holds character data
{
int x, z; // Coordinates in tile map
SVector2D PositionDeltas; // location inside the (x, z) cell, being (0.5, 0.5) the center of the cell
float Speed; // cells per second /* other character variables follow... */};We can then configure some speed for the enemies during the setup() step:
// use this function to initialize enemies void setupEnemies( SGame* gameObject )
{
#define INITIAL_ENEMY_COUNT 5
for( int iEnemy=0; iEnemy < INITIAL_ENEMY_COUNT; iEnemy++ )
{
SCharacter newEnemy;
newEnemy.Speed = float(iEnemy); // generate the speed from the enemy index
newEnemy.PositionDeltas = {0,0}; // reset this to a valid value between 0 and 1
/* other setup code... */And also to our player character:
// Use this function to setup player at level startup.
void setupPlayer( SGame* gameObject )
{
gameObject->Player.PositionDeltas = {0,0};
gameObject->Player.Speed = 5.0f;
/* other player initialization code... */Now that we have our data structures set up and the values initialized, we can start using them during the update() step to calculate the movement that happened in the time step of the frame:
void updatePlayer( SGame* gameObject, float fLastFrameTime )
{
SVector2D* playerDelta = &gameObject->Player.PositionDeltas; // get memory address of our data (pointer to data)
float fSpeed = gameObject->Player.Speed; // get player speed
// Update position from speed*time
if(GetAsyncKeyState(VK_UP))
playerDelta->y -= fSpeed*fLastFrameTime; // decrease by speed*time
if(GetAsyncKeyState(VK_DOWN))
playerDelta->y += fSpeed*fLastFrameTime; // increase by speed*time
if(GetAsyncKeyState(VK_RIGHT))
playerDelta->x += fSpeed*fLastFrameTime; // increase by speed*time
if(GetAsyncKeyState(VK_LEFT))
playerDelta->x -= fSpeed*fLastFrameTime; // decrease by speed*time
// refresh tile coordinates after accumulating the distance walked
refreshPosFromDeltas( &gameObject->Player );}We define an auxiliary function that will update the x, z coordinates of the tile for each unit accumulated in our deltas after calculating the displacement from the time. Then if we have x = 10 and deltas.x = 2.5f after accumulating the displacement, we shall increase x and decrease deltas.x by one until x = 12 and deltas.x = 0.5:
void refreshPosFromDeltas( SCharacter* character )
{
SVector2D* charDeltas = &character->PositionDeltas; // get pointer to deltas
// Update X coordinate until deltas.x < 1
while( charDeltas->x >= 1.0f )
{
character->x += 1.0f; charDeltas->x -= 1.0f;
}
// Update X coordinate until deltas.x >= 0
while( charDeltas->x < 0.0f )
{
character->x -= 1;
charDeltas->x += 1.0f;
}
/* Do the same for the Y coordinate */}We also need to implement these changes in the code for the enemy updates:
for( unsigned int iEnemy=0; iEnemy < gameObject->Enemy.size(); iEnemy++ )
{
SCharacter* currentEnemy = &gameObject->Enemy[iEnemy]; // get pointer to character
SVector2D* enemyDelta = ¤tEnemy->PositionDeltas; // get pointer to deltas to be updated
float fEnemySpeed = currentEnemy->Speed; // get enemy speed
if( gameObject->Player.x < currentEnemy->x )
enemyDelta->x -= fEnemySpeed*fLastFrameTime; // decrease speed*time
else if( gameObject->Player.x > currentEnemy->x )
enemyDelta->x += fEnemySpeed*fLastFrameTime; // increase speed*time
if( gameObject->Player.z < currentEnemy->z )
enemyDelta->y -= fEnemySpeed*fLastFrameTime; // decrease speed*time
else if( gameObject->Player.z > currentEnemy->z )
enemyDelta->y += fEnemySpeed*fLastFrameTime; // increase speed*time
// refresh cell coordinates now that we have accumulated the distance walked
refreshPosFromDeltas( currentEnemy );
/* more enemy update code follows */ This will be enough for our game characters to move smoothly depending on the actual time elapsed between frames instead of moving a whole cell on each update, giving more realism to the movement of the characters and making the game more enjoyable.
The complete code and binaries for this tutorial can be found in the repository at google code. In the next tutorial we will be adding a persistent direction system and our vector struct in order to set a base to add bullets and a improve the character controls and enemy AI. See you there!
No hay comentarios:
Publicar un comentario