1
\$\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\$
  • \$\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 '19 at 0:59
  • \$\begingroup\$ @Ocelot a little more detail and that could make a good answer. ;) \$\endgroup\$ – DMGregory Sep 2 '19 at 1:04
  • \$\begingroup\$ @Ocelot yeah that would be great :) \$\endgroup\$ – wixy0 Sep 2 '19 at 1:07
  • \$\begingroup\$ your snake movement never should be bigger than a cell size \$\endgroup\$ – virtouso Sep 14 '19 at 18:57
0
\$\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\$
  • \$\begingroup\$ Thank you for help! Didn't really know about this lerp algorithm. \$\endgroup\$ – wixy0 Sep 2 '19 at 1:59
  • \$\begingroup\$ @wixy0 if everything works as it should, don't forget to mark the answer as accepted. \$\endgroup\$ – Ocelot Sep 2 '19 at 2:01
  • \$\begingroup\$ of course, I'm new here so thanks for mentioning that \$\endgroup\$ – wixy0 Sep 2 '19 at 2:24
  • \$\begingroup\$ @wixy0 and how am I supposed to know if you implemented it right? Update your question. \$\endgroup\$ – Ocelot Sep 2 '19 at 3:55
  • \$\begingroup\$ @wixy0 you're supposed to reset animation time every time you finish animation. \$\endgroup\$ – Ocelot Sep 2 '19 at 4:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.