Jump to content
Unity Insider Forum

Reihenfolge von Objekten


Singular
 Share

Recommended Posts

Hallo zusammen,

Ich bräuchte mal wieder eure Hilfe. Ich habe eine 2D Umgebung in der ich Objekte per Drag&Drop verschieben kann. Das Funktioniert auch soweit alles ganz wunderbärchen. Problem ist jetzt, dass, wenn die Objekte übereinander liegen, teilweise unterschiedlich gegriffen werden können.

Habe ich zum Beispiel 3 Objekte übereinander gestapelt, kann es passieren, dass ich das unterste beim klickken bewegen kann, es kann aber auch das oberste oder das mittlere sein. Es sieht ein wenig zufällig aus welches Objekt ich erwische. Die Reihenfolge in der Herachie scheint keine Auswirkungen darauf zu haben und auch nicht in welcher Reihenfolge sie gerendert werden.

Ich würde gerne dass das Objekt, dass ich gerade Bewege als letztes gerendert wird. Also ganz oben angezeigt wird. Wie kann ich diese Reihenfolge ändern?
Außerdem möchte ich, dass immer das Objekt, das oben liegt zuerst bewegt werden kann.

Edited by Singular
Link to comment
Share on other sites

Das müsste eigentlich mit dem Spriterenderer zu machen sein. Der hat nämlich Sorting Layer. Die kann man im Code auslesen und auch setzen.
Du musst also den aufnehmbaren Sprites sagen, dass sie in dem obersten Layer sind. Nur Sprites aus diesem Layer können aufgenommen werden.
Der Layer ist ja bekannt und den kann man ja beim Versuch eines Drags abfragen.

Jetzt musst du nur irgendwie hin kriegen, dass ein Sprite es merkt, dass ein anderes auf ihm drauf positioniert wurde und sich dann selbstständig in einen tieferen Layer schiebt. Genauso muss erkannt werden, dass ein Sprite von obendrauf jetzt weg ist und man selbst jetzt oben liegt.



 

Link to comment
Share on other sites

Es ist auch nicht schlecht, wenn du eine Datenstruktur hast, in der alle deine SpriteRenderer in einer Liste sind, die ihre Reihenfolge darstellt. Du kannst dann bei jeder Änderung der Liste einmal komplett durch sie durchgehen und allen Objekten darin einen auf-/absteigenden Order In Layer geben. Wenn du dann deine Klicks checkst, kannst du von vorne nach hinten durch deine Liste durchgehen und das erste von den getroffenen Objekten, das du findest, wird aufgehoben. Das klingt auf den ersten Blick vielleicht fragwürdig in Sachen Performance, ist aber voll okay.

Link to comment
Share on other sites

😱 Ich wußte, dass dieses Thema mich füher oder später einholen wird... Ich muss euch beiden gestehen, ich habe keine Ahnung von Layern und was sie tun.

Ich gehe mal vorsichtig davon aus, dass sie "Schichten" sind wie die Objekte angezeigt werden. Im Inspector gibt es ja auch das schöne Dropdownfeld "Layer - Default", hat das etwas damit zu tun?

vor 7 Stunden schrieb Sascha:

Es ist auch nicht schlecht, wenn du eine Datenstruktur hast, in der alle deine SpriteRenderer in einer Liste sind,

Alle Objekte in eine Liste stecken, Check! Das bekomme ich hin und ist überschaubar und machbar.

vor 7 Stunden schrieb Sascha:

die ihre Reihenfolge darstellt. Du kannst dann bei jeder Änderung der Liste einmal komplett durch sie durchgehen

Bei klick das Objekt finden und auf Platz 1 setzen, Check! Das ist auch kein Problem. --> LinkedList.Remove(Renderer),  LinkedList.AddFirst(Renderer)

vor 7 Stunden schrieb Sascha:

und allen Objekten darin einen auf-/absteigenden Order In Layer geben.

Häh? Wie ändere ich den Layer? 

vor 8 Stunden schrieb malzbie:

Das müsste eigentlich mit dem Spriterenderer zu machen sein. Der hat nämlich Sorting Layer. Die kann man im Code auslesen und auch setzen.
Du musst also den aufnehmbaren Sprites sagen, dass sie in dem obersten Layer sind

Wahrscheinlich darüber, richtig? Läuft das dann darüber, dass ich GetComponent<SpriteRenderer>().sortingLayerID = 0.

Muss ich dann nicht nochmal durch die ganze Liste durch und jedem Object die nächst höhere Zahl zuweisen also sozusagen an welcher Position sie sich in der Liste sie sich gerade befinden?

