包装底层对象时创建 SpriteBatch 的最佳方法

发布于 2024-12-28 06:11:32 字数 2908 浏览 5 评论 0原文

问题本身在这里很简单,但所有解决方案似乎都是一堆糟糕的解决方案,所以我无法决定哪个是最好的,所以我想我应该在这里问...

我目前包装了一些 XNA 组件(SpriteBatch、GraphicsDevice) 、ContentLoader 等),在大多数情况下,它们将所有内容 1-1 包装,但存在一些差异,可选参数减少了函数重载。这样做主要是为了启用依赖注入并使我能够在测试中更轻松地进行模拟,因为我宁愿使用注入模式而不是服务定位器模式(尽管我还制作了一个实现 IServiceProvider 的 NinjectServiceProvider )。

无论如何,这里的主要问题是,由于不同的组件使用不同的混合模式等,我需要新建一些不同的 ISpriteBatch 对象。因此,我创建了一个 SpriteBatchFactory 对象来新建。在继续之前,这里有一些示例片段,以便您可以了解我的来源:

public class MySpriteBatch : ISpriteBatch
{
    private SpriteBatch underlyingSpriteBatch;
    private IGraphicsDevice graphicsDevice;

    public MySpriteBatch(SpriteBatch underlyingSpriteBatch, 
        IGraphicsDevice graphicsDevice)
    { 
        this.underlyingSpriteBatch = underlyingSpriteBatch; 
        this.graphicsDevice = graphicsDevice;
    }

// ...
}

MyGraphicsDevice:

public class MyGraphicsDevice : IGraphicsDevice
{
    private GraphicsDevice underlyingGraphicsDevice;

    public MyGraphicsDevice(GraphicsDevice underlyingDevice)
    {
        this.underlyingGraphicsDevice = underlyingDevice;
    }

    // ...
}

SpriteBatchFactory:

public class SpriteBatchFactory
{
    public SpriteBatchFactory(IGraphicsDevice graphicsDevice)
    {
        this.GraphicsDevice = graphicsDevice;
    }

    public IGraphicsDevice GraphicsDevice { get; private set; }

    public ISpriteBatch Create()
    {
        var underlyingGraphicsDevice = ????

        var underlyingSpriteBatch =
            new SpriteBatch(underlyingGraphicsDevice);

        return new MySpriteBatch(underlyingSpriteBatch,
            this.GraphicsDevice);
    }
}

现在,正如您所看到的,要新建一个 MySpriteBatch (ISpriteBatch),我需要一个新的 SpriteBatch,而这又需要一个 GraphicsDevice 实例。 ..这就是我不确定如何最好地满足这种依赖关系的地方...我想到的一些解决方案是:

1)MyGraphicsDevice包含这个,但它没有公开,所以我可以公开它作为接口级别的对象或具体类级别的实际 GraphicsDevice。无论哪种方式,这都需要对对象进行强制转换 -> GraphicsDevice 或 IGraphicsDevice ->我的图形设备。

2)我给SpriteBatchFactory一个GraphicsDevice而不是IGraphicsDevice,并且每次我需要更新MySpriteBatch时都会更新一个。

3)我在内部将MySpriteBatch新建为一个SpriteBatch,并使其需要一个MyGraphicsDevice并将其暴露在那里,因此MySpriteBatch对SpriteBatch没有外部依赖。

我认为第三种听起来是更封装的方法,但我不太喜欢这两种方法,所以其他人能想出更好的方法来解决这个问题吗?我并不担心 XNA 依赖项,因为我不想创建这个跨平台,只是想在 Xna 和我的框架之间提供一个更可控的层。

=== 编辑 ===

当我说我正在包装底层组件时,我的意思是字面上包装调用,即:

public class MyGraphicsDevice : IGraphicsDevice
{
    private GraphicsDevice underlyingGraphicsDevice;

    public MyGraphicsDevice(GraphicsDevice underlyingDevice)
    {
        this.underlyingGraphicsDevice = underlyingDevice;
    }

    public VertexBuffer[] GetVertexBuffers()
    {
        return underlyingGraphicsDevice.GetVertexBuffers();
    }

