Jump to content
Unity Insider Forum

Instantiate


Kojote

Recommended Posts

Grüße!

Ich hab gleich mal ne Direktfrage an @Sascha. Du hattest vor einigen Monaten mal irgendwo geschrieben, dass Instantiate recht Performance fressend ist, wenn man Objkete initialisieren will. Ich beschäftige mich im Bezug auf meine Ladefunktion gerade mit dem Thema Spawn. Ich finds leider nicht mehr, was war denn noch mal die Alternative zu Instantiate?

Grüße von Zeex

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dass etwas Performance braucht heißt nicht automatisch, dass man es nicht benutzen sollte. Wenn du ein neues Objekt mit bestimmten Werten erstellst, müssen diese Werte von irgendwo gelesen werden, und dieser Vorgang des "Deserialisierens", der da passiert, braucht immer ein bisschen. Aber wenn du das nicht über Unity machen lässt, muss es ja trotzdem gemacht werden.

Worauf du dich vermutlich beziehst ist mein Thema zu Pooling. Da werden bereits existerende Objekte recycled, anstatt gelöscht zu werden, und da man dann die Objekte bereits hat, muss man sie nicht noch einmal deserialisieren. Bringt natürlich nur etwas bei Objekten, die oft spawnen und despawnen, wie Schuss-Effekte, Raketen oder Figuren, die im Hintergrund vorbei gehen.

Wenn du einfach nur Dinge in dein Level platzieren willst, nimm Instantiate, spricht nichts gegen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

OK, danke für die Antwort.

Malzbie brachte mich gerade in einem anderen Thema auf die Idee, dass sich das erzeugte Objekt selbst seine Daten, die es noch braucht aus meinem Speicherscript holt.

Ich habe eine Liste mit allen Objekten die initialisiert werden sollen. Sagen wir 10 mal Objekt Enemy.

Im Script erzeuge ich also 10 mal das Objekt:

Instantiate(prefab, Vector3, Quaternion);

Problem ist nun, ich hab zwar nen Prefab, kann ihm auch die Position und Quaternion verraten, dann wird das Objekt aber erzeugt und gut. Ich möchte aber allen Prefabs noch verraten, was sie für Variablen haben, unter anderem Animationszustand, Lebenspunkte, Mana, etc.

Wie weiße ich nun dem erzeugten Prefab noch diese Werte zu, ich muss mich ja auf irgendwas beziehen können.

Bisher sieht mein Script so aus:

        public void GameStartObjectInitialization() {
            Debug.Log("Methode \"GameStartObjectInitialization\" muss noch fertig geschrieben werden!");

            // Initialisierung des Spielers bei Spielstart
            for (int i = 0; i < LD_SP_Save_Data_Object_Manager.Instance.characterList.Count; i++) {
                Instantiate(
                    playerPrefab,
                    new Vector3(
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerPos.x,
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerPos.y,
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerPos.z),
                    new Quaternion(
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerRotation.x,
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerRotation.y,
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerRotation.z,
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerRotation.w)
                    );
            }

            // Initialisierung der Childs bei Spielstart
            for (int i = 0; i < LD_SP_Save_Data_Object_Manager.Instance.characterList.Count; i++) {
            }

            // Initialisierung der Enemys bei Spielstart
            for (int i = 0; i < LD_SP_Save_Data_Object_Manager.Instance.enemyList.Count; i++) {
            }
        }

Ich lege zwar den Player-Prefab fest, aber was mache ich, nach dem das Objekt erstellt wurde. Bei Player ist es ja noch recht einfach, den könnte ich über seinen Tag suchen.

Problematisch wirds dann halt bei Enemy und Child, da können dutzend Objekte existieren, alle haben entweder Child oder Enemy als Tag. Damit fällt die Möglichkeit aus.

EDIT: An manchen Tagen denk ich zu kompliziert:

            for (int i = 0; i < LD_SP_Save_Data_Object_Manager.Instance.characterList.Count; i++) {
                GameObject testGO = Instantiate(
                    playerPrefab,
                    new Vector3(
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerPos.x,
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerPos.y,
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerPos.z),
                    new Quaternion(
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerRotation.x,
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerRotation.y,
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerRotation.z,
                                LD_SP_Save_Data_Object_Manager.Instance.characterList[0].playerRotation.w)
                    );
                testGO.GetComponent<LD_SP_Controller_Player>().mana = 100;
            }