Danke schpn mal für eure Hilfe

 

Link to comment
Share on other sites

Also ich sehe da 2 Wege. Eintweder du gehst davon aus, dass alle Objekte zu Beginn in der obersten Ebene sind, oder sie sind zu Beginn in der untersten Ebene.
Es ist ja eine Frage der Verortung. Also wie und wo können Sprites übereinander liegen.
Geht es da nur um eine Art Inventory, dann ist es ja leicht zu wissen, welche Sprites an welcher Position liegen. Und wenn sie dann gestackt würden, dann müsste(n) das(die) Sprite(s) im Layer jeweils eins nach unten. Immer nur das Sprite im obersten Layer kann angeklickt werden. Das wäre einfach für den Test und ginge ohne Liste.
Wird das oberste Sprite aufgehoben, dann müssten alle da drunterliegenden eine Ebene höher im Layer. Das wäre ein kleiner Befehl (event) für alle Sprites im entsprechenden Fach.
Ist es aber irgendwie in einer Spielewelt, wo zufälligerwiese Sprites voreinander liegen würden, dann wird das schon schwieriger, denn jetzt musst du es irgenwie schaffen, diesen Haufen zu gruppieren.

Da sehe ich klare Vorteile bei einer oder mehreren Listen, die immer dann da sind, wenn es zu Anhäufungen von Sprites kommt. 

Was ich aber noch wichtig fände, ist die Tatsache, dass man nicht unendlich viele SortingLayer anlegen muss. Man braucht vielleicht 3 für das Stacken. Das oberste Sprite soll natürlich immer oben gezeichnet sein. Das da drunter dann auch so, dass es optisch direkt unter dem Obersten liegt. Aber alle weiteren Sprites des Stacks können gemeinsam im untersten Layer liege. Ist vollkommen egal, wann welches gezeichnet wird.
Nur musst du dann intern wieder eine Liste haben, die die richtige Reihenfolge für alle Sprites darstellt. Und dann ist es wirklich gut, wenn in der Liste das Oberste gelöscht würde und alle anderen nachrutschen. Die jetzt obersten 2 Sprites verschieben sich um einen Layer, fürs optische, und alle weiteren bleiben im untersten Layer.
Der Test, welches Sprite denn anklickbar ist, ist ja ganz einfach über die Liste zu ermitteln.

  • Thanks 1
Link to comment
Share on other sites

vor 6 Stunden schrieb Singular:

Im Inspector gibt es ja auch das schöne Dropdownfeld "Layer - Default", hat das etwas damit zu tun?

Ne. Es gibt gleich eine Handvoll Dinge, die "Layer" heißen. Das da ist der GameObject-Layer - der wird für selektives Ausblenden von Objekten - Sichtbarkeit, Beleuchtung oder in der Physik - benutzt. Du kannst z.B. deine Raycasts durch bestimmte Layer hindurchschließen lassen. Oder die Kollision zwischen zwei Layern komplett abschalten.

Hier brauchen wir den "Sorting Layer", das ist eine Eigenschaft von Renderern. Im SpriteRenderer kannst du das direkt einstellen. Da gibt es zum einen die Layer selbst, von denen du ein paar anlegen kannst. Typisch wären so Layer wie "Background, Gameplay, Foreground" oder so. Reicht dann auch.

Was du dir hier anschauen musst, ist aber die zweite Eigenschaft: "Sorting Layer". Das ist schlicht eine Zahl, die innerhalb eines Layers benutzt wird, um eine Ordnung festzulegen. Ob hohe Zahl = vorne oder = hinten ist, kann ich mir immer nicht merken :D

Da das nur eine Zahl ist, musst du hier nichts anlegen. Du kannst also sowas machen:

for (var i = 0; i < spriteRenderers.Length; i++)
{
  spriteRenderers[i].sortingOrder = i;
}

(for-Schleife natürlich nicht bei einer LinkedList, da bräuchtest du eine foreach-Schleife die nebenbei hochzählt... oder du nimmst halt ne List)

Link to comment
Share on other sites

Okay, nice das hat geklappt mit. Und es ist tatsächlich so, Hohe Zahl = Ganz oben, niedrige Zahl = ganz unten.

Ich habe es jetzt so gemacht, dass alle Objecte, sich in ihrer Awake Methode bei einem Static Object anmelden und dort in eine Liste eingetragen werden. Beim Anklicken des Objects, bekommt dieses die Zahl "List.Count" zugewisen und alle anderen werden dann absteigend durch die Liste zugewisen. Also bekommen sozusagen die Zahl an der Sie stehen in der Liste (invertiert).

