在不同的游戏引擎实现组件中填充SharpDX和OpenTK
本文关键字:填充 SharpDX OpenTK 组件 实现 游戏 引擎 | 更新日期: 2023-09-27 18:18:49
我希望我的c#引擎可以让用户选择使用Direct3D还是OpenGL。我的引擎内部是这样设计的一个shader类的抽象例子:
internal class Shader
{
private IShaderImplementation _implementation;
internal Shader(ShaderType type, string code)
{
switch (Engine.GraphicsInterface)
{
case GraphicsInterface.Direct3D:
_implementation = new Direct3DShader(type, code);
break;
case GraphicsInterface.OpenGL:
_implementation = new OpenGLShader(type, code);
break;
default:
throw new UnsupportedInterfaceException("Shader");
break;
}
}
internal void Compile()
{
_implementation.Compile();
}
}
我认为这是一个很好的设计模式。(我也更喜欢调用构造函数而不是工厂方法,正如你可能已经注意到的,但这只是一个侧面的事情)。
然而:- 我不想在一个类库中填充OpenTK(用于OpenGL的东西)汇编引用和SharpDX(用于Direct3D的东西)汇编引用。我都不知道这是不是个好主意。从目前的设计来看,这将需要Direct3DShader和OpenGLShader类直接引用D3D或OGL方法。即使用户只希望使用OpenGL,我也不需要同时发布OpenTK/SharpDX程序集吗?
- 我更喜欢有一个主程序集只包含一般接口的东西,如上面所见(让我们说"Awful.Engine.dll",另一个只包含OpenGL的东西("Awful.Engine.OpenGL.dll"),另一个Direct3D的东西("Awful.Engine.Direct3D")。但是如何在主程序集中引用它呢?正如你所看到的,我有一个接口枚举,这将不再工作了。
以更一般的形式询问,我的主程序集接口和类如何在子程序集中实现和扩展?
PS:我听说过反射,我知道一二,然后是托管可扩展性框架,但我还没有弄清楚它如何适合我的设计。
为你的图形和声音层设计3个类库。
类库1:接口层
- 包含所有的包装器和接口,你的包装器将实现。
- 将接口设计为在自己的应用程序域中工作,并为所有内容分发接口。
类库2 (OpenTK):
- 参考类库1和OpenTK.
- 添加像OpenAL一样的声音API
- 实现包装调用OpenTK和播放声音的接口。
类库3 (SharpDX):
- 参考类库1和Sharp DX.
- 实现你的接口包装调用SharpDX和播放声音通过SharpDX。
游戏代码
- 添加对Class Library 1的硬引用,这样您就有了所有的接口类型,枚举等。
- 带反射的动态加载类库2或类库3
- 为你的硬件层创建一个新的应用程序域。
- 使用
AppDomain.Load
加载类库2或3。 -
一旦硬件组装被加载,你有了
Assembly
对象,使用反射来获得对你的接口的引用,也许是这样的:// Making up this pseudo code GameApi gameApi = (GameApi)hardwareAsm.GetType(GameApi).GetMethod("Initialize").Invoke(); TheGame gameWorld = gameApi.CreateGame(...); gameWorld.ResolutionChanged += Resolution_Changed(...); gameWorld.Terminated += Terminated(...); gameWorld.KeyDown += Key_Down(...); // etc.... gameWorld.Start();
让整个层在它自己的应用程序域中运行的好处是很多的:
- 应用程序域可以被卸载,从而导致它们加载的任何程序集也被卸载。(如果用户可以在运行时从DirectX切换到OpenGL,这将是必需的,这将是整洁和可能的,我以前从未见过有人这样做过)。
- 应用程序域可以使用它们自己的安全描述符、ACE等来保护。例如,你可以阻止在应用程序域中运行的代码访问创建文件,发出互联网请求等(如果你正在为游戏插件设计插件层,这非常有用)。
- 应用程序域可以被编写成能够优雅地崩溃,记录它们的崩溃,并在不影响根进程的情况下重新启动它们自己。