Die GML Trickkiste

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

    • Die GML Trickkiste

      oder "tipp-sammlung", wie man es nun auch nennen mag :P

      Ich habe mir mal gedacht, das sowas hier noch fehlt. Sinn dieses Threads ist es, Tricks und Kniffe aus dem Bereich GML festzuhalten. Sinn der Trickkiste ist es nicht, hier Scripte zu posten, die man einfach mit copy&paste in seinen Projekten nutzt, und eingentlich nicht genau weiß, was die so machen. Es geht viel mehr darum, anderen seine methoden zu zeigen, sich den GML altag leichter zu machen.

      Sorgt am besten dafür, dass ihr ein kleines Beispiel in form eines GML codes zur Hand habt. Ein Trick soll ja nicht nur aus Theorie bestehen. Macht euch bitte auch die mühe, herrauszufinden ob der Trick relevant ist. (Es kann ja eine noch genialere Idee geben)

      Ich fange dann gleich mal an:

      In vielen Sprachen können Funktionen optionale Argumente haben. In GML hingegen, muss man eine Funktion die anzahl argumente geben, die sie verlangt. Wenn man doch seine Eigenen scripte macht, kann man sowas einfach nutzen. Hier ein kleines beispiel, damit man versteht was ich meine:

      beispielscript: return_input(,)

      dieser script gibt, wenn man argument1 ignoriert, argument0 zurück.
      Also: retrun_input(3) gibt 3 wieder.

      Wenn man hingegen auch noch input2 mitnimmt, gibt er die summe aus beiden argumenten wieder:
      Also return_input(3,5) gibt 8 wieder.

      Und so funktionier das dann:

      GML-Quellcode

      1. if (variable_local_exists('argument1')) {
      2. return argument0+argument1;
      3. } else {
      4. return argument0;
      5. }


      Oftmals werden argumente in scripten überflüssig. Da kann dieser Trick helfen und die Benutzerfreundlichkeit steigern.

      Zb: Eine funktion initalisiert eine DLL. Wenn man den script ohne argumente ausführt, nimmt er working_directory als pfad zur dll. Wenn man hingegen ein argument angibt, wird die dll von dem Ort geladen, den man angibt!

      Ich hoffe das schilderte ein bisschen, wie man sich diesen Thread vorstellen kann. Und jetzt, viel spaß beim posten und diskutieren.

      MfG SDX

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von SDX ()

    • Wenn ich deine Erklärung richtig verstanden hab, ist das Script nutzlos. Der GM verlangt nicht, alle Argumente beim Ausführen eines Scripts anzugeben. Nehmen wir z.B. ein Script, das 3 Argumente addiert:

      GML-Quellcode

      1. // Script-Name: script
      2. return argument0+argument1+argument2;

      Nun rufen wir das Script im Create Event auf und lassen uns das Ergebnis in der Titelleiste anzeigen. Dabei spielt es keine Rolle, wieviele Argumente wir angeben:

      GML-Quellcode

      1. room_caption = string(script(5));

      Das Ergebnis wird logischerweise '5' sein. Wichtig ist nur, dass automatisch immer die letzten Argumente fehlen. Wenn man 2 Argumente angibt, werden es die die beiden ersten sein. Allerdings hab ich die Vermutung, dass ich dich einfach falsch verstanden hab. ^^
      █████ ██ █ ████ everything ███ █████ is █████ ████ ████ fine ████ ███ █ ██████ love.
      █████ ███████ ███ your █████ ████ government.
    • Nun, ich weiß das mein Beispiel nutzlos ist. Zum addieren von Zahlen brauch mein wirklich keinen Script. Aber es gibt ja auch weit komplexere situationen.
      Ein ungesetztes Argument hat btw immer den Wert 0, da hast du recht. Aber wer hat gesagt, dass es unbedingt 0 sein soll, wenn man kein Argument angibt?

      Hoffe du verstehst es jetzt ^^

      MfG SDX
    • Ja, jetzt versteh ich es. ^^
      Es ist nur so, dass ich denke, wer komplexere Scripts schreibt, wird schon wissen, wie er sie einzusetzen hat. Also in den Bereich "Trickkiste" gehört das jetzt eher nicht. Es ist eigentlich nur grundlegendes Programmieren.
      █████ ██ █ ████ everything ███ █████ is █████ ████ ████ fine ████ ███ █ ██████ love.
      █████ ███████ ███ your █████ ████ government.
    • Ich weiß, dass es nicht das beste Beispiel war, um diesen Thread zu starten, aber es sollte nur ein Beispiel sein, wie man diesen Thread gestalten kann.
      Zumal geht es ja auch darum, seine Kniffe mit anderen zu teilen. Ich wette mal, es wird bestimmt schon ein paar leute gegeben hat, die auch mit Fortgeschrittenden wissen 2 oder mehr Scripte gemacht haben, wo man durch diese Technik bei einem hätte bleiben können. Wäre doch praktisch, wenn das öfters, besonders bei Scripten die veröffentlicht werden, genutzt wird. Zb wie ich es gesagt habe bei einem Script fürs initalisieren einer DLL.


      Ich hoffe natürlich auch, dass mein "Trick" sich den alltag einfacher zu machen, nicht der einzige hier bleibt ;)

      Mfg SDX
    • Nur um das Niveau mal etwas hochzupushen, viele Leute wissen ja nichts anzufangen mit Datenstrukturen. Dabei sind sie sehr hilfreich, besonders wenn man viele Daten von einem zum anderen Skript übergeben muss oder einfach keine extra Objekte erstellen will. Hat übrigens den nützlichen Effekt, dass man solche "Systeme" - sag ich mal - schnell in andere Projekte einbauen und erweitern kann. Hier ein (mehr oder weniger sinnvolles) Beispiel:
      Du willst also die Inventare deiner Spieler mithilfe von einigen Scripts managen.
      [hide=Skripte]

      GML-Quellcode

      1. // scr_inventory_init()
      2. global._inventories = ds_list_create(); // Hält alle unsere Inventare, damit wir
      3. // prüfen können, ob diese existieren.
      4. // scr_inventory_create()
      5. // returns a new inventory structure
      6. var list;
      7. list = ds_list_create();
      8. ds_list_add(list,0); // Wieviel Geld im Inventar steckt.
      9. ds_list_add(global._inventories,list);
      10. return list;
      11. // scr_inventory_exists(inventory)
      12. // returns if the inventory exists
      13. var inv;
      14. inv = argument0;
      15. if (ds_list_find_value(global._inventories,inv)==-1) { return false; }
      16. else { return true; }
      17. // scr_inventory_destroy(inventory)
      18. var inv;
      19. inv = argument0;
      20. if (!scr_inventory_exists(inv)) { exit; }
      21. ds_list_delete(global._inventories,ds_list_find_index(global._inventories,list));
      22. ds_list_destroy(inv);
      23. // scr_inventory_money_get(inventory)
      24. // returns the amount of money in the inventory
      25. var inv;
      26. inv = argument0;
      27. if (!scr_inventory_exists(inv)) { return -1; }
      28. return ds_list_find_value(inv,1);
      29. // scr_inventory_money_set(inventory,money)
      30. var inv,money;
      31. inv = argument0;
      32. money = argument1;
      33. if (!scr_inventory_exists(inv)) { exit; }
      34. ds_list_replace(inv,1,money);
      Alles anzeigen
      [/hide]Ist also nur ein kleines, unfertiges Beispiel. In einigen Fällen ist übrigens eine Map praktischer (z.B. wenn man Items nach Typ sortieren will). Wie gesagt, ziemlich praktisch. Ich habe damit auch mal zwei Downloadscripts geschrieben :)

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von copyboy ()

    • Tricks ja mh mal sehen was ich da so habe:

      Events in Scripts auslagern
      Für jedes Objekt das ich benutze und für jeden Event dieses Objekts lege ich ein neues Script an.
      Wenn das Objekt z.B. obj_gokart heißt dann gibts z.B. folgende Scripts:
      scr_gokart_create
      scr_gokart_step
      scr_gokart_draw
      Der Vorteil für mich liegt darin, dass ich mehrere Script-Code Fenster gleichzeitig offen haben und beliebig hin und her springen kann. Bei den "Execute Code" Fenstern in den Events selbst geht das nämlich nicht, da muss ich immer das Fenster schließen um ein anderes Code-Fragment zu öffnen.

      Konstruktor Scripts für Objekte

      Da ich ja meine Events in Scripts auslagere, habe ich auch immer ein scr_xxx_create Script.
      Durch das Script kann ich Objekte leichter instanzieren und gleich noch beliebige Parameter für Objektvariablen mitgeben. Häufig wiederholt man nämlich irgendwelche Variablenzuweisungen direkt nach einem instance_create()

      Debugobjekt + Debugflag
      Ich habe in jedem Projekt ein Debugobjekt was im Prinzip nicht mehr macht als eine Liste (heißt hier messages) von Debugausgaben auf den Bildschirm zu malen.
      Das Objekt ist persistent und ist daher immer verfügbar. Dazu hab ich dann noch ein Script mit folgendem Code:

      GML-Quellcode

      1. //scr_debug_add_message(message : String)
      2. //Fuegt den Parameter argument0 der Nachrichtenliste des Debugobjekts hinzu
      3. ds_list_add(obj_debug.messages, argument0);

      Im Draw-Event script des Debugobjekts steht dann:

      GML-Quellcode

      1. //scr_debug_draw(void)
      2. if(c_debug){
      3. for(i=0; i<ds_list_size(messages); i+=1){
      4. msg = string(ds_list_find_value(messages, i));
      5. msgHeight = string_height(msg);
      6. debug_string = "dout " + string(i) + ": " + msg;
      7. draw_set_color(c_black); //Schatten
      8. draw_text(2, 2+i*msgHeight, debug_string);
      9. draw_set_color(c_white); //Eigentlicher Text
      10. draw_text(3, 3+i*msgHeight, debug_string);
      11. }
      12. }
      Alles anzeigen

      c_debug ist eine Konstante die ich bei den "Global Game Settings" angelegt habe, wenn c_debug = 1 ist dann werden alle Debugausgaben angezeigt, wenn c_debug = 0 ist dann eben nicht.
      Mit dem Flag kann ich dann beliebig meine Testausgaben ein- und ausschalten.

      Edit: Also irgendwie vermurkst das Code-Tag immer meine Code-Formatierung...

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von Thodd ()

    • Thodd schrieb:

      Der Vorteil für mich liegt darin, dass ich mehrere Script-Code Fenster gleichzeitig offen haben und beliebig hin und her springen kann. Bei den "Execute Code" Fenstern in den Events selbst geht das nämlich nicht, da muss ich immer das Fenster schließen um ein anderes Code-Fragment zu öffnen.

      Du sprichst mir aus der Seele. Es ist wirklich ein wichtiger Punkt, dass Scripts einen nicht daran hindern, noch andere Dinge im GM zu machen. Ich gehe also recht ähnlich vor, allerdings lagere ich meist nicht die Events komplett aus, sondern unterteile sie in verschiedene Bereiche, um einfach mehr Übersicht im "Hauptcode" zu haben (dummes Beispiel: lenken, beschleunigen, bremsen). Diese Einzelscripts führe ich dann trotzdem im jeweiligen Event aus, kann aber so ganz schnell immer bestimmte Positionen finden, da ich mich nicht durch überlange Codeblöcke kämpfen muss. Außerdem versuche ich, Scripts möglichst universell zu halten, um sie - mit Argumenten gefüttert - mehrfach verwenden zu können. Für 3D Models hab ich bislang auch immer Scripts mißhandelt. Das ist komfortabler, als sie extern zu laden.

      Wo wir grad beim Thema Übersichtlichkeit waren... Wenn man in die GM Projekte einiger User schaut, findet man oft überhaupt keine Struktur. Unter 50 unsortierten Objekten findet man "obj_player", "O_gegner", "grüne linie" und noch mehr solcher Leckerbissen. Das ist dann auch der Punkt, wo ich keine Lust mehr hab, demjenigen bei einem normalerweise banalen Problem zu helfen. Man sollte sich von Anfang an für einen Stil entscheiden und dann nicht mehr abweichen.
      Ich persönlich halte auch die Unterordner der Ressourcen für ein wichtiges Feature. So kann man bestimmte Objektgruppen wunderbar trennen und kategorisieren ("Enemies", "Obstacles", usw.).


      Ich hab tatsächlich auch eine Art Angewohnheit, die ich in wirklich jedem Projekt mehrfach auslebe. Es ist eigentlich eine bedeutungslose Kleinigkeit, aber zumindest für mich immer wieder reizvoll. Verwendung findet es häufig bei Grafiken, die ich smooth auf und ab bewegen will. Die Geschwindigkeit nimmt bis zum Nullpunkt stetig zu, und danach wieder ab, bis sie schließlich umgekehrt wird. Das kann man sich wie eine Wellenbewegung vorstellen. Eigentlich eine simple Sache, die ich seit Ewigkeiten in dieser Form ausführe:

      GML-Quellcode

      1. if(y < 0) {y_add += .05;}
      2. if(y > 0) {y_add -= .05;}
      3. y += y_add;

      Das kann man sich natürlich als Script auslagern, aber bisher hab ich es meist direkt verwendet. Zum Starten darf 'y' natürlich nicht auf '0' gesetzt werden und 'y_add' muss logischerweise vorher deklariert werden. Ich glaub, der einzige Grund, warum ich das nicht als Script nutze, ist, dass mir einfach kein Name dafür einfällt. ^^
      █████ ██ █ ████ everything ███ █████ is █████ ████ ████ fine ████ ███ █ ██████ love.
      █████ ███████ ███ your █████ ████ government.
    • Eine andere möglichkeit für Wellenbewegungen, wie ich sie auch öfters nutze, ist Sinus.
      Das kann man sehr leicht als script auslagern, und bewirkt genau das selbe wie Mauge es macht:

      GML-Quellcode

      1. fortschritt+=argument0 // argument0 ist die Geschwindigkeit, mit der die bewegung geht.
      2. return sin(fortschritt)*argument1 // argument1 ist die Höhe der Welle.
      Genutzt wird das dann so:

      GML-Quellcode

      1. y+=scr_welle(Geschwindigkeit,Stärke);


      Was es der Übersichtlichkeit angeht. Ich mache da eigentlich nur Scripte wenn ein codeteil mehr als ein mal genutzt wird oder es halt durch Gruppenarbeit oder ähnlichem auch für andere sofort verständlich sein muss.
      Am Ende kommt dann meistens ein 50-50 mischmasch aus eigenen Scripten und GM Funktionen raus. Ich denke jeder wird da irgendwo einen Zwischenweg haben. Thodd ist da das eine Extrem!

      Das Debugobjekt hat mir besonder gefallen. Bis jetzt hatte ich, wenn es probleme gab, den ganzen code mit Messages bestückt, und konnte nachher sehen, wo es dann Probleme gab. Danach wieder diese Messages zu finden, war dann doch sehr umständlich :P

      MfG SDX
    • Ich mag das überhaupt nicht mit dem Event-Code in Skripts auslagern, aber das ist nur ein Gefühl von mir ohne echte Begründung(Gewohnheitssache) ^^

      Worauf ich immer achte ist, alle Komponenten meines Spiels immer möglchst unabhängig voneinander zu entwickeln, zum einen zu Wiederverwendbarkeit und Flexibilität, aber vorallem, weil es grauenhaft ist, wenn man nicht mehr weis, von welchen Objekten mein 3-Meter Skript(whatever) noch so abhängig ist. Dann macht man später eine kleine Veränderung und schon zieht man nen Rattenschwanz hinter sich her. Ich versuche also, Abhängigkeiten möglichst offensichtlich zu machen und auch zu dokumentieren.

      Eine andere Sache ist parentising. Ich seh öfters Gmks mit 100 verschiedenen "Schuss"-Objekten, die alle mehr oder weniger das gleiche machen, aber wenn man nur eine Sache verändern möchte, muss man das 100 mal machen.
      Ich geh sogar soweit, dass ich nicht nur einen obj_enemy als parent habe, sondern der hat wiederrum obj_human(oder so) als parent, wo auch obj_player davon erbt usw... Ich versuche immer, möglichst vgrosse Vererbungsbäume zu machen, damit ich jederzeit was verändern kann an einem bestimmten Punkt, und weiss dass es dann auch andernorts funktioniert.

      Wie man sieht lege ich grossen Wert auf Abstraktion und Flexibilität, solange das die Performance nicht zu sehr beeinträchtigt.
      "das war meine letzte flamewar PM an dich ."
    • Ich musste jetzt auch einfach meinen senf dazugeben xD...

      ich sag jetzt was mir bei der übersichtlichkeit hilft, was aber für die meisten hier wohl selbstverständlich ist.

      also ich arbeite viel mit dem Tabulator, um zeilen einzurücken. Dadurch sieht man beim durchschauen recht schnell was überhaupt wo zu gehört.
      Außerdem nutze ich die Komentarfunktion um code optisch von einander zu trennen. Naja ich könnte noch viel reden, aber ich zeig einfach mal ein Beispiel aus meinem akutellen Projekt...

      Spoiler anzeigen

      GML-Quellcode

      1. //--- bewegung rechts laufen
      2. if (keyboard_check(vk_right)) && !(keyboard_check(vk_left)) && !(sprite_index=spr_ducken)
      3. {
      4. if (place_meeting(x+4,y,obj_boden_par))
      5. {
      6. move_contact_solid(0,4);
      7. }
      8. else {x += 4;}
      9. //---Sprite rechts laufen
      10. if (image_speed=0)
      11. {
      12. image_speed=0.33;
      13. }
      14. sprite_index=spr_player_laufend;
      15. image_xscale=1;
      16. }
      17. //-------rechts stehen-------
      18. if keyboard_check_released(vk_right)
      19. {
      20. sprite_index=spr_player_stehend;
      21. image_xscale=1;
      22. }
      23. //----------------------------------------------------------------
      24. // - <= Links <= -
      25. if (keyboard_check(vk_left)) && !(keyboard_check(vk_right)) && !(sprite_index=spr_ducken)
      26. {
      27. if (place_meeting(x-4,y,obj_boden_par)) {
      28. move_contact_solid(180,4);
      29. }
      30. else {x -= 4;}
      31. //---sprite links laufen
      32. if (image_speed=0)
      33. {
      34. image_speed=0.33;
      35. }
      36. sprite_index=spr_player_laufend;
      37. image_xscale=-1;
      38. }
      39. //-------links stehen--------
      40. if keyboard_check_released(vk_left)
      41. {
      42. sprite_index=spr_player_stehend;
      43. image_xscale=-1;
      44. }
      Alles anzeigen


      das ist ein recht langes beispiel wie ich meine codes glieder.

      Zugegeben das ist jetzt wohl eher ein Tipp für Neulinge, aber ich wollte auch was dazu schreiben :P

      LG Gruen

      ps. ich merk gerade das das nicht exakt so dargestellt wird wie ichs reinkopiert habe...

      also eigendlich rücke ich ALLES ein was nach einer { kommt.


      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Gruen ()

    • @Gruen: Nur ein kleiner Tip, um noch etwas Platz zu sparen:
      Ich bezieh mich mal gleich auf deine erste Zeile.

      GML-Quellcode

      1. if (keyboard_check(vk_right)) && !(keyboard_check(vk_left)) && !(sprite_index=spr_ducken)

      Das könntest du auch folgendermaßen machen (wie es meines Wissens sogar üblich ist):

      GML-Quellcode

      1. if (keyboard_check(vk_right)) &! (keyboard_check(vk_left)) &! (sprite_index=spr_ducken)


      Wie gesagt, ist nur eine Kleinigkeit, aber meistens ist es ja so, dass man wirklich schaut, wo man noch Platz sparen kann - vor allem, wenn es an komplexe Codes geht.
      █████ ██ █ ████ everything ███ █████ is █████ ████ ████ fine ████ ███ █ ██████ love.
      █████ ███████ ███ your █████ ████ government.
    • @mauge ich muss zugeben meistens schreibe ich das sogar aus...

      GML-Quellcode

      1. if (keyboard_check(vk_right)) and !(keyboard_check(vk_left)) and !(sprite_index=spr_ducken)


      weil ich mit 10 fingersystem schreibe, aber die zeichen auf den nummern oben nicht auswendig schreiben kann.
      dann geht des meistens flotter wenn ichs ausschreibe. :P

      lg gruen


    • Was mir gestern beim Proggen aufgefallen ist, dass ich immer öfters anstatt

      GML-Quellcode

      1. if(keyboard_check(vk_some)) {
      2. variable+=20;
      3. }
      das hier mache:

      GML-Quellcode

      1. variable+=keyboard_check(vk_some)*20;


      Was auch immer öfters in meinem code finde, ist diese methode, lange ifs zu umgehen:

      GML-Quellcode

      1. var gedruckt, released;
      2. gedruckt=keyboard_check(ord('W'))+keyboard_check(ord('A'))+keyboard_check(ord('S'))+keyboard_check(ord('D'));
      3. released=keyboard_check_released(ord('W'))+keyboard_check_released(ord('A'))+keyboard_check_released(ord('S'))+keyboard_check_released(ord('D'));
      4. if (gedruckt-released<0) {
      5. // bremsen
      6. }
      Dies wirkt auf den ersten Blick ganz schön komplex, aber wenn man es erst mal versteht, erleichtert es die Arbeit und steigert die übersichtlichkeit!

      MfG SDX
    • Hier nochn kleiner Trick denn ich durch zufall rausbekommen hab:

      Folgendes funktioniert xD

      var a;
      a = 50;

      with (object0) b = 20+a;


      jetzt wird b für Object0 den Wert 70 haben.

      Mit var initialisierte variablen bleiben auch bei With-statements bestehen.
      Somit muss man nicht other.a schreiben um darauf zuzugreifen.
      Kann nützlich sein. Z.B. wenn man geschachtelte with-statements hat und auf das Object zwei instanzen höher zugreifen will.

      Willst du auf diese Drachen und -eier klicken?
      Sie werden sich freuen ;)