0

我正在尝试在可变时间步长(准确地说是 Slick2D)中复制为固定时间步长系统(60 FPS)编写的Sonic 物理引擎的逻辑。

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

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

例如,如果我们预计一帧需要 16 毫秒,而它确实需要 16 毫秒,则循环将迭代一次并thisMiniTick作为 1 传递。如果增量不是 16 毫秒而是 40 毫秒,则循环将执行 3 次,传递 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;
    }
4

1 回答 1

2

不要将其视为“为”设置上限和下限delta。您的应用程序和线程取决于您的应用程序的操作系统调度时间,以及对系统的所有其他要求,以及您只需要注意的一些事情。这一挑战在 PC 游戏中与我们从单任务操作系统转向多任务操作系统的那一天一样古老。

使用 Slick,您可以(并且应该)断开逻辑更新与渲染更新的连接,这就是delta值在应用程序中传递的原因。通过使用.setMinimumLogicUpdateInterval 和 .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.03和之间的值0.06。如果您的游戏陷入困境并且您的帧速率变慢,您的逻辑更新仍然不会超出这些界限。取而代之的是,整个游戏似乎会变慢(就像在过去的 Sega 时代一样,屏幕上有太多内容),但碰撞和物理计算仍将按预期工作。

于 2011-12-19T02:45:35.233 回答