在不同的游戏引擎实现组件中填充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:我听说过反射,我知道一二,然后是托管可扩展性框架,但我还没有弄清楚它如何适合我的设计。

在不同的游戏引擎实现组件中填充SharpDX和OpenTK

为你的图形和声音层设计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等来保护。例如,你可以阻止在应用程序域中运行的代码访问创建文件,发出互联网请求等(如果你正在为游戏插件设计插件层,这非常有用)。
  • 应用程序域可以被编写成能够优雅地崩溃,记录它们的崩溃,并在不影响根进程的情况下重新启动它们自己。