Ob das jetzt so sauber ist, weiß ich nicht... Ich weiß halt nicht, wann so etwas in die Knie geht, da mit jedem Klick jedes Object in der Spielwelt einmal angesprochen wird, bzw deren SpriteRenderer.

Am 17.11.2022 um 10:02 schrieb malzbie:

Es ist ja eine Frage der Verortung. Also wie und wo können Sprites übereinander liegen.

Mein "Spiel" ist sogesehen kein richtiges Spiel... (kennt Ihr TabelTopSimulator auf Steam?) du darfst es dir als Schreibtisch vorstellen auf dem alles Mögliche liegen kann. Spielkarten, Würfel, ...  Dementsprechend kann alles immer überall liegen und auch über und untereinander. Es soll zwar einzelne Objekte geben, die immer oben liegen (Würfel) bzw immer unten liegen (Spielbrett) aber das mal am Rande und darum kümmere ich mich später^^.

 

Am 17.11.2022 um 10:02 schrieb malzbie:

Was ich aber noch wichtig fände, ist die Tatsache, dass man nicht unendlich viele SortingLayer anlegen muss.

Was ich ja sogesehen damit getan habe... Die Liste könnte bis ins unendliche gehen. Deswegen weiß ich nicht ob das so sauber ist.

Ich habe gerade mal versucht, dass das oberste Object einfach auf 5 gesetzt wird und alle anderen auf 3, dann geht es nicht mehr. Im zuge dess stellt sich mir nochmal die Frage: Warum geht das hier mit einer Linked List nicht: layerList[0].sortingOder = 5; bzw, wie komme ich am besten an das erste oder xte Objekt dran???

 

Am 17.11.2022 um 10:02 schrieb malzbie:

Der Test, welches Sprite denn anklickbar ist, ist ja ganz einfach über die Liste zu ermitteln.

Derzeit mache ich das über das Unity Event (ist das ein Event? 🤔) OnMouseDown bzw. OnMouseDrag usw. wie prüfe ich das denn am besten? Es werden immer noch Objekte, die eigentlich hinter einem anderen sind, zuerst bewegt. Nicht immer, aber leider sehr oft. Alle Objekte haben in diesem Fall einen BoxCollider2D. Wenn der Spieler Klickt, wird das Objekt nach oben im sortingOrder gesetzt, "Draged" der Spieler, wird das Objekt an den Mauszeiger geheftet und bewegt sich mit.

Theoretisch müsste beim Klick geprüft werden ob sich zwischen dem angeklickten Objekt und dem Mauszeiger Objekte sind, die in der Liste weiter oben liegen.

Ich glaube, würden ALLE Objekte dem Mauszeiger folgen wäre der Fall klar, da mehrere Objekte gefunden wirden die unter dem Mauszeiger sind aber da nur eins folgt (Was ja auch sein soll) bin ich mir nicht sicher, wie ich da vor gehen soll.

Link to comment
Share on other sites

vor 4 Stunden schrieb Singular:
Am 17.11.2022 um 10:02 schrieb malzbie:

Was ich aber noch wichtig fände, ist die Tatsache, dass man nicht unendlich viele SortingLayer anlegen muss.

Was ich ja sogesehen damit getan habe... Die Liste könnte bis ins unendliche gehen. Deswegen weiß ich nicht ob das so sauber ist.

malzbie meinte damit die tatsächlichen Layer. Wenn du die sortingOrder-Eigenschaft genommen hast, ist das schon völlig in Ordnung so.

vor 4 Stunden schrieb Singular:

Warum geht das hier mit einer Linked List nicht: layerList[0].sortingOder = 5;

In dem Moment, wo du mit einem Laufindex auf deine Liste zugreifst, solltest du keine LinkedList mehr nehmen. Kann gut sein, dass einige LinkedList-Implementationen deshalb gar keinen Index-Zugriff erlauben. Nimm einfach ne normale Liste. Wenn du nicht zehntausende Elemente hast, bringt dir ne LinkedList keinen Performance-Vorteil.

vor 4 Stunden schrieb Singular:

Unity Event (ist das ein Event? 🤔) OnMouseDown bzw. OnMouseDrag

Ich nenne die inzwischen "MonoBehaviour-Events", und ich glaube das machen viele so.

vor 4 Stunden schrieb Singular:

wie prüfe ich das denn am besten?

Mach selber nen Raycast (oder vermutlich eher Physics2D.OverlapPointNonAlloc) und filtere dir dann das Objekt mit der höchsten sortingOrder raus.

  • Thanks 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

×
×
  • Create New...