引入可变时间步长很麻烦

Trouble introducing a variable timestep

本文关键字:麻烦 时间      更新时间:2023-10-16

我目前在大学学习计算机游戏编程,正在用c++创建一个物理引擎。

我被要求通过GetTickCount()方法引入一个时间步进,尽管不准确,因为它适合当前深度,我们要进入时间步进

我的问题是,现在我已经引入了基于时间步长的运动,已经绘制到屏幕上的对象通过更新功能的两个不同的覆盖似乎不起作用。

通过在代码中添加断点,看起来好像mElapsedTime似乎没有传递任何值,我被难住了。

抱歉,如果这篇文章太啰嗦或有些跑题,我试图提供尽可能多的背景,这是我的第一篇文章,任何帮助都是感激的。

(frameStartTime参数只是获取帧更新和绘制之前的滴答计数)

编辑:float类型的失效时间,以便在更新粒子类时更容易乘法(例如pos.x = velocity)。x * timeStep),这就是从unsigned long转换为float的原因,它仍然是多余的吗?

void Simulation::gameLoopDelay(DWORD frameStartTime)
{
    DWORD presetFrameInterval = 16;
    DWORD frameProcessingTime = GetTickCount() - frameStartTime;
    if (frameProcessingTime < presetFrameInterval)
    {
        Sleep(presetFrameInterval - frameProcessingTime);
    }
    mElapsedTime = (float)frameProcessingTime / 1000.0f;
}

我不是绝对确定(因为您没有提供足够的信息),但我看到的第一个问题是您做mElapsedTime = (float)frameProcessingTime / 1000.0f;而不考虑Sleep(...)。换句话说,你忘了把睡眠考虑在内。要做到这一点,您必须在调用Sleep(...)后重新获取GetTickCount()。但即使这样,你的代码也会变得过于样板化,容易出错。

通过编写小的、灵活的、可重用的类来处理你的问题,这些类只做简单的任务:

class Stopwatch {
public:
  Stopwatch(): _start(0), _elapsed(0), _running(false) {}
  void
  start() {
    if (_running)
      return;
    _running = true;
    _start   = GetTickCount();
  }
  void
  stop() {
    if (!_running)
      return;
    _elapsed += GetTickCount() - _start;
    _running  = false;
  }
  void
  reset() {
    _running = false;
    _elapsed = 0;
  }
  void
  restart() {
    _running = true;
    _elapsed = 0;
    _start   = GetTickCount();
  }
  bool
  isRunning() const {
    return _running;
  }
  DWORD
  elapsed() const {
    return _running ? _elapsed + (GetTickCount() - _start) : _elapsed);
  }
  bool
  hasExpired(DWORD interval) const {
    return elapsed() > interval;
  }
private:
  DWORD _start;
  DWORD _elapsed;
  bool  _running;
};

如何利用这个类来满足你的需要是很简单的。

其次,我真的不明白你为什么使用Sleep。我最后一次看到帧延迟的例子是在5年前的一些关于编写玩具游戏的蹩脚教程中。这些内容在老游戏中非常流行,但在现代游戏中却没有意义。当然,如果这是你作业的要求,那么我道歉,但这句话仍然适用。

最后,我想从我自己的实时可视化经验中给你一些额外的建议。永远不要使用GetTickCount或其他Windows API(或Linux API,无关)程序员中流行的原始工具编写依赖于时间的代码,因为它再次散发出创建玩具游戏的过时教程的味道。

你会问,那该用什么呢?现在我们很幸运,因为有了设计良好、可靠的跨平台库,比如Boost。你可能应该熟悉它,但如果你不熟悉,你一定要去做。对于您的特定问题,有一个Boost模块。Chrono是精心设计的,可以与时间一起工作。我已经在几个涉及实时渲染的项目中使用过它,我不得不承认它非常健壮,值得学习。

另一个,库- Boost。单位-这是一个必须编写稳定和准确的物理引擎。这个库为单位(力、质量、速度、加速度等)和各种单位系统模型(例如SI或CGS)的数量提供类型安全支持。它基于模板元编程,因此不会产生运行时开销。

再一次,我理解你的任务可能不允许这样的事情。但是,我相信我在这里提到的一切在将来对你都是有价值的。顺便说一下,这是一个很好的例子,说明为了能够解决现实生活中的问题,你应该比大学作业了解更多。

如果你还需要更多的帮助,特别是你的问题-我需要更多的信息。例如,首先向我们展示游戏循环的全部代码。

我认为,基本上,你需要改变你的代码:

void Simulation::gameLoopDelay(DWORD frameStartTime)
{
    DWORD presetFrameInterval = 16;
    DWORD frameProcessingTime = GetTickCount() - frameStartTime;
    if (frameProcessingTime < presetFrameInterval)
    {
        Sleep(presetFrameInterval - frameProcessingTime);
    }
    frameProcessingTime = GetTickCount() - frameStartTime;
    mElapsedTime = (float)frameProcessingTime / 1000.0f;
}

这不是最建议的方法,因为这里的Sleep可能会锁定整个线程。最好是在分数时间内完成帧,但我认为这将修复您的代码。