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



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


我曾经声明 Game1 的 spriteBatch是静态的,并在每个IDrawable.Draw中调用SpriteBatch.Begin.Close。这效果不佳。为每个可绘制对象提供自己的SpriteBatch也效果不佳。

Q1:我认为最好有一个 SpriteBatch 实例,并且只调用开始/关闭一次。这是对的吗?


目前,我的Game1.Draw如下所示:

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

Q2: 这样,Begin只调用一次。这是要走的路吗?你们是怎么解决这个问题的?


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


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

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;
}
}

这样,类的默认纹理仅在第一次对该类调用LoadContent时加载。但是,当在构造函数中指定参数texture时,我必须事先加载该纹理。

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

我曾经声明过 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发生冲突。

是否将元素添加到组件的全局集合中?向此集合添加可绘制对象是个坏主意。您无法控制绘图的顺序,组件是全局的。看到这个答案。

在组件中实现 IUpdatable 和 IDrawable,并在代码中根据需要调用它们。摆脱静态的东西并使用依赖注入 istead。

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

您应该在需要时加载资产,并且您对此负责。当用户刚刚开始游戏时,您不必加载所有 30 个关卡,是吗?例如,当游戏启动时,您会加载开始屏幕和主菜单所需的所有资产。如果您只加载所需的内容,则玩家很高兴游戏快速启动。然后玩家想要开始游戏。在这里,您可以在单独的线程上加载资产,以保持应用程序的响应。

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

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

从教程中可以收集到

的仅调用开始和结束一次是首选方法。

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


A4:我通常在TextureLib类中创建一个Dictionary<string, Texture2D> NameTextureDic(在运行时处理加载png图像),因此我可以获得如下所示的任何纹理:

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

最新更新