Jump to content
Unity Insider Forum
Sign in to follow this  
malzbie

Performance in Unity3D

Recommended Posts

Gerade unerfahrene Leute haben starke Bedenken, wenn es um die Performance in Unity geht.

Sie haben aus unterschiedlichen Quellen gehört, dass Dies oder Jenes schlecht sein soll und jetzt gehen sie total ängstlich an das Projekt heran.

Denen möchte ich aber sagen:

 

Macht euch nicht verrückt!

 

Denn wie immer ist alles eine Frage von der Menge an leistungsfressenden Dingen, dem gewünschten Resultat und der Hardware, auf der das Spiel laufen soll. Viele Dinge beziehen sich auf Hardware, die kaum noch genutzt wird, wie z.B. das iPad1, welches nicht mehr als 120MB Ram für das Spiel zur Verfügung gestellt hatte. Oder aber es bezog sich auf veraltete Techniken bei denen Unity schon längst performatere Dinge gebastelt hat, wie die unterschiedlichen Lighting-Arten.

 

Ich will mal versuchen, die einzelnen Bereiche abzuarbeiten.

 

Das Grafische:

 

Alles was ich sehen kann kostet Leistung. Mal mehr, mal weniger! Je mehr Realismus ich in ein Spiel rein bringen will umso mehr Leistung wird verbraucht. Denn jedes Poygon, jedes Licht, jede Schattenberechnung und jedes Material verbraucht etwas.

Diese Dinge belasten meist die Grafikkarte, denn die ist es, die das ja anzeigen und vorher berechnen muss.

Habt ihr ein Spiel mit üppiger Szenerie und schaut euch zur Laufzeit mal den Profiler an, dann seht ihr, dass der Grafikbereich die meiste Leistung schluckt.

Unity ist von sich aus schon bemüht, sparsam zu sein und kann gleichartige Dinge zusammen fassen, wenn sie z.B. das gleiche Material haben und auch vom selben Licht bestrahlt werden. Das reduziert die Drawcalls, die einen Großteil der Performance ausmachen.

Man hat die Möglichkeit Beleuchtungen und Verschattung vor zu berechnen und die Ausleuchtung einer Szene zu backen. Das reduziert ganz massiv die Last auf der Grafikkarte, kostet aber Speicherplatz und natürlich geht die Dynamik der Beleuchtung dadurch flöten.

Aber was bringt es, wenn man genau diese Dynamik haben will. Genau, es bringt nichts!

Mit Shadern sieht es genauso aus. Ein einfacher Shader mit nur einer Textur ist recht sparsam. Er gibt aber auch nicht viel her. Schöner ist er, wenn er glänzen kann, transparent ist, erhaben ist und eben realistisch aussieht. Will ich das haben muss ich ihn auch einsetzen.

Die Geometrie eines Körpers sollte natürlich so sparsam wie möglich aufgebaut sein. Die Zeit, wo ein Oberarm aus 12 Polygonen besteht, ist aber längst vorbei. Ich kann zwar vieles mit einer NormalMap simulieren, aber wenn die Ränder des Polygons zu eckig sind, sieht man das und es wirkt einfach nicht. Also auch da muss ich mich den Qualitätsansprüchen stellen und mehr Polygone nutzen.

Ich kann sogar DirectX11 einsetzen und aus wenigen Polygonen eine Million Polygone machen. Ja, aber eben nur auf einer Grafikkarte, die das auch kann. Egal, wollt ihr die Technik einsetzen dann funktioniert es eben auch nur auf einer Hardware, die das auch kann.

Lasst euch also nicht zu stark einschränken. Ihr werdet natürlich irgendwann merken, dass das Ganze zu viel geworden ist und die Framerate einbricht. Meist lässt sich das aber mit etwas geänderter Beleuchtung, LOD oder anderen Dingen schon wieder beheben. Da aber niemand genau sagen kann, wo die Grenzen liegen werden, bringt es auch nichts, wenn man vorher seitenweise darüber diskutiert.

 

Die Physik:

 

Die physikalischen Berechnungen kosten Leistung, die der Prozessor bringen muss. Und es ist klar, dass viele Berechnungen auch viel Leistung kosten.

Trotzdem ist das jetzt kaum ein Bereich, wo man "extrem" sparen muss. Unity kann ohne weiteres mehrere 100 Objekte gleichzeitig physikalisch berechnen.

