了解简单的模拟和渲染循环
Understanding simple simulation and rendering loop
这是您如何模拟和渲染视频游戏的示例(伪代码)。
//simulate 20ms into the future
const long delta = 20;
long simulationTime = 0;
while(true)
{
while(simulationTime < GetMilliSeconds()) //GetMilliSeconds = Wall Clock Time
{
//the frame we simulated is still in the past
input = GetUserlnput();
UpdateSimulation(delta, input);
//we are trying to catch up and eventually pass the wall clock time
simulationTime += delta;
}
//since my current simulation is in the future and
//my last simulation is in the past
//the current looking of the world has got to be somewhere inbetween
RenderGraphics(InterpolateWorldState(GetMilliSeconds() - simulationTime));
}
这是我的问题:
我有 40 毫秒的时间来完成外部 'while true' 循环(意味着 25FPS)。
RenderGraphics 方法需要 10 毫秒。所以这意味着我有 30 毫秒的内循环。 UpdateSimulation 方法需要 5 毫秒。其他一切都可以忽略,因为它的值低于 0.1 毫秒。
为了保持在我的 40 毫秒(外循环)时间安排中,我可以将变量 'delta' 设置的最大值是多少?
为什么?
这在很大程度上取决于您想要和需要更新模拟状态和用户输入的频率,考虑到下文提到的限制条件。例如,如果您的游戏包含基于物理行为的内部状态,您将需要一个较小的 delta
以确保移动和碰撞(如果有)在游戏状态中得到正确评估和反映。此外,如果您的用户输入需要细粒度评估和状态更新,您还需要更小的 delta
值。例如,具有模拟用户输入(例如鼠标、操纵杆)的射击游戏将受益于大于 30Hz 的更新频率。如果您的游戏不需要如此频繁地评估输入和游戏状态,那么您可以使用更大的 delta
值,甚至可以在检测到玩家的任何输入时简单地更新您的游戏状态。
在您的特定伪代码中,您的模拟将根据长度为 delta
的固定时间片进行更新,这 要求您的模拟更新在比要模拟的挂钟时间。否则,挂钟时间会比您的模拟时间更新得更快。 这最终会限制您的 delta
,具体取决于 delta
模拟时间的任何模拟更新的实际计算速度。 这种关系还取决于您的用例,可能不会是线性的或恒定的。例如,物理引擎通常会将内部给定的 delta
时间分配给它们可以合理处理的更新速率,因为更长的 delta
时间可能会导致数值不稳定并且更难解决线性系统,从而增加非线性处理工作量.在其他用例中,模拟更新可能需要线性甚至常数时间。即便如此,许多(可能是外部的)事件可能会导致您的模拟更新处理得太慢,如果它本身要求很高的话。例如,在模拟更新期间加载资源,您的操作系统决定将您的执行线程放在一边,用户的另一个进程 运行,反病毒软件启动,低内存压力,缓慢 CPU 和很快。直到现在,我看到的主要是两种策略来规避这个问题或补救它的影响。首先,如果模拟更新工作量较小并且假定减速的原因只是暂时的,则简单地忽略它可能会起作用。这将导致或多或少明显的 "slow motion" 模拟行为,这可能 - 在最坏的情况下 - 导致模拟时间滞后永远堆积。我经常看到的第二种策略是简单地将要模拟的测量帧时间限制为某个人为值,比如 1000 毫秒。一旦减速的原因消失,这会导致平滑的行为,但缺点是 'capped' 模拟时间是 'lost',如果不处理或考虑,可能会导致动画打嗝。要选择一种策略,分析您的用例可能包括测量处理 delta
和 x * delta
时间的模拟更新所需的挂钟时间以及如何更改 delta
时间和模拟负载来处理实际上反映了计算它所需的挂钟时间,这将提示您 delta
的最大值对于您的特定硬件和软件环境是多少。
这是您如何模拟和渲染视频游戏的示例(伪代码)。
//simulate 20ms into the future
const long delta = 20;
long simulationTime = 0;
while(true)
{
while(simulationTime < GetMilliSeconds()) //GetMilliSeconds = Wall Clock Time
{
//the frame we simulated is still in the past
input = GetUserlnput();
UpdateSimulation(delta, input);
//we are trying to catch up and eventually pass the wall clock time
simulationTime += delta;
}
//since my current simulation is in the future and
//my last simulation is in the past
//the current looking of the world has got to be somewhere inbetween
RenderGraphics(InterpolateWorldState(GetMilliSeconds() - simulationTime));
}
这是我的问题:
我有 40 毫秒的时间来完成外部 'while true' 循环(意味着 25FPS)。 RenderGraphics 方法需要 10 毫秒。所以这意味着我有 30 毫秒的内循环。 UpdateSimulation 方法需要 5 毫秒。其他一切都可以忽略,因为它的值低于 0.1 毫秒。
为了保持在我的 40 毫秒(外循环)时间安排中,我可以将变量 'delta' 设置的最大值是多少? 为什么?
这在很大程度上取决于您想要和需要更新模拟状态和用户输入的频率,考虑到下文提到的限制条件。例如,如果您的游戏包含基于物理行为的内部状态,您将需要一个较小的 delta
以确保移动和碰撞(如果有)在游戏状态中得到正确评估和反映。此外,如果您的用户输入需要细粒度评估和状态更新,您还需要更小的 delta
值。例如,具有模拟用户输入(例如鼠标、操纵杆)的射击游戏将受益于大于 30Hz 的更新频率。如果您的游戏不需要如此频繁地评估输入和游戏状态,那么您可以使用更大的 delta
值,甚至可以在检测到玩家的任何输入时简单地更新您的游戏状态。
在您的特定伪代码中,您的模拟将根据长度为 delta
的固定时间片进行更新,这 要求您的模拟更新在比要模拟的挂钟时间。否则,挂钟时间会比您的模拟时间更新得更快。 这最终会限制您的 delta
,具体取决于 delta
模拟时间的任何模拟更新的实际计算速度。 这种关系还取决于您的用例,可能不会是线性的或恒定的。例如,物理引擎通常会将内部给定的 delta
时间分配给它们可以合理处理的更新速率,因为更长的 delta
时间可能会导致数值不稳定并且更难解决线性系统,从而增加非线性处理工作量.在其他用例中,模拟更新可能需要线性甚至常数时间。即便如此,许多(可能是外部的)事件可能会导致您的模拟更新处理得太慢,如果它本身要求很高的话。例如,在模拟更新期间加载资源,您的操作系统决定将您的执行线程放在一边,用户的另一个进程 运行,反病毒软件启动,低内存压力,缓慢 CPU 和很快。直到现在,我看到的主要是两种策略来规避这个问题或补救它的影响。首先,如果模拟更新工作量较小并且假定减速的原因只是暂时的,则简单地忽略它可能会起作用。这将导致或多或少明显的 "slow motion" 模拟行为,这可能 - 在最坏的情况下 - 导致模拟时间滞后永远堆积。我经常看到的第二种策略是简单地将要模拟的测量帧时间限制为某个人为值,比如 1000 毫秒。一旦减速的原因消失,这会导致平滑的行为,但缺点是 'capped' 模拟时间是 'lost',如果不处理或考虑,可能会导致动画打嗝。要选择一种策略,分析您的用例可能包括测量处理 delta
和 x * delta
时间的模拟更新所需的挂钟时间以及如何更改 delta
时间和模拟负载来处理实际上反映了计算它所需的挂钟时间,这将提示您 delta
的最大值对于您的特定硬件和软件环境是多少。