模块化游戏引擎:DLL 循环依赖项
Modular game engine: DLL circular dependencies
我想创建一个游戏引擎作为培训和投资组合项目,模块化方法听起来很有前途,但我在模块设计方面遇到了一些问题。
首先,我想创建低级模块,如渲染、应用程序、实用程序等,并在高级模块(如地形(中使用它们。所以依赖关系有点像这个游戏<引擎><地形><渲染。>地形>引擎>
我想创建多个渲染"子模块",如 Rendering.Direct3D11 和 Rendering.OpenGL。这就是我将具有循环依赖性的地方。子模块将使用渲染接口,渲染需要管理子模块,对吧?游戏<引擎><地形><渲染><-->渲染.Direct3D11渲染>地形>引擎>
我可能会创建一个像 RenderingInterfaces 这样的模块并打破循环依赖关系,但这似乎是一个笨拙的解决方法。我计划多次使用"子模块设计",例如:Game<-Engine<-Application<-->Application.Windows
子模块设计难看吗?有没有办法在没有循环依赖的情况下使用子模块设计?
你可以抽象地解决这个问题。假设您有三个 dylib:Game.dll
、Renderer.dll
、SubRenderer.dll
。
渲染器界面可能如下所示(简化(:
// Renderer.h
class SubRenderer
{
public:
virtual ~SubRenderer() {}
virtual void render() = 0;
};
class API Renderer
{
public:
explicit Renderer(SubRenderer* sub_renderer);
void render();
private:
SubRenderer* sub_renderer;
};
您可以将其粘贴在Renderer.h
或类似的东西中,并且 Renderer 构造函数和 render
方法可以用Renderer.cpp
实现,您可以将其包含在输出Renderer.dll
的项目中。
现在在 SubRenderer.dll
中,您可能有一个这样的函数:
// SubRenderer.h
class SubRenderer;
API SubRenderer* create_opengl_renderer();
这可以在编译/链接到输出"SubRenderer.dll的SubRenderer.cpp
中实现。它可能看起来像这样:
// SubRenderer.cpp
#include "SubRenderer.h"
#include <Renderer.h>
class OpenGlRenderer: public SubRenderer
{
public:
virtual void render() override {...}
};
SubRenderer* create_opengl_renderer()
{
return new OpenGlRenderer;
}
最后但并非最不重要的一点是,在 Game.dll 中的某个源文件中,您可以在某些Game.cpp
中执行以下操作:
// Game.cpp
#include <Renderer.h>
#include <SubRenderer.h>
int main()
{
SubRenderer* opengl_renderer = create_opengl_renderer();
Renderer renderer(opengl_renderer);
renderer.render(); // render a frame
...
delete opengl_renderer;
}
。当然,希望采用符合 RAII 的更安全的设计。
对于这种系统,您有以下标头依赖项:
`Game.cpp->Renderer.h`
`Game.cpp->SubRenderer.h`
`SubRenderer.cpp->Renderer.h`
在模块依赖方面:
`Game.dll->Renderer.dll`
`Game.dll->SubRenderer.dll`
就是这样 - 任何地方都没有循环依赖。 Game.dll
取决于Renderer.dll
和SubRenderer.dll
,但Renderer.dll
和SubRenderer.dll
是完全独立的。
这是有效的,因为这个Renderer
可以使用给定其虚拟接口的SubRenderer
,而不知道它到底是什么(因此不需要依赖于具体类型的"子渲染器"(。
您可以将Renderer.h
放在可从所有三个项目集中访问的位置,并具有公共包含路径(例如:在SDK
目录中(。没有必要复制它。
在你的设计中不需要任何反向依赖关系。
这都是关于接口的。呈现模块需要一个本机呈现 API(用你的话来说,是子模块(,但它不应该关心它是 OpenGL 还是 Direct3D11。API 子模块只需要公开一个通用的 API;像CreatePrimitiveFromResource()
,RenderPrimitive()
...这些子模块不应该知道上层,它们只是公开了它们的通用API。
换句话说,唯一需要的"依赖"是渲染模块依赖于渲染子模块(使用通用接口(,而渲染子模块不依赖于任何东西(在您的引擎中(,它们只是公开一个通用接口。
简单的例子:
我们有一个渲染模块"IntRenderer"来渲染整数。它的工作是将整数转换为字符并打印它们。现在我们希望有子模块"IntRenderer.Console"和"IntRenderer.Window",以便在控制台或窗口中打印。
有了这个,我们定义了我们的接口:子模块必须是导出函数void print( const char * );
的 DLL。
整个描述就是我们的界面;它描述了我们所有 int 渲染器子模块必须具有的公共面孔。从编程方式上,你可以说接口只是函数定义,但这只是一个术语问题。
现在每个子模块都可以实现接口:
// IntRenderer.Console
DLLEXPORT void print( const char *str ) {
printf(str);
}
// IntRenderer.Window
DLLEXPORT void print( const char *str ) {
AddTextToMyWindow(str);
}
有了这个,int 渲染器可以只使用导入子模块,并使用 printf(myFormattedInt);
,而不管子模块如何。
显然,您可以根据需要定义接口,如果需要,可以使用C++多态性。
示例:子模块 X 必须是导出函数CreateRenderer()
返回继承类Renderer
的类的 DLL,并实现其所有虚函数。
- C++GTKMM gui循环依赖关系
- 如何在头文件中声明类模板(由于循环依赖关系)
- 如何在具有循环依赖的类中dynamic_cast?
- C++模板方法中的循环依赖关系
- 修复循环依赖项 c++17 标头
- 涉及全局对象的循环依赖C++
- 循环依赖,在继承类的情况下使用覆盖方法
- Wt::D bo 中的循环依赖关系
- 在包含窗口标头时难以解决循环依赖关系问题
- 解决循环依赖关系 c++ 的想法
- C++循环依赖关系,未声明的标识符
- C++ 中的循环依赖关系问题
- 错误 C2512 视觉C++(并且不是循环依赖项)
- CMake 外部和内部静态库的循环依赖关系
- 正在从继承中解析循环依赖项
- "std::shared_ptr"循环依赖关系是如何导致问题的
- 纯引用而不是weak_ptr来打破循环依赖关系
- "invalid use of incomplete type" .解决循环依赖关系
- 如何避免模板方法的循环依赖
- 循环依赖结构,使用前向声明时结构的错误重定义