Programmiereffizienz

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • Programmiereffizienz

      Original: Coding efficiency
      Autor: Simon Donkers
      Homepage: gamemaker.simondonkers.com/

      übersetzt von Windapple mit Genehmigung von Simon Donkers


      Viele Benutzer beklagen sich das Game Maker langsam ist. Die Geschwindigkeit hängt aber stark von deiner Programmiertechnik ab. Vieles kann viel schneller gelöst werden. Angefangen bei der Benutzung von lokalen statt globalen Variablen bis hin zur Abfrage von Kollisionsereignissen kann viel opimiert werden.

      Kollisionen

      Game Maker hat ein sehr fortschrittliches Kollisionssystem welches einige erweiterte Kollisionsbestimmungsmethoden erlaubt. Jedoch brauchst du das in 99% aller Fälle überhaupt nicht. Dadurch, das dieses System so komplex ist, ist es nicht besonders schnell. Also schauen wir uns mal einige grundlegende Dinge in Sachen Kollisionen an. Wir haben ein Objekt Spieler hast und 100 Objekte Kugeln die sich zum Spieler bewegen. In dem Fall kannst du allen 100 Objekten ein Kollisionsereignis mit dem Spieler verpassen. Du kannst dem Spieler aber auch nur eines geben. Die letzte Möglichkeit ist bedeutend schneller. Kollisionsereignisse sollten immer von dem Objekt ausgelöst werden, von dem es die wenigsten Instanzen im Spiel gibt. Man kann dies auch effizienter lösen, indem man den 100 Kugeln ein Elternobjekt gibt und dem das Kollisionsereignis einträgt. Die macht das Programmieren einfacher, da
      A: du den Code nur einmal schreiben musst
      B: , wenn dein Code für eine Kugel funktioniert, für alle funktioniert.

      Trotzdem hast du hierbei noch die Möglichkeit deinen Kugeln spezielle Fähigkeiten zu verleihen. Du kannst z.B. jeder Kugel im Erzeugungsereignis eine Variable Schaden geben. Je höher die Variable desto mehr Schaden erleidet der Spieler, du ziehst im Kollisionsereignis einfach den Schaden vom Leben des Spielers ab. Aber was, wenn einige Kugeln spezielle Kollisionseffekte bekommen sollen? Für den Fall kannst du beispielsweise im Elternobjekt ein benutzerdefiniertes Ereignis mit dem Standardverhalten bei einem Einschlag einrichten. Die speziellen Kugeln erhalten einen eigenes benutzerdefiniertes Ereignis und im Kollisionsereignis führst du einfach das benutzerdefinierte Ereignis aus. Auf diese Weise können 90% der Kugeln dasselbe Verhalten bekommen und der Code trotzdem nur einmal geschrieben werden.

      Ein anderes wichtiges Element auf dem Weg zur optimierten Kollision ist die präzise Kollisionsprüfung. Präzise Kollisionsprüfung ist eine nette Sache, wird jedoch aber sehr oft gar nicht gebraucht. Mit einem einfachen Begrenzungsrahmen kommst du meist schon aus und ersparst dem Game Maker eine Menge Rechenarbeit. Präzise Kollisionsprüfung wird in den meisten Spielen ässuerst selten benötigt. Auch andere Entwicklungsumgebungen machen selten gebrauch von dieser Möglichkeit da sie äussert rechenintensiv ist.

      Abschließend betrachten wir uns mal die soliden Objekte. Die Verwendung von soliden Objekten macht die Kollisionsprüfung um einiges einfacher aber kaum jemand benutzt diese Möglichkeit richtig. Solide Objekte sind einfach feste Objekte wie ein Boden in einem Plattformspiel, deswegen können einfache Kollisionsroutinen genutzt werden. Wenn alles wodurch man nicht durchkönnen soll solide ist kann man einfach die place_free Funktion benutzen um zu prüfen, ob man irgendwo stehen kann, ohne jedes einzelne Objekt zu überprüfen. Was die Benutzer nicht wissen ist, das Kollisionserignisse mit soliden Objekten ganz anderes behandelt werden als solche mit nicht soliden Objekten. Ich gehe hier jetzt nicht ins Detail, jedoch ist die Chance extrem groß in einem sich bewegenden soliden Objekten stecken zu bleiben, da die Game Maker Kollisionsroutinen hier leicht versagen. Erinnere dich also immer an folgende Regel: "Niemals sich bewegende solide Objekte benutzen!!!". Mark Overmars sagte mir, dies sei der am meisten in Spielen gemachte Fehler. Benutze also niemals sich bewegende solide Objekte.

      GML

      Es gibt viele mögliche Programmiertechniken in GML. Zwischen ihnen gibt es geringe Geschwindigkeitsunterschiede aber diese sind nicht wirklich zu beachten. Die große Bremse steckt in ineffizienten Programmroutinen. Das bekannteste Beispiel ist die switch Funktion:

      GML-Quellcode

      1. if a=b then
      2. {
      3. //do this
      4. }
      5. else
      6. {
      7. if a=c then
      8. {
      9. //do this
      10. }
      11. else
      12. {
      13. if a=d then
      14. {
      15. //do this
      16. }
      17. else
      18. {
      19. //do default action
      20. }
      21. }
      22. }
      Alles anzeigen


      Dieser Code kann hiermit ersetzt werden:

      GML-Quellcode

      1. switch a
      2. {
      3. case b: //do this
      4. break;
      5. case C: //do this
      6. break;
      7. case d: //do this
      8. break;
      9. default: //do this
      10. break;
      11. }
      Alles anzeigen


      Dieser Code benutzt nur zwei Klammern statt zwölf was die Fehleranfälligkeit des Codes sehr reduziert. Er ist auch viel leichter zu lesen. Jetzt siehst du auf den ersten Blick das der Code bei d für a=d (vorrausgesetzt a ist nicht b und nicht c) ausgeführt wird. Beim vorherigen Codestück musstest du dich erst durch drei If-Abfragen hangeln um darauf zu kommen.

      Eine weitere Möglichkeit des effizienten Programmierens bieten Einrückungen. In meinem Beispiel mit den If-Abfragen kann man klar sehen welche Schleife zu welcher Abfrage gehört da Einrückungen verwendet wurden . Mit guten Einrückungen kann man den Code um einiges leichter lesen. Dasselbe gilt bei klaren Ressourcenbezeichnungen. Das heißt kein sprite0, nutze zum Beispiel spr_player. Du musst nicht spr_ benutzen, du kannst auch dein eigenes System aus Präfixen entwickeln. Dies macht das Lesen des Codes für dich und jeden anderen um einiges einfacher, und das hört nicht bei den Ressourcennamen auf. Dasselbe gilt für Variablennamen. Geben eine Bezeichnung an die auf den Inhalt schließen lässt.

      Eine andere Sache die man lernen sollte ist skriptbezogene Variablen zu verwenden. Diese werden schneller als lokale und noch schneller als globale Variablen verarbeitet. In vielen Fällen brauchst du eine Variable nur temporär um einen Wert in ihr zwischenzuspeichern den du danach nicht mehr brauchst. Skriptbezogene Variablen existieren nur in diesem Skript und werden am Skriptende gelöscht, der benutzte Speicher wird freigegeben.

      Eine andere Sache die man im Kopf haben sollte ist die Geschwindigkeit von Funktionen. Einige Funktionen sind schneller als andere. Ich schreibe hier keine Liste mit Funktionen und deren Geschwindigkeiten. Einige dieser Funktionen findest du aber in den beiden GMC Threads, die am Ende dieses Artikels verlinkt sind. Es gibt auch Situationen in denen sinnloser Code ausgeführt wird. Zum Beispiel ist string('5') sinnlos. Du musst keine Zeichenkette in eine Zeichenkette konvertieren. Eine weitere Sache ist es, mehrere Schritte in einen zusammenzufassen.

      GML-Quellcode

      1. ip=mplay_ipaddress();
      2. show_message(ip);


      Kann kürzer so geschrieben werden:

      GML-Quellcode

      1. show_message(mplay_ipaddress());


      Dieses einfache Beispiel gilt auch für viele andere Dinge. Anstatt image_index=direction/360*image_number zu benutzen wenn du genau weißt, das das Sprite 36 Bilder hat, verwende dies: image_index=direction/10; Es gibt viele Möglichkeiten Code zu verkürzen um die Geschwindigkeit zu erhöhen.

      Ein anderes Beispiel ist das Speichern von komplex berechneten Werten. Die Funktion zur Ermittlung der IP Adresse ist recht langsam. Statt die IP Adresse in jedem Step zu ermitteln machst du das nur im Erzeugungsereignis und speicherst sie in einer Variable. Das gilt auch für oft genutzte Dinge in einem Skript. Frage den Wert einmal ab und speichere ihn in einer skriptbezogenen Variable zwischen. Zum Beispiel string_height_ext ist bei großen Textblöcken sehr langsam. Das Ergebnis kann aber zwischengespeichert werden und so schnell verarbeitet werden. Es gibt viele Arten langsamen Code möglichst selten zu verweden, experimentiere selber etwas damit herum.


      Ereignisnutzung

      Denke immer darüber nach welcher Ereigniscode ausgeführt werden muss. Künstliche Intelligenz kann besser jede Sekunde als jeden Step arbeiten. Mit einer Raumgeschwindigkeit von 30 spart dies eine Menge ein. Aber selbst wenn man etwas nur jeden zweiten Step macht spart das 50% ein. Verlege den Code einfach vom Step- in ein Alarmereignis und setze am Ende des Alarmereignisses den Alarm auf 2.

      Du solltest das Step-Ereignis so weit wie möglich vermeiden. Dasselbe gilt für das Zeichenereignis. Dies wird nämlich für jeden Step und dort für jede Ansicht ausgeführt. Wenn du 3 Ansichten und eine Raumgeschwindigkeit von 60 hast wird dein Code 180 mal pro Sekunde ausgeführt. Das Zeichnen geht im GM6 einigermaßen schnell aber du solltest immer noch großen Wert auf das Optimieren legen. Wenn etwas nur in view0 gezeichnet werden soll, verwende: if view_current=0 then draw_sprite(sprite,image,x,y); Dadurch sparst du 66% im Zeichenereignis, nur weil du etwas nicht zeichnest was man nicht sehen würde.

      Objekte deaktivieren/zerstören

      Game Maker bietet die Möglichkeit ein Objekt kurzzeitig zu deaktivieren. Das bedeutet, das kein einziges Ereignis dieses Objektes mehr abgearbeitet wird. Dadurch wird viel Prozessorzeit eingespart. Du kannst auch alle Objekte, die den Raum verlassen, Zerstören. Die Objekte würden sonst bestehen bleiben und ihre Ereignisse ausführen. Wenn du sie noch brauchst dann deaktiviere einige Funktionen wenn sie nicht sichtbar sind. Ein Objekt ausserhalb der Ansicht muss nicht sichtbar sein. Stelle also die Sichtbarkeit ab und schon wird das Zeichenereignis nicht mehr abgearbeitet. Auch kannst du die künstliche Intelligenz bei diesen Objekten abstellen, die Einheiten müssen nichts tun wenn man sie erst in einer halben Stunde antrifft. In einem Plattformspiel muss die künstliche Intelligenz nicht im ganzen Level aktiv sein, sondern nur im dem Bereich in dem man sich aufhält. Passe beim Deaktivieren aller Objekte ausserhalb des Raumes/der Ansicht auf, dass du nicht versehentlich das Steuerobjekt selbst deaktivierst. Beachte, dass die Funktion zum zählen von Objekten keine deaktivierte Objekte zählt. Wenn dein Spiel also endet wenn keine Objekte mehr da sind muss die besonders geregelt werden.


      Räume

      Wie zuvor besprochen wird das Zeichenereignis in jeder Ansicht abgearbeitet. Versuche also so wenig Ansichten wie möglich zu verwenden. Behalte auch den room_speed niedrig um weniger Ruckeln im Spiel zu provozieren da so weniger Code pro Sekunde berechnet werden muss. Du kannst ein Spiel auch durch die Benutzung von kleinen Räumen beschleunigen. Je weniger Objekte in einem Raum sind desto schneller läuft es. Du kannst ein Level oft in mehrere Räume teilen. Mit ein wenig Geschick kannst du es sogar so programmieren, das in den nächsten Raum gerutscht wird und dieser genauso aussieht wie der alte, soass der Wechsel gar nicht bemerkt wird. Dies kann beispielsweise mit einem langen Gang in beiden Räumen gelöst werden. Mit gutem Timing bemerkt der Spieler nie das er den Raum gewechselt hat. Du kannst auch das Zeichnen einer Hintergrundfarbe bei der Verwendung von Ansichten abstellen. Dies spart auch etwas Zeichenarbeit. Manchmal kann man auch Objekte durch Tiles ersetzen. Du musst dann nur Objekte an den Rand eines Bereiches setzen anstatt ihn damit zu füllen. Je weniger Objekte desto schneller das Spiel.


      Fazit

      Es gibt viele Möglichkeiten des Optimierens. Ich kann sie nicht alle hier besprechen. Eine gute Lernmöglichkeit ist, sich den Code anderer Benutzer anzuschauen und aus diesem zu lernen. Vielleicht erlernst du dadurch auch eine Technik die hier gar nicht besprochen wurde. Probier es einfach aus. Achte immer darauf die Menge an Code auf das nötigste zu reduzieren. Dies erspart die Programmierzeit und dem Game Maker Rechenzeit. Faulheit nützt :)

      Verwandte Links:

      Game Maker Hilfe, Überlegungen zur Geschwindigkeit
      Big Brain, multiplayer performance tips
      GMC, Functions to avoid
      GMC, GM functions measured for execution speed


      Dieser Inhalt ist unter einer Creative Commons-Lizenz lizenziert.
      "Die Erde ist ein Irrenhaus. Dabei könnte das bis heute erreichte Wissen der Menschheit aus ihr ein Paradies machen. Dafür müsste die weltweite Gesellschaft allerdings zur Vernunft kommen."
      - Joseph Weizenbaum
    • RE: Programmiereffizienz

      Je häher die Variable desto mehr Schaden erleidet der Spieler,
      ...höher...
      Vieles kann viel performanter gelöst werden
      performanter klingt komisch; einfacher, effektiver, schneller? Sonst ist mir nichts aufgefallen... gute Arbeit, wenn auch nur übersetzt.

      Dragoon
      int (*x(*x(int))[5])(int*);
      Confused? Yes, it's C!
    • Benutzer online 1

      1 Besucher