我的游戏引擎包含一系列松散耦合的模块,可以加载和卸载。
一些例子是:基本模块,处理窗口管理和响应OS事件,实体管理器,Lua经理,物理经理。
现在,这些模块被组织为命名空间,它们的状态通过相应源文件中的局部变量来定义。每个命名空间都有一个Open(),Close()和Update()函数。
现在,我不再喜欢名称空间的解决方案了。
它不够灵活
即使实际上可能不需要它,但是具有创建模块的多个实例的简单能力似乎是正确的
好像我在这里没有使用OOP - 带有虚拟Update()成员函数的模块基类听起来更合理
当模块关闭并重新打开时,更难以确保所有变量也将被重置(带有构造函数和析构函数的类将更容易)
如果没有显式调用Open(),Close()和Update()
所以,我的想法是对每个模块使用类,从模块基类派生。模块类实例将由ModuleManager类处理,后者会更新它们。
但是OOP的解决方案带来了模块应该如何通信的问题。现在,基本模块告诉控制台模块通过console::print()
如何在不使用g_ModuleManager.GetConsoleModule()->print()
之类的情况下解决此问题?
该模块管理器如何详细工作?
我的最后一个问题是:
关于使用OOP在C ++中编写模块化游戏引擎的主题,您对我有什么进一步的提示吗?
在这种情况下,是否有任何设计模式可以帮助我,或者甚至是具体的阅读材料?
答案 0 :(得分:2)
命名空间很快变得非常灵活
保持松散耦合的一种方法是通过中央消息调度程序使用消息传递;而不是说console::print
你会说messenger->sendMessage(PRINT_MESSAGE, stuffToPrint)
控制台会将自己注册为PRINT_MESSAGE
的监听器,并按照自己想要的方式行事。发送者不需要关心某人是否在监听,每个消息甚至可能有几个监听器(对调试很有用)。
关于阅读材料,Jason Gregory的"Game Engine Architecture"非常好,讨论了几种架构的优点和缺点。
答案 1 :(得分:1)
首先作为一般提醒,请记住使用继承来代替可替代性,而不是代码重用。粗略地说,这意味着从您的基础模块继承的任何内容都需要支持(大部分)相同的操作,并且如果用户在任何模块上说open
,close
或update
,将提供预期的结果。
你真的想要一个模块经理吗?可能更好的是绘制不同类/模块之间关系的图表,并在那里创建链接而不是顶级经理。然后应用程序的主循环将知道一个或多个高级模块并直接调用它们上的适当方法。如果需要,甚至可以有一个高级模块来管理这种高级别的授权。
对于您的控制台示例,假设您不想支持多个控制台,您可能最好只保留名称空间:它使您能够直接引用您的第一个控制台&#39重新接口。或者你可能需要一个"父控制台"传递给创建的每个模块,并使用该关联控制台根据需要执行其I / O.这样可以提高灵活性,但需要确保必须在基础模块中维护控制台。
答案 2 :(得分:0)
g_ModuleManager.GetConsoleModule()->print()
很简单,正如我之前所说,你很可能不得不重构很多东西来利用OOP。你希望每个班级都有一个独特的关系,而不是所有人都在平等的竞争环境中进行管理。告诉ConsoleModule要打印的人应该有自己的打印功能,告诉ConsoleModule打印。这样你就可以说print()
也认为“有一个”用于组成,“是一个”用于继承。 我会阅读整个第8节:http://www.learncpp.com/cpp-tutorial/81-welcome-to-object-oriented-programming/
作为我当前项目的一个例子:我有一个“拥有”beatles
,walls
和apples
的董事会。每个“都是”icon