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