使用嵌套的渲染循环是否不好? 我正在把一个游戏从Ruby移植到C++。有一个主渲染循环,用于更新和绘制内容。现在让我们假设在游戏中,你想在另一个屏幕上选择一个项目。在原始代码中执行此操作的方式是执行Item Item=getItemFromMenu()getItemFromMenu是一个打开菜单的函数,它有自己的更新/渲染循环,这意味着在播放器打开另一个屏幕的整个过程中,您处于嵌套的渲染循环中。我觉得这是一个糟糕的方法,但我不知道为什么。另一方面,它非常方便,因为我只需要一个函数调用就可以打开菜单,因此代码是本地化的。 你知道这是不是一个糟糕的设计吗? 我犹豫是否将其发布在gamedev上,但由于这主要是一个设计问题,我将其发布在这里

使用嵌套的渲染循环是否不好? 我正在把一个游戏从Ruby移植到C++。有一个主渲染循环,用于更新和绘制内容。现在让我们假设在游戏中,你想在另一个屏幕上选择一个项目。在原始代码中执行此操作的方式是执行Item Item=getItemFromMenu()getItemFromMenu是一个打开菜单的函数,它有自己的更新/渲染循环,这意味着在播放器打开另一个屏幕的整个过程中,您处于嵌套的渲染循环中。我觉得这是一个糟糕的方法,但我不知道为什么。另一方面,它非常方便,因为我只需要一个函数调用就可以打开菜单,因此代码是本地化的。 你知道这是不是一个糟糕的设计吗? 我犹豫是否将其发布在gamedev上,但由于这主要是一个设计问题,我将其发布在这里,c++,C++,编辑:一些伪代码给你一个想法: 代码主要部分中的常见循环: while(open) { UpdateGame(); DrawGame(); } 现在,在UpdateGame()中,我将执行以下操作: if(keyPressed == "I") { Item& item = getItemFromInventory(); } UpdateGame() { switch(gameState) { case INVENTORY:

编辑:一些伪代码给你一个想法:

代码主要部分中的常见循环:

while(open) {
   UpdateGame();
   DrawGame();
}
现在,在UpdateGame()中,我将执行以下操作:

if(keyPressed == "I") {
   Item& item = getItemFromInventory();
}
UpdateGame()
{
    switch(gameState)
    {
        case INVENTORY:
            UpdateInventory();
            break;

        case MAIN:
        default:
            UpdateMain();
            break;
    }
}
getItemFromInventory()


处理这种情况的一个好方法是将
DrawInventory()
调用替换为
InvalidateInventory()
之类的调用,这样会将资源清册的当前图形状态标记为过期,并请求在下一帧渲染期间重新绘制(这将在主循环到达
DrawGame()
后不久发生)

这样,你可以继续运行主循环,但是屏幕上唯一被重新绘制的部分是那些已经失效的部分,在正常的游戏中你可以使你的(2/3)失效D环境作为处理的正常部分,但在库存中,您始终可以仅将库存资产标记为需要重新绘制,从而将开销降至最低

如果使用标志指示当前游戏状态,则内部循环的另一部分,
UpdateInventory()
,可以是
UpdateGame()
的一部分,例如:

if(keyPressed == "I") {
   Item& item = getItemFromInventory();
}
UpdateGame()
{
    switch(gameState)
    {
        case INVENTORY:
            UpdateInventory();
            break;

        case MAIN:
        default:
            UpdateMain();
            break;
    }
}
如果确实需要,也可以将此应用于图形:

DrawGame()
{
    switch(gameState)
    {
        case INVENTORY:
            DrawInventory();
            break;

        case MAIN:
        default:
            DrawMain();
            break;
    }
}

但是我认为绘图应该被封装,你应该告诉它需要绘制屏幕的哪个部分,而不是游戏的哪个单独区域。

你用嵌套的渲染循环创建的功能是一个状态机(就像大多数游戏渲染循环一样)。嵌套循环的问题是,很多时候,您希望在嵌套循环中执行与外部循环相同的操作(处理输入、处理IO、更新调试信息等)

我发现最好使用一个渲染循环并使用有限状态机(FSM)来表示您的实际状态。您的状态可能如下所示:

while (!LeaveGame()) {
  input = GetInput();
  timeInfo = GetTimeInfo();

  StateMachine.UpdateCurrentState(input, timeInfo);
  StateMachine.Draw();
}
  • 主菜单状态
  • 选项菜单状态
  • 库存状态
  • 世界观状态
您可以连接状态之间的转换以在它们之间移动。播放器单击按钮可能会触发可以播放动画或其他内容的转换,然后移动到新状态。使用FSM,您的循环可能如下所示:

while (!LeaveGame()) {
  input = GetInput();
  timeInfo = GetTimeInfo();

  StateMachine.UpdateCurrentState(input, timeInfo);
  StateMachine.Draw();
}
一个完整的FSM对于一个小游戏来说可能有点重,所以你可以使用一堆游戏状态来尝试一个简化的状态机。每次用户做一个动作来转换到一个新的状态时,你都会将状态推到一个堆栈上。同样,当他们离开一个状态时,你会将其弹出。通常只有堆栈顶部接收输入和打开的其他项目堆栈可能会/可能不会绘制(取决于您的偏好)。这是一种常见的方法,根据您与谁交谈,它有一些优点和缺点


最简单的选择就是在其中加入一个switch语句来选择要使用的渲染函数(类似于darvids0n的答案)。如果你正在编写一个街机克隆或一个小益智游戏,那就很好了。

我在一个VB.NET应用程序中遇到了一个类似的现实,该应用程序在“进度窗口"这是一种渲染循环,可以防止GUI冻结、崩溃和死机,但也可以避免引入线程的肮脏。这意味着一次只能更新我的一个进度窗口。我真的不介意,它可以工作,并且可以避免线程。但这感觉很糟糕。我暂时离开了它……我看到的一个不好的事情是p在你有这样一个游戏循环的所有地方都可能有重复的代码,这取决于它是如何完成的。你能为你所想的显示伪代码吗?@LightnessRacesinOrbit:你也可以通过使用GUI调度循环来避免线程,同时避免阻塞。例如,每次执行一个步骤,在循环i时安排另一个步骤s还根据需要重新绘制GUI..否?尽管这比创建一些后台工作线程要复杂得多。@Vlad:我不确定是否得到了“计划另一个步骤”部分。