Einfach schauen, dass nur die Objekte einen Rigidbody bekommen, die auch einen haben müssen weil sie der Schwerkraft oder anderen Forces folgen sollen und/oder Kollisionen auswerten sollen. Collider so einfach wie möglich halten und lieber einen Box-Collider mehr nehmen, als einen Mesh Collider zu nutzen. Meist muss es nämlich gar nicht ganz genau sein. Merkt eh keiner.

 

 

Der Code:

 

Ja, hier kann und muss man aufpassen. Denn wenige Kleinigkeiten können das System ganz schön verlangsamen. Aber macht euch auch hier nicht verrückt. Denn auch hier ist alles eine Frage der Dosis.

Ein paar extreme Dinge will ich aber mal aufzählen.

 

Als Faustregel gilt, dass jede Unity-eigene Funktion, die im Script steht, auch durchlaufen wird. Egal, ob innerhalb dieser Funktion etwas drin steht oder nicht. Die OnGUI Funktion ist die hungrigste, weswegen man so wenig Scripte wie möglich mit dieser Funktion bestücken sollte.

Legt also nur die Funktionen an, die auch wirklich nötig sind, auch wenn das einsparpotential hier (außer bei der OnGUI) nur gering ist.

 

Immer wenn aus einem Script heraus ein anderes Objekt oder dessen Komponente gesucht wird, kostet es Performance. Es hilft aber nichts. Manchmal muss man einfach nach anderen Objekten oder Komponenten suchen. Um jedoch so wenig Leistung wie möglich zu verbrauchen, sollte man nur einmal danach suchen und einfach die Referenz des Gesuchten in eine Variable speichern.

 

Manche Befehle sind recht hungrig, wie z.B. die Entfernungsmessung über Vector3.Distance. Da sollte man sich überlegen, ob denn wirklich in jedem Frame die Entfernung gemessen werden muss oder reicht es vielleicht auch, wenn es nur wenige Male pro Sekunde passiert. So eine Messung würde ja in jedem Frame eine gewisse Grundlast verursachen, die nicht unbedingt sein muss. Und gerade wenn viele Objekte viele Entfernungsmessungen machen, ist es sinnvoll das Ganze in der Zeit aufzuteilen um die Grundlast zu verringern. So ist das natürlich auch mit komplexen Berechnungen die in einer Schleife ausgeführt werden.

 

SendMessage ist ein teurer Befehl, der an ein gewisses Objekt und an all seine Komponenten etwas sendet. Egal ob die Komponente damit etwas anfangen kann, oder nicht. Diesen Befehl sollte man wirklich nur sparsam nutzen. Will ich einem anderen Script jedes Frame etwas mitteilen, dann ist dieser Befehl dafür total ungeeignet. Für ein einmaliges oder seltenes Senden ist er aber voll ok. Für das ständige Übergeben von Informationen bietet sich an, die Komponente, also das andere Script, vorher zu suchen und als Referenz in eine Variable zu speichern. Jetzt kann ich auf alle Public Variablen oder Funktionen des anderen Scripts zugreifen und sie aufrufen oder manipulieren. Das kostet nicht oder kaum mehr, als wenn es eine Variable des eigenen Scripts wäre.

 

Alles das, was ich da aufgezählt habe macht sich erst ab einer gewissen Menge bemerkbar. Je schwächer die Hardware ist, desto früher merkt man es.

Nur weil etwas viel Performance kostet, müsst ihr nicht darauf verzichten. Ihr solltet aber weise damit umgehen.

Nur weil ich etwas jedes Frame tue, muss das nicht schlecht sein. Es kommt halt immer darauf an, was ich da tue und wie viele Objekte das gleichzeitig machen.

Macht euch vorher Gedanken, was ihr für euer Spiel alles braucht und wie ihr das am besten lösen könnt. Aber lasst euch durch diese Planung nicht blockieren. Fangt einfach an. Vieles ist im Nachhinein leicht änderbar.

Spiele, die auf einem PC schön aussehen sollen und können, können nicht unbedingt genauso für ein Handy übernommen werden. Es macht aber nicht unbedingt Sinn, sich auf den kleinsten gemeinsamen Nenner zu einigen. Manchmal muss man einfach mehrere Versionen für ein und das selbe Spiel bauen, weil die Qualitysettings nicht ausreichen werden.

 

Fazit:

Es ist also alles gar nicht so schlimm!

  • Like 21

Share this post


Link to post
Share on other sites

Schöner Artikel. Was mir fehlt sind aber die Fallstricke, auf die man reinfallen kann. Ich bin zwar noch relativ neu bei Unity, aber ein paar Sachen hab ich schon mitbekommen.

 

So soll man zB die Zahl der Meshes möglichst niedrig halten. Viele Meshes mit wenig vertices brauchen mehr Ressourcen als wenige Meshes mit vielen vertices.

 

Auch bei C# kann man Leistung verschenken. Mehrdimensionale Arrays sollen etwas langsamer sein als zB bei C++, die for Schleife ist schneller als eine foreach Schleife usw

 

Oder kurz gesagt, Programmierfehler, die zwar funktionieren, aber unnötig Performance kosten, wären interessant

Share this post


Link to post
Share on other sites

Die Punkte, die du ansprichst, kann man nicht so einfach verallgemeinern.

 

Um ein Mesh anzuzeigen muss es auch als Ganzes geladen sein, wenn du also 10 Große statt 100 kleinere hast, dann kann das ein Problem sein.

Ein kleines Mesh fällt schnell außer Sicht und kann dann entladen werden, ein großes Mesh kann dann meistens seltener entladen werden, auch wenn es z.B kaum sichtbar ist.

Unter anderem teilt man deswegen sein Terrain auch in Chunks, statt in ein großes Mesh.

 

Der Zugriff auf ein Mehrdimensionales Array lässt sich schon in Millisekunden bei modernen Rechnenern schwer angeben, die Zeit, die so ein Zugriff kostet, musst du dir in den meisten Fällen schon in Ticks ausgeben lassen, so schnell läuft das alles ab.

Das gleiche gilt, meistens, für die Ersparnis bei Nutzung von For statt Foreach Schleifen.

Wobei For auch nicht immer schneller als Foreach ist, aber dazu findet man genügend im Netz.

 

Um hier in Bedrängnis zu kommen musst du also schon ziemlichen Blödsinn anstellen :D

Man sollte in der jeweiligen Situation schauen was am besten (auch für den Workflow !) geeignet ist.

  • Like 1

Share this post


Link to post
Share on other sites

Das man bei den Meshes das richtige Maß finden muss ist klar. Aber so etwas meine ich

 

Das sich die Unterschiede bei Arrays und for Schleifen erst im Bereich von größeren Datenmengen bemerkbar machen ist auch klar. Aber gerade wenn man mehrere zig tausend oder mehr Iterationen pro Frame über einen großen Datensatz machen muss können sich ein paar Mikrosekunden mehr oder weniger schon bemerkbar machen. Alles schon erlebt.

Share this post


Link to post
Share on other sites

Das ist ja alles richtig. Aber das ist genau das, worum es in meinem Post geht!

Die Dosis macht's! Deswegen nicht verrückt machen und sich selbst im Weg stehen, nur weil es vielleicht eine minimal bessere Möglichkeit gibt.

Wer so ans Entwickeln heran geht, wird nie fertig.

Meist kommt man gar nicht in den Bereich, wo es sich spürbar auswirkt. Und wenn doch, dann kann man immer noch optimieren.

Nachträgliches Optimieren gehört einfach zum Entwickeln dazu. :)

  • Like 3

Share this post


Link to post
Share on other sites

Genau so sehe ich das auch - Ich habe vor 2 Jahren angefangen einen Simulator zu entwickeln. Vor rund 1,5 Jahren habe ich diesen veröffentlicht (war eigentlich nur für mich gedacht). Da ich einer der ersten (glaube sogar der erste) war, der so einen Sim rausgebracht hat, waren die Anforderungen an mich extrem hoch. Ich persönlich versuche natürlich immer das beste heraus zu holen. Versuche immer um die 300FPS zu liegen (auf meinem PC) - Hintergrund ist, dass 50% meiner User Mac User sind und die anderen 50% haben eher Leistungsschwache Laptops oder Ähnliches.

 

Wenn man sich aber an verschiedene Regeln hält (LOD, Baked Lightmaps, Occlusion Culling, Mesh Baker, saubere Scripts und vor allem Singleton, keine leeren Schleifen (void Update), komprimeirte Grafiken etc.), kann man schon einiges heraus holen. Ich versuche immer meinen Sim so detail getreu zu halten wie es nur eben geht. Was bei einem Flugsimulator (fpv quads) nicht immer einfach ist. Die Kamera bewegt sich sehr schnell, kann sehr hoch fliegen (somit kein occlusion culling) und muss noch extrem genaue Physics haben.

 

Das einzige was mir immer noch Kopfschmerzen bereitet, sind Bäume und Grass. Aus diesem Grund bin ich auch auf diesen Thread hier gelandet.

 

VG

Share this post


Link to post
Share on other sites

Ich bin bei meinem Projekt auch einfach so dran gegangen, ohne richtig darauf zu achten. Nur einmal kam ich an einen Punkt, wo es sehr stark geruckelt hat, wenn man in eine bestimmte Richtung geschaut hat. Hab dann mal geguckt, was dort so liegt und Schuld war eine Blumenwiese, bei der ich es (Die hatte ich irgendwann morgens voll verpennt "dahingehauen") leicht übertrieben hatte. Da waren dann mal locker an die 2000 einzelne Blumenmodelle :D Joa, am Ende hatte ich nur noch 100 Blumen und es sah fast genauso aus wie vorher. Deswegen, konnte man auch ganz leicht beheben, als es soweit war .

 

Und einmal hatte ich ne Falle gebaut, wo man in eine Grube mit Dutzenden Mannsgroßen Fledermäusen gefallen ist, die einen dann töten.... Naja, das fand mein Rechner auch nicht gut. Habs dann in Genickbruch durch den Fall geändert :D

 

Lange Rede, kurzer Sinn: Stimmt, wenn es soweit ist, kann man es ändern, vorher sich Verrückt machen braucht man nicht :)

Share this post


Link to post
Share on other sites

Ab Unity 2018.1 wird sich in Sachen Performance einiges ändern, da kommt das neue C# Jobsystem und das neue Entity Component System, das vermutlich über lang das alte MonoBehaviour ersetzen wird. Die Art wie man Unity Spiele Programmiert wird sich ändern und einigen Stellen einiges am Umdenken erfordern.

Z.B. im Jobsystem sind keine Referenztypen mehr erlaubt, alles nur structs.
Das neue Komponentensystem teilt Daten und Businesslogik, das heißt in den Komponenten die man an das ""GameObject" (Entity im neuem System) hängt werden nur mehr die Daten als struct gehalten, kein Update() Start() usw. Das passiert alles in eigenen Klassen, denen Unity automatisch ein NativeArray der Komponenten übergibt, die man sich Wünscht.

Wer sich mehr dafür Interessiert und es noch nicht kennt, hier ein Link zu einem Video dazu von der der Unite Austin 2017:
https://www.youtube.com/watch?v=tGmnZdY5Y-E

 

  • Like 3

Share this post


Link to post
Share on other sites

Naja, daß betrifft dann denke ich "nur" die Skripte und ich denke wenn man bisher Skripte in Unity "unperformant" verwendet hat, dann schafft man das mit dem neuen System bestimmt auch *g.
Was mich eher erschreckt, was dann wieder mit vorhandenen Assets passiert, hoffentlich werden diese über Unity ordentlich migriert.

Share this post


Link to post
Share on other sites
vor 5 Stunden schrieb runner78:

da kommt das neue C# Jonsystem und das neue Entity Component System, das vermutlich über lang das alte MonoBehaviour ersetzen wird. Die Art wie man Unity Spiele Programmiert wird sich ändern und einigen Stellen einiges am Umdenken erfordern.

Sorry, aber wo zum Geier hast du das her? Nach meinen Informationen sind die neuen Systeme reichlich optional und keineswegs kompletter Ersatz für das aktuelle System. Wenn du die neuen Sachen nutzen willst, musst du neue Dinge lernen. Ist klar. Aber was du da schreibst, erweckt den Eindruck, als könnte ich nächstes Jahr nicht mehr so arbeiten, wie ich es jetzt tue, auch wenn ich es wollte.

Share this post


Link to post
Share on other sites

Ich habe mir das Video jetzt so zur Hälfte angeschaut und bis jetzt ist das eigentlich einfach "nur" ne Erweiterung. Also, es ist schon eine recht mächtige Erweiterung, aber eben kein Ersatz.

 

Share this post


Link to post
Share on other sites

Eigentlich wird es mehr als eine Erweiterung, ich glaube in einem Anderen Video habe ich gehört, dass man die Unity eigenen Komponenten auf das neue System umgestellt werden, wie z.B. die Kamera usw. (Ich finde das Video nicht)

In diesem Video wird kurz dazu was zum neuem Behaviour gesagt (35:47). ich interpretiere das so, dass die aktuelle MonoBehaviour irgendwann in ferner Zukunft rausfliegt, aber aus Kompatibilität uns noch lange erhalten bleibt:
https://www.youtube.com/watch?v=IqD88cPHbdU

 

