7

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

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

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

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

抱歉,如果这篇文章太罗嗦或有些离题,我试图提供尽可能多的上下文,这是我的第一篇文章,感谢任何帮助。

(frameStartTime 参数在更新和绘制帧之前只需要一个滴答计数)

编辑:浮点类型的 mElapsed 时间,以便更容易在粒子类的更新中相乘(例如 pos.x = velocity.x * timeStep),这就是从无符号长转换为浮点的原因,它仍然是多余的吗?

void Simulation::gameLoopDelay(DWORD frameStartTime)
{
    DWORD presetFrameInterval = 16;
    DWORD frameProcessingTime = GetTickCount() - frameStartTime;

    if (frameProcessingTime < presetFrameInterval)
    {
        Sleep(presetFrameInterval - frameProcessingTime);
    }

    mElapsedTime = (float)frameProcessingTime / 1000.0f;
}
4

2 回答 2

1

我认为,基本上,您需要将代码更改为:

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这里可能锁定了整个线程。只在部分时间内完成框架可能会更好,但我认为这将修复您的代码。

于 2013-03-19T20:11:21.293 回答
1

我不确定(因为你没有提供足够的信息),但我看到的第一个问题是你mElapsedTime = (float)frameProcessingTime / 1000.0f;不管Sleep(...). 换句话说,你忘了考虑睡眠。为此,您必须GetTickCount()在调用Sleep(...). 但即便如此,您的代码也会变得过于样板且容易出错。

通过编写执行简单任务的小型、灵活且可重用的类开始解决您的问题:

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

再一次,我明白你的任务可能不允许这样的事情。但是,我相信我在这里提到的所有内容将来都会对您有价值。顺便说一句,这是一个很好的例子,说明你应该知道多少比大学作业提供的知识能够解决现实生活中的问题。

如果您仍然特别需要有关您的问题的更多帮助 - 我需要更多信息。例如,首先向我们展示游戏循环的整个代码。

于 2013-03-19T20:12:12.807 回答