[翻译]XNA 3.0 Game Programming Recipes之fourteen

时间:2023-01-14 11:31:56
PS:自己翻译的,转载请著明出处
                                                  3-4 使用SpriteBatch类时考虑性能
问题
    当错误的使用 SpriteBatch类时,如果您要提供大量的图像您的应用程序将运行极其缓慢。
解决方案
    如前所述在3-1节,在开始或结束之间或每一祯都创建一个新的SpriteBatch类,你绘制的每一个图象将会杀死你的性能。但是你应该知道也有更微妙的方面。
它是如何工作的
性能优化:精灵存储模式
    SpriteBatch类方法的开始允许你设置一个SpriteSortMode.但是在此之前通过不同的模式,你需要知道你的显卡喜欢和不喜欢什么样的工作。
    绘制一个2D图象到屏幕上是被两个三角形绘制的,填充它们的颜色是来自于一个纹理。图象卡喜欢一次绘制大量的三角形,没有任何的阻碍。虽然,不管何时你改变纹理,你阻碍你的图形卡。换句话说,从同样的纹理中绘制100副图象将比从100个纹理中绘制100副图象更快。这意味着, 您可以提高您的性能,从而确保您尽可能的少的改变 ative texure
    考虑到这一点,你可以读一个关于精灵可能的存储模式说明,您可以一个参数到 SpriteBatch.Begin方法中:
    1。Deferred:这是默认的 SpriteSortMode。你几乎从未想过利用它,不过,因为实际上没有参加排序。当你添加一批精灵靠调用 SpriteBatch.Draw方法,精灵是很容易放在堆栈的顶部的。此时,你可以调用 SpriteBatch.End的方法,在堆栈中所有的精灵按一定次序出栈绘制出
    2。 Texture:一般来说,为了理想的性能和易用性,你会使用这些代码的。在此时你调用 SpriteBatch.End方法,所有图象从堆栈输出的都被纹理保存,在 XNA实际上要求你的图形卡去绘制它们之前。这样,您的显卡可以得出所有三角形,在运行使用相同的纹理, 你会减少你必须去打扰你的图象卡改变纹理的次数。不过,假如您使用的是阿尔法的透明度,这可能会带来麻烦,您需要使用下一个模式。
    3。 BackToFront:当使用 alpha混合器,你想要对象以最快的方式先绘制。为了知道原因,参看以前的节。为此, SpriteBatch.Draw方法,添加了一个图象到 SpriteBatch类,加载接收了一个 layerDepth.使用浮点型,你可以指定哪层图象应该被绘制,制定关于在1.0f表示层,这是最远(其中举例来说,包含您的草)和0.0f显示层,是最接近 由(您可以,例如,使您的岩石) 。你可以指定任意的数字在两者之间。当使用 BackToFont 模式,不管何时调用 SpriteBatch保存最远的层被先绘制。
    4。 FrontTOBack:这是和以前模式相反的,因此最接近的图像层首先被绘制。由于每个像素在屏幕上提请将永远需要覆写(因为所有下列图像之后被绘制) ,这产生最好的性能。然而,这不会与 alpha混合(见以前的章节)
和获得更大的性能仅仅在所有的图象来自于同一纹理!如果纹理需要更换10次去绘制近景层,你的性能将会比使用Texture模式变的更糟。
    5。 Immediate:与其他模式相矛盾,在XNA模式里不在等待调用 SpriteBatch.End方法去绘制所有图象在 SpriteBatch类中。只要你添加图象使用同一纹理到 SpriteBatch靠调用 SpriteBatch.Draw方法, SpriteBatch将会把它放到堆栈中。尽管,在此时你要绘制一个精灵使用别的纹理,在当前堆栈中的精灵会被立即绘制。
    第一次使用四种方式,仅在调用 SpriteBatch.End时,这个精灵会被保存,绘制状态会被设置,三角形和纹理将会被送到图象卡中。这允许你使用 SpriteBatch类的混合对象,用随即次序添加新的精灵到它里面,或者绘制3D对象使用它自己的绘制状态。
    使用 Immediate 模式,尽管,在你调用 SpriteBatch.Begin方法时这一时刻绘制状态被设置,精灵将会被绘制在调用 SpriteBatch.Draw方法后。也就是说在这之间你可以有机会改变绘制状态!这样,你可以通过更多的 alpha混合器模式甚至绘制精灵使用一个自定义的象素渲染器!然而,当你使用 Immediate模式绘制图象时,你不应绘制任何对象,如3D对象,因为你这样做了可能会改变绘制状态。当你想使用 SpriteBatch来继续绘制精灵时,绘制状态不会被重新设置,所以绘制的精灵使用绘制状态和3D对象的象素绘制器。
性能优化:在一个图象文件中存储多个图象
    正如上一节所说的,当你绘制一定数量的精灵时你想要的一件事是你的图象卡尽可能少的改变你当前的纹理。 SpriteSortingMode.Texture已经帮了不少忙,但是一个完整的游戏需要成千上百的不同图象,这个将导致你的图形卡切换数百次在纹理之间,会使它非常气愤的。
   一个有用的办法是存储图片的问题,一些互相有联系的图象组成一个大图象,如图3-4节左边所示 。这样,你可以使用 SpriteBatch.Draw方法的 sourceRectangle参数去指定大图象的某一部分包含你实际上要绘制的图象,如3-3节代码片段所示。
    如图象3-4所示,所有子图象包含 40*40,3-3节的代码定义一些矩形它们声明在大图象上的子图象的位置。在 SpriteBatch.Draw方法中指定这些正方形之一的作为第三个参数,将会造成这个子图象被绘制到屏幕上。
使用多个SpriteBatches
     正如你们目前位置所看的, SpriteBatch是十分有用的对象。你可以批量或单一的 SpriteBatch 可以保存它们和很容易绘制他们到屏幕上。然而,也有情况下,您可能要使用多个 SpriteBatch类。
    说你是创建一个飞行游戏,飞机互相发射火箭弹。当然,你有多种类型的飞机和火箭。您可以存储所有可能的轮换一个单一的平面或火箭变成一个大纹理贴图,这将减少你需要的总纹理数量。
    您想进行一些烟雾在飞机的引擎后面,慢慢地消失,在火箭的后面有许多烟雾,当然。如果您绘制这些图像使用单一 SpriteBacth,,你有一个问题:你希望所有的图像用纹理来保存以得到优化性能。然而,你需要的飞机和火箭队被首先绘制,所以在此之后, 您可以很好结合您的精灵烟雾。这将需要您的精灵用他们的 layerDepth来保存!
    解决方法是使用 SpriteBatch类的两个对象: planeBatch和smokeBatch.在你的 Draw方法的开始,你调用 SpriteBatch类的Begin方法下一步,为每一个飞机和火箭,你添加飞机或者火箭图象到 planeBatch和在飞机之后的烟雾或者火箭到 smokeBatch.
    planeBatch能被 Texture存储, smokeBatch能被 BackToFont存储。在所有图象被添加到 batches之后,你首先调用 planeBatchEnd 方法,这将导致所有的飞机和火箭图象用 texture被保存,他会被绘制到屏幕上用一个非常好的性能。下面,结束 smokeBatch,它将导致烟雾精灵混合成很好的图象。
    所以,有一个最佳的性能去绘制你的飞机,被正确的混合!