3
\$\begingroup\$

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);

}
\$\endgroup\$
4
  • \$\begingroup\$ Linear interpolation is better than constant accumulation. Just interpolate snake's position between the cells. Misalignment comes from the floating point precision sorcery. lerp(prev_pos, curr_pos, 1.0f/lerp_duration*lerp_time) \$\endgroup\$
    – Ocelot
    Sep 2, 2019 at 0:59
  • \$\begingroup\$ @Ocelot a little more detail and that could make a good answer. ;) \$\endgroup\$
    – DMGregory
    Sep 2, 2019 at 1:04
  • \$\begingroup\$ @Ocelot yeah that would be great :) \$\endgroup\$
    – wixy0
    Sep 2, 2019 at 1:07
  • \$\begingroup\$ your snake movement never should be bigger than a cell size \$\endgroup\$
    – virtouso
    Sep 14, 2019 at 18:57

1 Answer 1

2
\$\begingroup\$

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;

\$\endgroup\$
12
  • \$\begingroup\$ Thank you for help! Didn't really know about this lerp algorithm. \$\endgroup\$
    – wixy0
    Sep 2, 2019 at 1:59
  • \$\begingroup\$ @wixy0 if everything works as it should, don't forget to mark the answer as accepted. \$\endgroup\$
    – Ocelot
    Sep 2, 2019 at 2:01
  • \$\begingroup\$ of course, I'm new here so thanks for mentioning that \$\endgroup\$
    – wixy0
    Sep 2, 2019 at 2:24
  • \$\begingroup\$ @wixy0 and how am I supposed to know if you implemented it right? Update your question. \$\endgroup\$
    – Ocelot
    Sep 2, 2019 at 3:55
  • \$\begingroup\$ @wixy0 you're supposed to reset animation time every time you finish animation. \$\endgroup\$
    – Ocelot
    Sep 2, 2019 at 4:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .