可变时间步和重力/摩擦力

时间:2011-12-18 22:48:28

标签: game-physics lwjgl slick2d 2d-games

我正在尝试复制Sonic physics engine的逻辑,这是为固定时间步系统(60 FPS)编写的,在一个可变的时间步长(Slick2D,确切地说)。

在按下跳跃按钮的原始状态下,玩家的velocity.y设置为-6.5,每个刻度0.21875被添加到velocity.y以模拟重力。

每次调用我的逻辑更新时,都会传递一个时间delta参数,指定已经过了多少毫秒。如果超过我预期的毫秒数,那么我重复更新逻辑,如果我们正在处理目标帧的“余数”,则传递一个最多为1或更小的“内部增量”。

E.g。如果我们期望一个帧需要16ms,而 花费16ms,则循环将迭代一次并将thisMiniTick传递为1.如果delta不是16ms而是40ms,则循环将执行三次,传递1,1,最后传递0.5。

我错误地认为在每个内部更新循环中我都可以velocity.y += (gravity * thisMiniTickRelative),但这不起作用。在较快的帧速率上,不会施加足够的重力导致更高的跳跃,而在较慢的帧速率上,跳跃较低(尽管不会明显接近)。

有没有一种方法可以用于几乎所有的帧速率,或者我是否必须设置delta的上限和下限?

'内部更新'循环:

    float timeRemaining = delta/1000f; 
    while(timeRemaining > 0)
    {
        float thisMiniTick = Math.min(timeRemaining, 1f / FRAMES_PER_SECOND);
        float thisMiniTickRelative = thisMiniTick / (1f / FRAMES_PER_SECOND);

        updateInput(container, game, thisMiniTickRelative);
        if (playerAirState)
        {
            playerVelocity.y += (GRAVITY * thisMiniTickRelative);
        }
        clampPlayerVelocity();
        playerPosition.add(playerVelocity);
        doCollisions();
        timeRemaining -= thisMiniTick;
    }

1 个答案:

答案 0 :(得分:2)

不要将其视为“设置delta的上限和下限”。您的应用程序和线程受制于应用程序的操作系统调度时间,以及系统的所有其他要求,以及您需要注意的事项。与我们从单任务操作系统转向多任务操作系统的那一天相比,这一挑战与PC游戏一样悠久。

使用Slick,您可以(并且应该)断开逻辑更新与渲染更新的连接,这就是delta值在您的应用程序中传递的原因。使用.setMinimumLogicUpdateInterval and .setMaximumUpdateInterval方法执行此操作。

在我参与过的项目中,包括Slick中的一个项目,我发现每秒30-60次逻辑更新中的任何内容(更新之间的30.3毫秒到16.6毫秒)效果很好,并且为您提供所需的平滑度你的运动,物理和碰撞计算。

字面意思是,对于每秒30-60个逻辑更新,您需要执行以下操作:

container.setMinimumLogicUpdateInterval(16);  // max 60 logic updates per second
container.setMaximumLogicUpdateInterval(31);  // min 30 logic updates per second

此外,尝试计算timeRemaing值是一个常见错误,但您不想这样做。你只想通过多少时间来增加移动量。如果已经过了30毫秒,大约是1/33秒,那么你应该将你的游戏对象移动1秒钟内移动量的1/33。

float timeElapsed = delta/1000f;

playerVelocity.y += (GRAVITY * timeElapsed);

如上所述设置上限/下限,您确定timeElapsed始终是0.030.06之间的值。如果您的游戏陷入困境并且帧速率变慢,那么您的逻辑更新仍然不会超出这些限制。相反的是,整个游戏似乎会变慢(就像它应该一样,就像在Sega那个日子里屏幕上有太多的东西一样),但是碰撞和物理计算仍然可以按预期工作。