C# 最佳实践:在XNA中高效绘制精灵

C# 最佳实践:在XNA中高效绘制精灵,c#,xna,drawing,2d,sprite,C#,Xna,Drawing,2d,Sprite,在我的2D XNA游戏中绘制精灵的有效方法是什么?更具体地说,我把这个问题分成4个问题 我曾经声明Game1的spriteBatch静态,并在每个IDrawable.Draw中调用spriteBatch.Begin和.Close。那没用。给每一个drawable它自己的SpriteBatch也没有很好地工作 Q1:我认为最好有一个SpriteBatch实例,并且只调用begin/close一次。这是正确的吗 目前,我的Game1.Draw看起来像这样: spriteBatch.Begin()

在我的2D XNA游戏中绘制精灵的有效方法是什么?更具体地说,我把这个问题分成4个问题


我曾经声明Game1的spriteBatch静态,并在每个
IDrawable.Draw中调用
spriteBatch.Begin
.Close
。那没用。给每一个drawable它自己的SpriteBatch也没有很好地工作

Q1:我认为最好有一个SpriteBatch实例,并且只调用begin/close一次。这是正确的吗


目前,我的
Game1.Draw
看起来像这样:

spriteBatch.Begin();

base.Draw(gameTime); // draws elements of Game.Components

// ...

spriteBatch.End();
Q2:这样,
Begin
只调用一次。这是路吗?你们是怎么解决的


Q3:此外,每个组件都有自己的
IGameComponent.LoadContent
方法。我应该在那里加载我的内容吗?还是最好在父类加载内容,例如
Game1.LoadContent


我意识到我不应该两次加载相同的内容,因此我给了我的可绘制组件一个静态的和一个非静态的
Texture2D
,并给了它们两个构造函数:

static Texture2D defaultTexture;
public Texture2D MyTexture;

public Enemy()
    : this(defaultTexture) { }

public Enemy(Texture2D texture)
{
    MyTexture = texture;
}

protected override void LoadContent()
{
    if (MyTexture == null)
    {
        if (defaultTexture == null)
        {
            defaultTexture = Content.Load<Texture2D>("enemy");
        }

        MyTexture = defaultTexture;
    }
}
静态纹理2d默认纹理;
公共纹理2d我的纹理;
公敌()
:此(默认纹理){}
公敌(纹理2D纹理)
{
MyTexture=纹理;
}
受保护的覆盖void LoadContent()
{
如果(MyTexture==null)
{
如果(defaultTexture==null)
{
defaultTexture=Content.Load(“敌人”);
}
MyTexture=defaultTexture;
}
}
这样,类的默认纹理仅在第一次对该类调用
LoadContent
时加载。但是,当在构造函数中指定参数
texture
时,我必须事先加载该纹理


Q4:我认为应该有更好的方法来做到这一点。我正在考虑创建一个纹理字典,将它们的资产名称作为字符串。你有什么建议?

尽可能从

只调用一次begin和end是首选方法

我曾经声明Game1的spriteBatch是静态的

你不必让它静止。做一个单身汉。内部
Draw
方法

SpriteBatch spriteBatch = ScreenManager.SpriteBatch; // singletion SpriteBatch
spriteBatch.Begin();
// Draw the background
spriteBatch.Draw(background, ...
foreach (var enemy in enemys)
{
     enemy.Draw(gameTime);
}
// etc.
spriteBatch.End(); // call here end 
base.Draw(gameTime); // and then call the base class
我认为最好有一个SpriteBatch实例,并且只调用 开始/关闭一次。这是正确的吗

我建议您在一个方法内打开并结束
SpriteBatch
。它将帮助您避免与开始绘制但未完成的SpriteBatch发生冲突

是否将元素添加到组件的全局集合?将drawable添加到此集合中不是一个好主意。您无法控制绘图顺序,组件是全局的

在组件IUpdatable和IDrawable中实现,并在需要时在代码中调用它们。去掉静态内容,使用istead

此外,每个组件都有自己的IGameComponent.LoadContent方法。我应该在那里加载我的内容吗?还是最好在父类加载内容,例如Game1.LoadContent

您应该在需要并负责的时候加载资产。当用户刚开始游戏时,您不必加载所有30个关卡,是吗?例如,当游戏开始时,加载开始屏幕和主菜单所需的所有资源。如果你加载了你需要的东西,玩家会很高兴游戏开始得很快。然后玩家想要开始游戏。在这里,您可以在单独的线程上加载资源,以保持应用程序的响应性

我认为应该有更好的方法来做到这一点。我正在考虑创建一个纹理字典,将它们的资产名称作为字符串。你有什么建议


Content.Load
已被缓存,因此您不必这样做

Q4:我认为应该有更好的方法来做到这一点。我正在考虑创建一个纹理字典,将它们的资产名称作为字符串。你有什么建议


A4:我通常在
TextureLib
类中创建一个
字典名TextureDic
(该类在运行时处理加载png图像),因此我可以获得如下任何纹理:

var t2d = textureLib.NameTextureDic["tree-4"];

我也是这样做的。如果我没有理由使用多重精灵批处理(例如,对于分割屏幕、不同效果等),我只在游戏的主类中调用一次开始和结束,并在此开始和结束之间绘制所有内容。您似乎在手动对敌人调用
绘制
。在我的游戏中,我已经将所有可绘制的东西(如墙和敌人)添加到
Game1。组件
列表,因此在调用基类时调用
Draw
。+1指出
Content.Load
本身处理冗余调用。然而,使用全局集合有那么糟糕吗?我不必控制绘图顺序。而且,我明白我不应该加载所有级别。但是,举个例子,我应该在玩家射击时加载bullet.png,在它离开时卸载吗?或者我应该在级别的开始加载图像?默认情况下,您不应该关心卸载<代码>内容
将管理缓存。游戏组件是一个设计缺陷。请参见,但是,我应该在init级别加载所需的精灵,还是在它们第一次出现时加载?:)