Share this post


Link to post
Share on other sites

Also ich kann mir beim besten willen nicht vorstellen, dass Unity diesen Schritt gehen sollte. Schließlich gehört das jetzige System doch zu den Stärken von Unity, da es so benutzerfreundlich ist. Wenn das wirklich komplett ersetzt werden soll, würden doch viel mehr Leute abgeschreckt werden. Mit Structs umzugehen und dann an anderen Stellen wieder in Klassen zu denken erfordert viel mehr Einarbeitung für Anfänger als es jetzt der Fall wäre. Damit würden sich die Jungs von Unity doch selbst ins Bein schießen. Konnte mir das Video jetzt aber auch nicht ansehen bin noch nicht Zuhause. Kann auch sein, dass ich das grade missverstehe.

Share this post


Link to post
Share on other sites

Benutzerfreundlich ist leider ein Zweischneidiges Schwert, es lädt zu schlechten code schreiben ein. 
Ich finde, das neue Entity Component System ist nicht so viel Komplizierter, teilweise sogar einfacher.
Es sollte z.B. Möglichkeit sein, eine Komponente zu schreiben, die bei allen Enities/GameObjects die Transform Komponente manipuliert, ohne manuelle die Komponente an irgendein anders Object zu hängen.

In dem Video oben hat Lucas Meijer gesagt, als sie damals MonoBehaviour konzeptioniert hatten, wussten sie nicht was sie taten.

Nemen wir ein beispiel mit ca. 100.000 Gegenern (Extrembeispiel basierend auf dem Jobsystem Live Demo) und jeder hat ein MonoBehaviour mit Start() Update() vielleicht noch OnCollisionEnter() usw.

Im Aktuellen System werden dabei für jede Componente an jedem Gegner eine neue Instanz erstellt.
In jedem Frame muss Unity also bei allen 100.000 Instanzen die Update() aufrufen, und zu gegebener Zeit auch die anderen Message Methoden. Dabei machen alle Komponenten alles das selbe. Das ist nicht besonders effizient.

Im Neuem System funktioniert das anders, Da werden nur noch Daten-Componenten an ein Entity gehängt.
Die Logik in einer separaten Klasse hat eine OnUpdate(), die wird in einem Frame nur noch ein mal aufgerufen.
Dort iteriert man dann über ein Array der Daten-Komponenten. Unity benutzt dafür die neue Native Collections, bei denen Unity intern den Speicher selbst verwaltet und für Performance optimiert.

Für genauere Detail muss man auf die 2018.1 warten.
Eine Möglichkeit ist auch, dass das neue System das alte bereits mit 2018.1 ersetzt wird, und die alten MonoBehaviour intern auf das neue System übertragen werden.

Share this post


Link to post
Share on other sites

Ist meine Rede, wer die Update-Methode verwendet hat schon einen konzeptionellen Fehler gemacht. Ausnahme von dieser "Regel" ist die Verwendung der Updatemethode für den Spielerinput, ansonsten wurde ich niemals an mehreren Entitäten die Updatemethode laufen lassen.
Ziel sollte immer sein, eine Aktion an einer Entität (Instanz in der Szene) über ein Event auszulösen. Aktionen die an mehreren Entitäten in einem Intervall ausgeführt werden müssen (sagen wir jedes Frame) sollten über eine "Controllerentität" ausgeführt werden. Diese Controllerentität kennt dabei alle Einzelentitäten und die Aktionen die auf diesen ausgeführt werden sollten. Bei der Controllerentität wäre ein Einsatz der Updatemethode wieder "erlaubt". Aber selbst hier sollte man sich überlegen, muss eine Aktualisierung zwingend jedes Frame erfolgen oder reicht es auch eine Aktualisierung jede 1/10 Sekunde auszuführen (das hängt vom Kontext ab).

Share this post


Link to post
Share on other sites

Für Leute die sich weniger mit der Zukunft von Unity beschäftigen mal ein kleines update zur Performance von Unity die zukünftig mit ECS Möglich ist.

Zur Unite LA 2018 hat Unity eine Demo Project Vorgestellt namens Megacity, die Eckdaten der Scene:

  • 4.500.000 Mesh renderer
  • 100.000 individuelle Audioquellen
  • 5.000 dynamische Fahrzeuge
  • Constant 60 fps

https://www.youtube.com/watch?v=j4rWfPyf-hk

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×