GameObject zuweisen und über die Komponente die Methode zum Laden der Daten ausführen oder wie in dem Fall die Variable ändern.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hab mich nun auch mal mit Pooling beschäftigt, so richtig Sinn macht Pooling ja nur, bei vielen Objekten. Bei 20 oder 30 Enemies auf der Karte macht es keinen Sinn, bei 50 oder 100 eher schon oder wirklich richtig bei Gewehrkugeln. Ab wann meint ihr, lohnt sich Pooling wirklich?

Ich überlge gerade, ob ich es in mein Spawn-Script einbaue. Auf der Karte wird es mehrere Spawner geben, die unterschiedliche Tiere spawnen, per Zufallsprinzip. Frage ist halt, ob sich der Aufwand lohnen würde mit Pools zu arbeiten. Es werden vielleicht 50, vielleicht 60 Tiere auf der Karte sein, vielleicht 80, kommt auf die Kartengröße an. Ein Spawner wird vermutlich um die 5 - 10 Tiere halten. Wenn der Spieler einen Bereich verlässt, sollen die Tiere nach einer Weile verschwinden.

Überlege gerade was ich hier mache. Wenn die Tiere aus der Kamera verschwinden, werden diese doch eh nicht mehr gerendert bzw. verarbeitet, richtig? Würde es sich hier lohnen überhaupt das GameObject zu deaktivieren?

Andere Frage, der Spieler erlegt ein Tiere, würde es bei einer geringen Anzahl von Enemies nun mehr sinn machen, dass Objekt zu zerstören oder zu deaktivieren und neu zu positionieren. Beisst sich aber vielleicht mit der Idee, dass Spawnen der Tiere etwas per Zufall zu machen, sprich was für ein Tier gespawnt wird.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Am 21.9.2019 um 12:05 schrieb Kojote:

Ab wann meint ihr, lohnt sich Pooling wirklich?

Kommt drauf an, wie du "sich lohnen" definierst. Beim Pooling geht es darum, dass beim Löschen von Objekten Müll entsteht, und du über den klassischen Garbage Collector nur sehr wenig Kontroller hast. Der kommt dann irgendwann an und nimmt sich einfach die Zeit, den Müll wegzuräumen. Und je mehr Müll du dann hast, desto größer die Auswirkungen. Das kann sich z.B. in einem kurzen Stocken des Spiels manifestieren. Unity bau aktuell an einem intelligenteren GC, mit dem sich die Spielregeln wieder ändern könnten, aber... mal sehen, wie das so wird.

Pooling lohnt sich meiner Meinung nach bei jedem Objekttypen, der ansonsten mehrfach instanziiert und wieder gelöscht wird. Nach dem Motto "Klein Vieh macht auch Mist".... und weil ich mein Pooling-Paket benutze, mit dem es einfach so gut wie kein Mehraufwand ist, knall ich einfach Pooling auf alles drauf, das diesen Kriterien entspricht.

Am 21.9.2019 um 12:05 schrieb Kojote:

Wenn die Tiere aus der Kamera verschwinden, werden diese doch eh nicht mehr gerendert bzw. verarbeitet, richtig?

Nicht mehr gerendert: Ja. (Stichwort Frustum Culling)

Nicht mehr verarbeitet: Alle Komponenten auf dem Objekt arbeiten noch. Sogar der Renderer, der ja feststellen muss, ob er jetzt wieder mit rendern anfangen muss. Wäre sonst ja auch doof - Stell dir vor, die Tiere laufen permanent nach Norden - verschwinden aus dem Bild, du schaust nach einer Minute wieder hin - und sie sind nicht weiter gekommen.

Da es super doof wäre, wenn Unity ungefragt deine Spielmechanik ändert (also deine Tierwanderung stoppt), nur weil die Kamera woanders hinzeigt, macht Unity da einfach gar nichts außer Draw Calls zu stoppen. Oh, und die Animator-Komponente hat ein Häkchen, das bestimmt, ob Animationen off-screen weiterlaufen. Aber alles weitere musst du machen.

Glücklicherweise ist das nichtmal schwer. Wenn es wirklich nur um das Sparen von Ressourcen geht, wenn ein Objekt nicht mehr gerendert wird, dann kannst du MonoBehaviour.OnBecameVisible und MonoBehaviour.OnBecameInvisible nutzen.

