Sunday, January 10, 2016

grid - Tile based smooth snake movement


while making my own Snake implementation I decided to use delta time for smoothing movement on screen. The problem is that before Snake was moving fixed amount of pixels every frame, but now when mulitplying snakeSpeed by timeStep it's no longer aligned to the grid. How can I fix it?



Main game function:


void Run()
{
while (!gameOver)
{
Time = (float)al_get_time() * 1000.0;
timeStep = Time - lastFrameTime;

Input();


if (draw && al_is_event_queue_empty(eventQueue))
{
al_clear_to_color(al_map_rgb(255, 255, 255));

UpdateSnake(&snake, timeStep, direction);
Draw();
Logic();

al_flip_display();
draw = false;

}
}
}

UpdateSnake() function (for now it's only one element moving):


void UpdateSnake(Snake *snake, float timeStep, Position direction)
{
snake->animationTime += timeStep;

float f = snake->animationTime * snakeSpeed;


if (f >= 1.0f)
{
snake->animationTime = 0.0f;
f = 1.0f;
}

snake->animationOrigin.x = ((int)snake->position.x / BOARD_CELL) * BOARD_CELL;
snake->animationOrigin.y = ((int)snake->position.y / BOARD_CELL) * BOARD_CELL;


if (direction.x == 1)
{
snake->animationDestination.x = snake->animationOrigin.x + BOARD_CELL;
snake->animationDestination.y = snake->animationOrigin.y;
}

if (direction.x == -1)
{
snake->animationDestination.x = snake->animationOrigin.x - BOARD_CELL;
snake->animationDestination.y = snake->animationOrigin.y;

}

if (direction.y == 1)
{
snake->animationDestination.x = snake->animationOrigin.x;
snake->animationDestination.y = snake->animationOrigin.y + BOARD_CELL;
}

if (direction.y == -1)
{

snake->animationDestination.x = snake->animationOrigin.x;
snake->animationDestination.y = snake->animationOrigin.y - BOARD_CELL;
}

snake->position.x = Lerp(snake->animationOrigin.x, snake->animationDestination.x, f);
snake->position.y = Lerp(snake->animationOrigin.y, snake->animationDestination.y, f);

}

Answer



Variable timestep is imprecise due to the floating point arithmetics, so by constant accumulation you add up the errors as well.



To avoid this, you can define start and the end points and with the use of linear interpolation you will achieve smooth transition between them.


Here's the code solution. It requires you to extend your snake class with few elements:


float anim_time; - how much time passed since the animation start


Position anim_origin; - position of the previous cell


Position anim_dest; - position of the destination cell


float lerp(float a, float b, float f)
{
return a + f * (b - a);
}


void UpdateSnake(Snake *snake, float timeStep)
{
snake->anim_time += timeStep;

float f = min(anim_time*snakeSpeed, 1.0f);//min clamps interpolation factor to (-inf; 1]
snake->position.x = lerp(snake->anim_origin.x, snake->anim_dest.x, f);
snake->position.y = lerp(snake->anim_origin.y, snake->anim_dest.y, f);
}

Note that you will have to reset anim_time to zero and calculate start and destination points every time you move to the next cell.



Also, instead of snake->anim_time += timeStep; you can store animation start time and use snake->anim_time = current_time - anim_time_start;


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...