我正在清理我的一款旧平台游戏,该游戏写得非常糟糕(抱歉,超出了我的范围)。执行时间和内存管理不是很好,所以我试图压缩大量冗余代码。让我困惑的一个地方是调用 void 函数来从枚举值加载特定级别(我有一个名为 EnumState 的枚举标头,它存储所有各个级别的名称),这是我能记得的(目前正在工作)无法访问确切的源代码):
void loadLevel(EnumState state, GameHandler *handler)
{
switch(state)
{
case main_menu:
mainMenu(handler);
break;
case lvl_1:
lvl1(handler);
break;
case lvl_2:
lvl2(handler);
break;
//...so on and so forth...
}
}
EnumState.h:
enum EnumState
{
main_menu,
level_select_screen,
lvl_1,
lvl_2,
lvl_3,
//...and so on...
};
这个 loadLevel 函数让我抓狂,因为它真正做的就是调用 lvl_xxx() 函数来获取级别数,无论如何,当你将它传递给函数时,你已经知道了级别数!它的主要目的是用于主菜单中的文件处理例程 - 当您选择以前保存的游戏时,您离开的最后一个级别的整数将保存在文本文件中,以及那个疯狂的长 switch 语句(比冰山一角还要长)我在上面展示的)负责获取该整数的枚举版本(一旦在下面所示的 gotolevelfromint 函数中将其从 int 转换为 EnumState),然后加载该编号级别的相应 void 函数。
EnumState goto_level_from_int(int whatLevel)
{
switch(whatLevel)
{
case 1:
return lvl_1;
break;
case 2:
return lvl_2;
break;
//...so on...
}
}
我几乎希望有一种方法可以基于连接的字符串值调用函数(例如,您可以调用:
lvl_ + levelNumber + (handler)
,而不是必须在 switch 块中调用 lvl1(handler) ,其中“levelNumber”是整数值从前面提到的文本文件中取出)。
这个无底开关块最近在我的几个程序中一直是一个问题。我听说矢量和地图是很好的替代品,但对此都没有经验。
顺便说一句,上面看到的“handler”参数是我设置的“GameHandler”类的一个对象,其中包含所有图像、字体、音乐等的数组,这些数组在 Main.cpp 中初始化一次,然后在整个游戏中传递为需要通过这些 *handler 指针参数。这就是为什么每个级别的 void 函数都将其作为参数的原因。我不确定这是支持多个级别之间切换的最佳方式。也许只有 1 个主 while 循环,在您进入游戏的下一部分后,它只会覆盖本地使用的背景图像、音乐等对象指针?不确定通常是如何完成的。同样,内存管理也不是很好 - 当我在游戏中进入暂停屏幕并退出 Windows 时,它会循环返回之前的所有状态 - 关卡选择屏幕、主菜单等,然后因错误而崩溃带有对话框。
我确信 C 或 C++ 有一些很棒的功能,但我完全错过了可以很好地浓缩这些知识的知识。非常感谢任何建议!
几年前,当我最初开始开发时,我试图完全避免使用整数级别处理系统,而是将最后一个级别作为字符串保存在同一文本文件中(例如“lvl1”)。我的想法是在主菜单函数的文件处理例程中设置一个、仅一个主 switch 语句,该语句将调用与文本字符串的值匹配的级别的 void 函数。好吧,显然我忘记了在 C++ 中, switch() 不适用于字符串,即使它确实有效,我认为单独的 switch 块会变得疯狂长,并且仍然可以以某种方式避免。如果我还记得我在 Python 课堂上的经历,我们就用字典解决了这样的问题......
您可以使用类似
std::map
的东西来保存指向函数的指针:
using level_handlers = void (*)(GameHandler *);
static const std::map<EnumState, level_handler> levels = {
{ main_menu, mainMenu },
{ lvl_1, lvl1 },
{ lvl_2, lvl2 },
{ lvl_3, lvl3 }
// ...
};
void loadLevel(EnumState state, GameHandler *handler)
{
auto handle = level_handlers.find(state);
if (handle == level_handlers.end())
throw std::runtime_error("Invalid level");
(*handle)(handler);
}
当然,根据您一般处理错误的方式,如果您得到错误的级别编号,您可能更喜欢除了抛出异常之外的其他方法。
根据情况,您可能想要使用指向函数的指针数组而不是映射。这通常至少会执行得更快一些,但这意味着
EnumState
和数组内容必须保持同步,这可能是一个维护问题。