Das ganze GameObject solltest du keinesfalls deaktivieren, weil der Renderer sonst aufhört zu evaluieren, ob das Objekt wieder sichtbar geworden ist. Es sei denn, du hast ganze Bereiche, die du als Ganzes deaktivieren willst. So wie bei Paper Mario die Inneneinrichtung der Häuser - da könntest du die Tür als Trigger benutzen, alle Objekte im Haus zu aktivieren und zu deaktivieren.

Am 21.9.2019 um 12:05 schrieb Kojote:

Objekt zu zerstören oder zu deaktivieren und neu zu positionieren

Letzteres ist 1:1 Pooling. Und dazu hab ich ja schon was gesagt ^^

Am 21.9.2019 um 12:05 schrieb Kojote:

Beisst sich aber vielleicht mit der Idee, dass Spawnen der Tiere etwas per Zufall zu machen, sprich was für ein Tier gespawnt wird.

Klar, wenn die Objekte eher unterschiedlich als gleich sind, wird Pooling etwas weniger interessant. Je unterschiedlicher die Objekte sind, desto mehr Initialisierungsarbeit musst du leisten... das heißt sowohl mehr Arbeit für dich (was doof wäre), aber auch mehr Arbeit für das Programm. Und je mehr das Programm bei der Pooling-Variante arbeiten muss, desto geringer ist der Performancegewinn gegenüber stumpfem Instanziieren. Heißt nicht, dass man bei ein bisschen Initialisierungscode gleich Pooling vom Tisch wischen sollte - der GC ist nach wie vor einer der größten Halunken in Sachen Performance kaputt machen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Erst mal Danke, für deine ausführliche Antwort! :)

vor 21 Stunden schrieb Sascha:

Glücklicherweise ist das nichtmal schwer. Wenn es wirklich nur um das Sparen von Ressourcen geht, wenn ein Objekt nicht mehr gerendert wird, dann kannst du MonoBehaviour.OnBecameVisible und MonoBehaviour.OnBecameInvisible nutzen.

Wie immer hällt sich Unity in seiner API kurz und bündig:

Zitat

Beschreibung

OnBecameInvisible wird aufgerufen, wenn der Renderer von keiner Kamera mehr sichtbar ist.

Diese Nachricht wird an alle Skripte gesendet, die an den Renderer angehängt sind. OnBecameVisible und OnBecameInvisible sind nützlich, um Berechnungen zu vermeiden, die nur notwendig sind, wenn das Objekt sichtbar ist.

Wenn es ausführlicher geworden wäre, wäre das bestimmt schlimm geworden. :D

Spaß beiseite, es spart Resourcen, welche genau?

EDIT: OK falsch verstanden, Unity macht da nix, außer mir die Methode zur Verfügung stellen und ich kann hier sagen, was passieren soll, wenn nicht mehr gerendert wird. Tja was kann ich dann alles auschalten... 😕

vor 21 Stunden schrieb Sascha:

So wie bei Paper Mario die Inneneinrichtung der Häuser - da könntest du die Tür als Trigger benutzen, alle Objekte im Haus zu aktivieren und zu deaktivieren.

Ja, hier macht es auch wirklich Sinn, die Objekte können ja deaktiviert und aktiviert werden, wenn der Spieler das Haus verlässt und betritt. Hier muss ja auch nichts weiterberechnet werden, wenn der Spieler nicht im Haus ist. Stimmt schon, komplett deaktivieren ist blödsinn und würde, denke ich bei dem Haufen von Tieren keinen Sinn machen, außer, wenn das Gebiet wirklich groß ist, und ganze Bereiche deaktiviert werden und schon wieder rechtzeitig deaktiviert werden, wenn der Spieler in die Nähe kommt, dass er auch sieht, dass wirklich etwas passiert ist und die Tiere nicht immer noch fast am selben Platz stehen.

Aber um noch mal auf Pooling zurück zu kommen. Pooling ist ja nun eher für das aktivieren und deaktivieren von Objekten gedacht, wenn diese "sterben". Statt zu zerstören, wird nach der Dead-Animation das Objekt nicht zerstört, sondern deaktiviert, alles auf 0 gesetzt und wartet auf seine Reaktivierung, hab ich das so richtig verstanden? Muss mir das noch mal ansehen, aber von dem Gesichtspunkt aus, könnte man ja auch, um abwechslung zu erzeugen, ein Zufälliges Objekt aus dem Pool spawnen lassen.

