2010-12-28 4 views
5

Ich programmiere in C# mit dem .NET Framework 4 und zielen darauf ab, ein Kachel-basiertes Spiel mit XNA zu machen. Ich habe eine große Textur (256 Pixel mal 4096 Pixel). Denken Sie daran, dies ist ein Kachel-basiertes Spiel, so dass diese Textur so massiv ist, nur weil es viele Kacheln enthält, die jeweils 32 Pixel mal 32 Pixel sind. Ich denke, die Experten werden definitiv wissen, wie ein Kachel-basiertes Spiel ist. Die Orientierung ist orthogonal (wie ein Schachbrett), nicht isometrisch.Draw() 20.000 32 von 32 Texturen oder 1 große Textur 20.000 Mal

In der Game.Draw() -Methode habe ich zwei Möglichkeiten, von denen eine sehr viel effizienter sein wird als die andere.

Wahl/Methode # 1:

Semi-Pseudocode:

public void Draw() 
    { 
     // map tiles are drawn left-to-right, top-to-bottom 
     for (int x = 0; x < mapWidth; x++) 
     { 
      for (int y = 0; y < mapHeight; y++) 
      { 
       SpriteBatch.Draw(
        MyLargeTexture, // One large 256 x 4096 texture 
        new Rectangle(x, y, 32, 32), // Destination rectangle - ignore this, its ok 
        new Rectangle(x, y, 32, 32), // Notice the source rectangle 'cuts out' 32 by 32 squares from the texture corresponding to the loop 
        Color.White); // No tint - ignore this, its ok 
      } 
     } 
    } 

Bildunterschrift: So effektiv, die erste Methode verweist auf eine große Textur viele, viele Male, jedes Mal, Verwenden Sie ein kleines Rechteck dieser großen Textur, um das entsprechende Kachelbild zu zeichnen.

Wahl/Methode # 2:

Semi-Pseudocode:

public void Draw() 
    { 
     // map tiles are drawn left-to-right, top-to-bottom 
     for (int x = 0; x < mapWidth; x++) 
     { 
      for (int y = 0; y < mapHeight; y++) 
      { 
       Texture2D tileTexture = map.GetTileTexture(x, y); // Getting a small 32 by 32 texture (different each iteration of the loop) 

       SpriteBatch.Draw(
        tileTexture, 
        new Rectangle(x, y, 32, 32), // Destination rectangle - ignore this, its ok 
        new Rectangle(0, 0, tileTexture.Width, tileTexture.Height), // Notice the source rectangle uses the entire texture, because the entire texture IS 32 by 32 
        Color.White); // No tint - ignore this, its ok 
      } 
     } 
    } 

Bildunterschrift: So effektiv, das zweite Verfahren wird viele kleine Texturen oft zeichnen.

Die Frage: Welche Methode und warum? Persönlich würde ich denken, es wäre unglaublich effizienter, die erste Methode zu verwenden. Wenn Sie darüber nachdenken, was das für das Tile-Array in einer Map bedeutet (denken Sie an eine große Map mit 2000 mal 2000 Kacheln, sagen wir mal), müsste jedes Tile-Objekt nur 2 Integer für die X- und Y-Position der Quelle enthalten Rechteck in der einen großen Textur - 8 Bytes. Wenn Sie jedoch Methode 2 verwenden, müsste jedes Tile-Objekt im Tile-Array der Map eine 32by32-Textur - ein Bild - speichern, das 32 Mal 32 Mal Speicher für die RGBA-Pixel zuweisen muss - das sind 4096 Byte pro Kachel dann? Also, welche Methode und warum? Erste Priorität ist Geschwindigkeit, dann Speicherlast, dann Effizienz oder was auch immer Sie Experten glauben.

Antwort

9

Die erste Methode wird viel schneller sein! Es hat auch nichts wirklich mit Ihren CPU-seitigen Datenstrukturen zu tun. Sie könnten in jedem Fall auf ein paar Bytes runterkommen - also wäre es egal.

Der Grund, dass die erste Methode schneller sein wird, ist wegen der Art, wie die GPU funktioniert. Um es einfach zu sagen, senden Sie die GPU-Zeichenbefehle mit Listen von Vertices/Dreiecken. Diese Scheitelpunkte haben verschiedene Daten (Position, Farbe, Texturkoordinaten usw.). Entscheidend ist, dass sie keine Textur angeben können - die Textur, mit der die Dreiecke gezeichnet werden, muss auf dem Grafikgerät vor dem Zeichnen für die gesamte Liste der Dreiecke angegeben werden.

Was SpriteBatch tut, wird automatisch Ihre Sprites bündeln, wo es möglich ist, so dass es Texture Swaps minimiert und daher Befehle gesendet werden. Dies ist der Grund, warum eine Sortierung nach Textur möglich ist. Es ist auch der Grund dafür, dass der Parameter source-rectangle vorhanden ist - um die Verwendung von Sprite-Blättern zu ermöglichen - so dass mehr Sprites gezeichnet werden können, ohne Texturen zu ändern.

(Tatsächlich werden die Zeichenbefehle, die Sie an die GPU senden - auch wenn Sie SpritBatch nicht verwenden - als "Stapel" bezeichnet. Wenn Sie zu viele senden, werden Sie "stapelweise". Es ist überraschend einfach zu werden Charge begrenzt - ein paar hundert Chargen ist eine sehr grobe Schätzung der Grenze.)

+0

Wirklich zu schätzen Ihre umfassende Antwort. Danke vielmals! – Jason

2

Zustimmen. Je weniger Texturen Sie von der höheren ziehen, desto geringer wird die Zustandsänderung auf der GPU.

Um es noch schneller zu machen, beachten Sie, dass die meisten Ihrer Kacheln nicht zwischen Frames wechseln. Zeichnen Sie sie also alle einmal auf ein einzelnes Bildschirm-Render-Ziel, und zeichnen Sie dann in nachfolgenden Frames diese einzelne Textur mit einem Draw-Aufruf neu. Füge die Kacheln hinzu, die sich oben ändern.

+0

Danke für Ihre Einsicht! – Jason