我正在将游戏从Ruby移植到C ++。有一个主渲染循环,用于更新和绘制内容。现在让我们说在游戏过程中,你想要选择一个项目另一个屏幕。在原始代码中完成的方式是Item item = getItemFromMenu();
getItemFromMenu
是一个函数,它将打开菜单并拥有自己的更新/渲染循环,这意味着玩家在整个过程中都有这个其他屏幕打开,你在一个嵌套的渲染循环。我觉得这是一个糟糕的方法,但我不确定为什么。另一方面,它非常方便,因为我可以通过一个函数调用打开菜单,因此代码已本地化。
不知道这是不是一个糟糕的设计?
我对在gamedev上发布它犹豫不决,但由于这主要是一个设计问题,我在这里发布了
编辑:一些伪代码给你一个想法:
代码主要部分的常用循环:
while(open) {
UpdateGame();
DrawGame();
}
现在在UpdateGame()里面我会做类似的事情:
if(keyPressed == "I") {
Item& item = getItemFromInventory();
}
getItemFromInventory()
:
while(true) {
UpdateInventory();
if(item_selected) return item;
DrawInventory();
}
答案 0 :(得分:2)
处理类似这样的事情的好方法是将DrawInventory()
调用替换为类似InvalidateInventory()
的调用,这会将库存的当前图形状态标记为过时,并请求在重新绘制过程中重新绘制下一帧渲染(主循环到达DrawGame()
后很快就会发生)。
通过这种方式,您可以继续在主循环中运行,但是屏幕上唯一被重新查看的部分是已经失效的部分,并且在正常游戏过程中,您可以使您的(2/3)D无效环境作为处理的正常部分,但随后在库存中,您始终只能将库存资产标记为需要重新绘制,从而最大限度地减少开销。
如果使用标志来指示当前的游戏状态,则内循环的另一部分UpdateInventory()
可以是UpdateGame()
的一部分,如:
UpdateGame()
{
switch(gameState)
{
case INVENTORY:
UpdateInventory();
break;
case MAIN:
default:
UpdateMain();
break;
}
}
如果你真的想要,你也可以将它应用于绘图:
DrawGame()
{
switch(gameState)
{
case INVENTORY:
DrawInventory();
break;
case MAIN:
default:
DrawMain();
break;
}
}
但我认为绘图应该被封装,你应该告诉它需要绘制屏幕的哪个部分,而不是游戏的哪个区域。
答案 1 :(得分:0)
您使用嵌套渲染循环创建的内容在功能上是一个状态机(因为大多数游戏渲染循环都是如此)。嵌套循环的问题是很多时候你会想要在嵌套循环中做同样的事情作为外部循环(进程输入,处理IO,更新调试信息等)。
我发现最好有一个渲染循环并使用有限状态机(FSM)来表示你的实际状态。您的州可能看起来像:
您可以在状态之间连接转换以在它们之间移动。单击按钮的播放器可能会触发可以播放动画或其他内容的转换,然后转到新状态。使用FSM,您的循环可能如下所示:
while (!LeaveGame()) {
input = GetInput();
timeInfo = GetTimeInfo();
StateMachine.UpdateCurrentState(input, timeInfo);
StateMachine.Draw();
}
对于小型游戏,完整的FSM可能有点重量级,因此您可以使用一堆游戏状态尝试简化的状态机。每次用户执行操作以转换到新状态时,您都会将状态推送到堆栈上。同样,当他们离开状态时,你会将其弹出。只有堆栈顶部通常会接收输入,而堆栈中的其他项目可能会/可能不会绘制(取决于您的偏好)。这是一种常见的方法,取决于你与谁交谈,有一些好处和缺点。
最简单的选择就是抛出一个switch语句来选择要使用的渲染函数(类似于darvids0n的答案)。如果你正在编写一个街机克隆或一个小巧的益智游戏,那就可以了。