    public void SetRenderTarget(RenderTarget2D renderTarget)
    {
        underlyingGraphicsDevice.SetRenderTarget(renderTarget);
    }

    // ...
}

所以实际上没有自定义逻辑,它只是一个愚蠢的包装器。

The problem itself is quite simple here, but all solutions seem to be a bad bunch, so I cannot decide on which is best, so thought I should ask in here...

I currently wrap a few of the XNA components (SpriteBatch, GraphicsDevice, ContentLoader etc), for the most part they wrap everything 1-1 but there are a few differences where optional arguments cut down on function overloading. This is mainly done to enable Dependency Injection and to enable me to mock a lot easier in tests, as I would rather go with an injection pattern rather than a service locator pattern (although I have also made a NinjectServiceProvider which implements IServiceProvider).

Anyway the main problem here is that I need to new up a few different ISpriteBatch objects due to different components using different blend modes etc. So I have made a SpriteBatchFactory object to new on up. Before I continue here are some example snippets so you can see where I am coming from:

public class MySpriteBatch : ISpriteBatch
{
    private SpriteBatch underlyingSpriteBatch;
    private IGraphicsDevice graphicsDevice;

    public MySpriteBatch(SpriteBatch underlyingSpriteBatch, 
        IGraphicsDevice graphicsDevice)
    { 
        this.underlyingSpriteBatch = underlyingSpriteBatch; 
        this.graphicsDevice = graphicsDevice;
    }

// ...
}

MyGraphicsDevice:

public class MyGraphicsDevice : IGraphicsDevice
{
    private GraphicsDevice underlyingGraphicsDevice;

    public MyGraphicsDevice(GraphicsDevice underlyingDevice)
    {
        this.underlyingGraphicsDevice = underlyingDevice;
    }

    // ...
}

SpriteBatchFactory:

public class SpriteBatchFactory
{
    public SpriteBatchFactory(IGraphicsDevice graphicsDevice)
    {
        this.GraphicsDevice = graphicsDevice;
    }

    public IGraphicsDevice GraphicsDevice { get; private set; }

    public ISpriteBatch Create()
    {
        var underlyingGraphicsDevice = ????

        var underlyingSpriteBatch =
            new SpriteBatch(underlyingGraphicsDevice);

        return new MySpriteBatch(underlyingSpriteBatch,
            this.GraphicsDevice);
    }
}

Now as you can see to new up a MySpriteBatch (ISpriteBatch), I require a new SpriteBatch, which in turn requires a GraphicsDevice instance... which is where I am unsure as to how best satisfy this dependency... A few solutions I have thought of are:

1) MyGraphicsDevice contains this, but it is not exposed so I could expose it as an object at interface level or an actual GraphicsDevice at concrete class level. Either way this will require casting be it the object -> GraphicsDevice or IGraphicsDevice -> MyGraphicsDevice.

2) I give the SpriteBatchFactory a GraphicsDevice rather than IGraphicsDevice and new one up each time I need to new up the MySpriteBatch.

3) I make MySpriteBatch new up a SpriteBatch internally, and make it require a MyGraphicsDevice and expose it there, so the MySpriteBatch has no external dependency on SpriteBatch.

I think the 3rd one is sounding the more encapsulated way of doing this but I dont really like either way, so can anyone else think of a nicer way to solve this? I am not that bothered about having XNA dependencies as I am not trying to make this cross platform, just trying to give me a more controllable layer between Xna and my framework.

=== Edit ===

When I say I am wrapping the underlying components I mean literally wrapping the calls, i.e:

public class MyGraphicsDevice : IGraphicsDevice
{
    private GraphicsDevice underlyingGraphicsDevice;

    public MyGraphicsDevice(GraphicsDevice underlyingDevice)
    {
        this.underlyingGraphicsDevice = underlyingDevice;
    }

    public VertexBuffer[] GetVertexBuffers()
    {
        return underlyingGraphicsDevice.GetVertexBuffers();
    }

    public void SetRenderTarget(RenderTarget2D renderTarget)
    {
        underlyingGraphicsDevice.SetRenderTarget(renderTarget);
    }

    // ...
}

So there is no custom logic really, its just a dumb wrapper.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文