Eine coole Art, Z-Index für XNA-Komponenten zu erstellen?

7

Ich versuche einige Elemente in meinem Spiel zu rendern. Zuerst rendere ich die Texturen (die Texturen der Textfelder selbst usw.). Dann rendere ich die Grundelemente (Rahmen um Steuerelemente usw.). Dann rendere ich den Text.

Auf diese Weise ergibt sich jedoch ein seltsames Ergebnis in meiner Benutzeroberfläche, wenn QuickInfos angezeigt werden (indem Sie mit der Maus über ein Textfeld fahren), wie im folgenden Screenshot dargestellt.

Bildschirmfoto

Aufgrund der Reihenfolge des Zeichnens werden Ränder um das Fenster nach Texturen gezeichnet, was (für den Tooltip, der angezeigt wird, wenn sich meine Maus über dem Textfeld befindet) dazu führt, dass es seltsam aussieht.

Mathias Lykkegaard Lorenzen
quelle

Antworten:

6

Wenn diese jeweils separat sind DrawableGameComponents, sollten Sie die DrawOrderEigenschaften jeder Komponente so festlegen , dass sie in der richtigen Reihenfolge gezeichnet werden.

Um die Verwaltung beim Hinzufügen weiterer Komponenten zu vereinfachen, DrawOrdersollte Folgendes verwendet werden enum:

/* DisplayLayer.cs */
public enum DisplayLayer
{
    Background, //back-layer
    Particles,
    Player,
    MenuBack,
    MenuFront //front-layer
}

public class MyComponent : DrawableGameComponent
{
    public MyComponent(Game game) : base(game)
    {
        this.DrawOrder = (int)DisplayLayer.Background;
    }

    /* etc. */
}

Wenn dies alles in einem einzigen gezeichnet wird DrawableGameComponent, sollten Sie @ Andrews Rat befolgen und sie einfach in der richtigen Reihenfolge zeichnen. Wenn Sie dies aus irgendeinem Grund nicht tun können, können Sie bei der Verwendung immer noch die Klarheit von enums haben, layerDepthindem Sie einfach den Wert von enumzwischen 0 und 1 normalisieren :

/* DisplayLayer.cs */
public enum DisplayLayer
{
    MenuFront, //front-layer
    MenuBack,
    Player,
    Particles,
    Background, //back-layer
    MAX_LAYER   //Do not use this as a layer
}

public void Draw()
{
    spriteBatch.Begin(SpriteSortMode.BackToFront);
    foreach(Thingy thingy in thingys)
        spriteBatch.Draw(/* blah blah */, (float)thingy.DisplayLayer/(float)DisplayLayer.MAX_LAYER);
    spriteBatch.End();
}

(Die Reihenfolge der enumist umgekehrt, da layerDepthfür die vordere Schicht niedrigere Zahlen als für die hintere Schicht verwendet werden.)

BlueRaja - Danny Pflughoeft
quelle
6

http://msdn.microsoft.com/en-us/library/ff433989.aspx

public void Draw (
         Texture2D texture,
         Vector2 position,
         Nullable<Rectangle> sourceRectangle,
         Color color,
         float rotation,
         Vector2 origin,
         Vector2 scale,
         SpriteEffects effects,
         float layerDepth
)


layerDepth Die Tiefe einer Ebene. Standardmäßig steht 0 für die vordere Ebene und 1 für die hintere Ebene. Verwenden Sie SpriteSortMode, wenn Sprites beim Zeichnen sortiert werden sollen.
Zum Beispiel haben Sie: Formular und in den Formularbeschriftungen Schaltflächen.
Für untergeordnete Elemente können Sie der Ebene einen bestimmten Wert hinzufügen.
Beispiel: Formular A hat untergeordnete Elemente : Formular B und einige Schaltflächen, Formular B enthält nur die Schaltfläche
Formular A - 1.0
Schaltflächen in Form A - 1.5
Form B in Form A - 1.5
Tasten in Form B - 2

Für Dialoge können Sie einen größeren Wert speichern

Piotrek
quelle
4
Einige Einschränkungen für diese Methode: Der gültige Bereich von layerDepth(da bin ich mir ziemlich sicher) liegt zwischen 0 und 1, sodass Sie innerhalb dieses Bereichs bleiben müssen. Zusätzlich muss das Zeichnen innerhalb eines Sprite-Stapels (zwischen Beginund End) erfolgen. Schließlich: Es ist darauf hinzuweisen, dass diese Methode keinen Leistungsvorteil bietet, wenn man die Dinge zunächst in der richtigen Reihenfolge zeichnet.
Andrew Russell
6

Ich werde die einfache und ziemlich offensichtliche Antwort geben, und das heißt, ich zeichne zunächst alles in der richtigen Reihenfolge .

Um ein Beispiel für das zu verspotten, was ich meine:

// Assuming you have sorted windowList by Z-order, using one of the many
// existing sorting mechanisms provided by the framework such as List<T>.Sort().
foreach(var window in windowList)
    window.Draw();

// in your Window class:
public void Draw()
{
    this.DrawBackground();
    this.DrawBorders();
    this.DrawText();
}

Gibt es einen bestimmten, zwingenden Grund, warum Sie Ihre Fenster nicht bereits auf diese Weise zeichnen?

Andrew Russell
quelle
Ich würde vermuten, dass er, wie er in seiner Frage feststellt, sowohl SpriteBatch- als auch primitive Aufrufe ausführt und sie daher zusammenfassen möchte, was keine wirklich schlechte Idee ist. Ich scheine mich daran zu erinnern, dass das SpriteBatch trotzdem alle Anrufe stapelt, daher bin ich mir ziemlich sicher, dass die Touroption die gleiche Leistung bringen würde. Es macht auch mehr Sinn.
Jonathan Connell
@ 3nixios Ich würde vermuten, dass sein Grund auch die Leistung ist - aber ich würde dann sofort vorschlagen, dass es eine vorzeitige Optimierung ist. Funktioniert seine GUI wirklich so schlecht? Und ist die Konsolidierung von Chargen wirklich die richtige Wahl für die Optimierung? Auch SpriteBatchkann nicht Batch an einer Textur ändern - so meine Methode ist möglicherweise langsamer als seine Methode , wenn seine Umsetzung ordnungsgemäß optimiert wird .
Andrew Russell
Und seine Methode ist übrigens realisierbar. Aber es ist schwierig, es richtig zu implementieren - einschließlich einer guten Antwort. Es ist also wahrscheinlich nicht die Mühe wert für den kleinen Leistungsgewinn - und daher wäre es die falsche Antwort. (Die Kurzversion ist: Verwenden Sie den Z-Puffer.)
Andrew Russell
Ich stimme vollkommen zu :)
Jonathan Connell
Ich folge nicht, warum der obige Ansatz Sie daran hindert, alle Ihre SpriteBatchVorgänge zusammenzufassen. Ändern Sie einfach die Zeichenmethoden, um das SpriteBatchals Parameter zu akzeptieren . Rufen Sie SpriteBatch.Begin()dann in der Basis-Methode "Zeichnen" zuerst auf - rufen Sie dann "Zeichnen" unter Windows auf (Übergabe SpriteBatch, die sie verwenden und an jedes untergeordnete Element übergeben) - und dann nach der Schleife in der Basis- Methode "Draw" -Methode aufrufen SpriteBatch.End(). . . (Meine $ 0,02: Ich wette, er benutzt keine Objekte und das Ganze ist wahrscheinlich ein großes prozedurales Durcheinander in seiner Hauptklasse Game.)
BrainSlugs83