EDIT: Also wenn ich so lese, ob man Pooling  heutzutage noch nutzen muss/ sollte, ist das so ein Streit wie: "Was war zuerst da? Henne oder Ei?" :D

Und kann mir wer verraten was der Unterschied zwischen Objekt-Pooling und Instanz-Pooling ist?

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 7 Stunden schrieb Kojote:

Tja was kann ich dann alles auschalten...

Naja... alles, was du sowieso nicht mehr brauchst, wenn ein Tier unsichtbar wird. Was das genau ist, hängt komplett davon ab, wie dein Spiel so funktioniert.

vor 7 Stunden schrieb Kojote:

Statt zu zerstören, wird nach der Dead-Animation das Objekt nicht zerstört, sondern deaktiviert, alles auf 0 gesetzt und wartet auf seine Reaktivierung, hab ich das so richtig verstanden?

Jein... ich sag mal so: Mit meinem Pooling-Paket (und sicherlich auch mir anderen) kannst du definieren, was genau passieren soll. GameObject.SetActive ist nämlich auch nichts, was keine Zeit kostet. Normalerweise bin ich ja kein so großer Performance-Nerd, aber bei Pooling geht's halt genau darum... :)

Du kannst also z.B. einstellen, dass Instanzen bestimmter Prefabs nicht deaktiviert werden, sondern z.B. auf Position (0,-10000,0) verschoben und von da dann wieder "hoch geholt" werden. Das ist nochmal eine ganze Ecke schneller als simples deaktivieren - hab das mal benchmarked. Geht natürlich nicht immer, und Deaktivieren bedeutet halt im Zweifelsfall auch, dass das Objekt nicht mehr weiterarbeitet und damit keine Performance braucht. Da man das von Fall zu Fall neu beurteilen muss, was am performantesten ist, kann man sich's bei meinem Paket halt pro Prefab aussuchen.

vor 7 Stunden schrieb Kojote:

Also wenn ich so lese, ob man Pooling  heutzutage noch nutzen muss/ sollte, ist das so ein Streit wie: "Was war zuerst da? Henne oder Ei?" :D

Schaden tut's meiner Meinung nach auf keinen Fall. Insbesondere, wenn das Paket, dass du nutzt, keine großen Änderungen in deiner Projektstruktur erfordert ;)

vor 7 Stunden schrieb Kojote:

Und kann mir wer verraten was der Unterschied zwischen Objekt-Pooling und Instanz-Pooling ist?

Hab da noch nie von einer Unterschiedung gehört, und würde jetzt ohne große zusätzliche Recherche einfach mal vermuten: Es gibt keinen. "Objekte" und "Instanzen" sind in allen realen Situationen, die mir einfallen, Synonyme.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Aber das ist doch genau das, worum es geht!
Du must bei der Queue aber immer darauf achten, dass auch noch ein Objekt zum Aktivieren drin ist. Versuchst du nämlich eins zu aktivieren aber die Queue ist leer, dann haut's dir ne Fehlermeldung raus und im Build würde dir wahrscheinlich das Spiel abschmieren.
Also immer auch den Count der Queue abfragen.
 

if (diesUndDas && theCueueObjects.Count>0){
  GameObject CueueObject= theCueueObjects.Dequeue();
  CueueObject.transform.position = startingPoint.position;
  CueueObject.transform.rotation = Quaternion.identity;
  CueueObject.SetActive(true);
}
else{
  // jetzt vielleicht doch ein zusätzliches Objekt instanzieren und der Queue zuweisen. 
  // Oder aber warten, bis wieder was in der Queue drin ist.
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja ich weis, aber Queue war für mich neu.

Übrigends danke, dass mit dem Count wusst ich net. :)

EDIT: Queue ist echt ne nette Klasse, nur noch ma ne Nachfrage, dass ichs richtig verstanden habe. Mit Enqueue kommt das Objekt in den Queue rein und mit Dequeue wieder raus, klar. Wenn nun das Objekt "zerstört" wird und zurück in den Pool soll, müsste es ja erneut wieder mit Enqueue aufgenommen werden?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...