Map Editor Undo Funktion

  • GM 8

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

  • Map Editor Undo Funktion

    Guten Tag,

    Ich sitze schon ne ganze Weile daran in meinem Map Editor eine Undo (Rückgängig) Funktion einzubauen.
    Normalerweise finde ich den Fehler nach so langer Zeit, aber ich bin schon alles tausendmal durchgegangen
    und hoffe, dass ihr den Fehler findet [=

    Die Undo Funktion sollte wie folgt gehen: Setzen und löschen von Objekten in eine 2 Dimensionale Array speichern.
    Bei drücken von Strg+Z sollte der Vorgang rückgängig gemacht werden, sprich gesetzte Objekte werden gelöscht und
    gelöschte Objekte (von Hand) wieder erstellt und die alte gelöschte id in der Array finden und mit der neuen ersetzen.

    Naja das wars erstmal, eine Vorwärts Funktion überleg ich mir noch zu machen, da es wohl noch ein wenig komplizierter wird :)

    Ich arbeite zum ersten mal richtig mit 2D Arrays, also wird der Code viell. nicht der beste sein^'^
    Es gibt 2 Objekte: obj_place und obj_controller. Nun zum Code:

    obj_controller:

    Create-Event:

    GML-Quellcode

    1. for(i=0;i<20;i+=1)
    2. {
    3. for(a=0;a<10;a+=1)
    4. {
    5. //[i,a] i=Reihenfolge | a=0: id der erstellten Instanz, a=1:id der gelöschten Instanz a>1: informationen der gelöschten Instanz
    6. global.undo[i,a]=-1;
    7. }
    8. }


    Ctrl-Event

    GML-Quellcode

    1. //Undo Funktion/Vorgang
    2. if (keyboard_check_released(ord("Z"))) //Wenn Z losgelassen wird
    3. {
    4. if(global.undo[19,0]>0) //Überprüfung ob im letzten Vorgang (19) ein Objekt gesetzt wurde.
    5. {
    6. with(global.undo[19,0])
    7. {
    8. instance_destroy();
    9. for(i=19;i>0;i-=1)
    10. {
    11. for(a=0;a<10;a+=1)
    12. {
    13. global.undo[i,a]=global.undo[i-1,a]; //Liste wird so verschoben, dass der nächste Schritt in 18 in 19 verschoben wird, 17 in 18, 16 in 17 und so weiter
    14. }
    15. }
    16. }
    17. }
    18. else
    19. {
    20. if (global.undo[19,1]>0) //Wenn im letzten Schritt ein Objekt gelöscht wurde (von Hand)
    21. {
    22. //Ich habe nach dem instance_create 18 statt 19 geschrieben, weil ich nicht weiß ob das Create Event vom erstellten Objekt zuerst durchgeführt wird?
    23. object=instance_create(global.undo[19,2],global.undo[19,3],object_place); //Das Objekt wird wieder erstellt mit den vorher gespeicherten Objekt Informationen
    24. object.objectid=global.undo[18,5];
    25. object.object_depth=global.undo[18,6];
    26. object.z=global.undo[18,4];
    27. object.position=global.undo[18,7];
    28. object.image_speed=global.undo[18,8];
    29. object.image_id=global.undo[18,9];
    30. for(i=0;i<20;i+=1)
    31. {
    32. if (global.undo[i,0]==global.undo[18,1]) //Array nach einer nicht existierenden id suchen die in [18,1] gespeichert ist um sie mit der neuen zu ersetzen
    33. {
    34. global.undo[i,0]=object; //Neue ID
    35. }
    36. }
    37. for(i=18;i>0;i-=1)
    38. {
    39. for(a=0;a<10;a+=1)
    40. {
    41. global.undo[i,a]=global.undo[i-1,a]; //Neu Ordnung der Array
    42. }
    43. }
    44. }
    45. }
    46. }
    Alles anzeigen


    obj_place (Objekt welches erstellt wird wenn man etwas auf die map legt):

    Create-Event:

    GML-Quellcode

    1. for(i=1;i<20;i+=1)
    2. {
    3. for(a=0;a<10;a+=1)
    4. {
    5. global.undo[i-1,a]=global.undo[i,a]; //Liste nach links verschieben um für 19 Platz zu machen
    6. }
    7. }
    8. global.undo[19,0]=id; //19,0 mit der id belegen
    9. global.undo[19,1]=-1; //19,1 mit -1 belegen


    Right-Released-Event (Hier wird ein Objekt auf der Map gelöscht):

    GML-Quellcode

    1. for(i=1;i<20;i+=1)
    2. {
    3. for(a=0;a<10;a+=1)
    4. {
    5. global.undo[i-1,a]=global.undo[i,a]; //Sortierung der Array, sodass der neue Vorgang in 19 gespeichert werden kann.
    6. }
    7. }
    8. //Informationen des gelöschten Objekts
    9. global.undo[19,0]=-1;
    10. global.undo[19,1]=id;
    11. global.undo[19,2]=x;
    12. global.undo[19,3]=y;
    13. global.undo[19,4]=z;
    14. global.undo[19,5]=objectid;
    15. global.undo[19,6]=object_depth;
    16. global.undo[19,7]=position;
    17. global.undo[19,8]=image_speed;
    18. global.undo[19,9]=image_id;
    Alles anzeigen


    Zusatzinformation:
    Also wenn man Objekte nur setzt funktioniert es einwandfrei diese mit Strg+Z wieder rückgängig zu machen.
    Wenn ich aber z.B. 10 Stück setze, den 10. mit der Maus per Rechtsklick lösche passiert folgendes:
    Objekt wird gelöscht. Nachdem Ich Strg+Z drücke wird das gelöschte Objekt wieder erstellt (wie es soll).
    Nach noch einem mal Strg+Z wird das zuletzt erstellte Objekt wieder entfernt. (auch richtig)
    Allerdings lassen sich die übrigen 9 Schritte nicht mehr rückgängig machen.

    Mit einem show_message habe ich herausgefunden, dass die id in 19,0 nicht existiert.
    Aber die müsste eigentlich durch das Create Event in obj_place vergeben sein : /

    Es wäre echt nett wenn es sich jemand antut die Zusammenhänge zu verstehen und mir einen möglichen Fehlerfaktor
    hinterlässt :)


    mfg
  • Wenn Du Dir beim Create Event nicht sicher bist, pack einfach die ganzen Zuweisungen (Undo-Array -> neues Objekt) ins Create Event.

    Wenn ich den Code richtig interpretiere, wird beim Rückgängig-Machen vom Löschen eines Objektes ein neues Objekt erstellt. Im Create Event wird wiederum am Ende des Undo-Arrays die ID des gerade erstellten Objektes eingetragen, was wiederum dazu führt, dass beim nächsten Rückgängig-Aufruf als letzter Schritt das Erstellen eines Objektes auftritt und somit das Rückgängig gemachte wieder rückgängig gemacht wird.

    Ich hoffe mein Kauderwelsch ist halbwegs verständlich ;p
    "Es gibt nie ein glückliches Ende, denn es endet nichts." - Schmendrick
  • Ersteinmal danke für die Antwort :)

    Ich verstehe deiner ersten Satz nicht. Also wenn ich mir im Create Event nicht sicher bin, dann soll ich das ganze ins Create Event packen?^'^
    Wenn ich deine Interpretation richtig verstehe hast du den Code richtig gedeutet. Und es soll an sich auch so laufen.
    Wen 2 Objekte gesetzt werden und das 2 Objekt per "Rechts Klick" gelöscht wird ist der Rückgängig Ablauf ja eig folgender:

    1. Gelöschtes Objekt wieder erstellen (das zweite Objekt)
    2. Wiedererstelltes Objekt löschen (das zweite Objekt)
    3. Nun das erste Objekt löschen.

    Und das ganze läuft nur bis schritt 2. Also wird Schritt 3. und wenn es mehrere gesetzte Objekte gäbe die restlichen Schritte nichtmehr ausgeführt.
    Durch eine Abfrage habe ich herausgefunden, dass genau dann eine id einer Instanz in 19,0 gespeichert ist die nicht existiert :/
    Anscheinend ist die alte id nicht durch die neue ersetzt worden, ich schau mir das ganze nochmal an.

    Edit: Ich habe gerade gemerkt, dass die neue id für das gelöschte objekt dann 2 mal in der Array besteht ... tut mir leid wenn du das meintest :'D
    Vielleicht liegt der ganze Fehler auch nur darin.

    Edit2: Es lag wirklich daran... Ich habe jetzt einfach eine Abfrage im Create Event im obj_place gemacht. Wenn zuvor eine Instanz gelöscht wurde
    soll "dort" nichts an der Array verändert werden.

    obj_place

    GML-Quellcode

    1. if (!global.undo[19,1>0]) //Nur ausführen wenn in 19,1 keine alte id besteht (Daran erkennt das Programm ob gerade eine Instanz neu erstellt wurde
    2. {
    3. for(i=1;i<20;i+=1)
    4. {
    5. for(a=0;a<10;a+=1)
    6. {
    7. global.undo[i-1,a]=global.undo[i,a];
    8. }
    9. }
    10. global.undo[19,0]=id;
    11. global.undo[19,1]=-1;
    12. }
    Alles anzeigen


    obj_controller - Ctrl Event sieht nun so aus:

    GML-Quellcode

    1. //Undo Funktion/Vorgang
    2. if (keyboard_check_released(ord("Z")))
    3. {
    4. if(global.undo[19,0]>0)
    5. {
    6. with(global.undo[19,0])
    7. {
    8. instance_destroy();
    9. for(i=19;i>0;i-=1)
    10. {
    11. for(a=0;a<10;a+=1)
    12. {
    13. global.undo[i,a]=global.undo[i-1,a];
    14. }
    15. }
    16. }
    17. }
    18. else
    19. {
    20. if (global.undo[19,1]>0)
    21. {
    22. new_object=instance_create(global.undo[19,2],global.undo[19,3],object_place);
    23. new_object.objectid=global.undo[19,5];
    24. new_object.object_depth=global.undo[19,6];
    25. new_object.z=global.undo[19,4];
    26. new_object.position=global.undo[19,7];
    27. new_object.image_speed=global.undo[19,8];
    28. new_object.image_id=global.undo[19,9];
    29. for(i=0;i<20;i+=1)
    30. {
    31. if (global.undo[i,0]==global.undo[19,1])
    32. {
    33. global.undo[i,0]=new_object;
    34. }
    35. }
    36. for(i=19;i>0;i-=1)
    37. {
    38. for(a=0;a<10;a+=1)
    39. {
    40. global.undo[i,a]=global.undo[i-1,a];
    41. }
    42. }
    43. }
    44. }
    45. }
    Alles anzeigen


    Eine Änderung habe ich nur am Ende vorgenommen in dem Teil wo eine Instanz neu erstellt wird.
    Und zwar wird jeder Schritt einen höher gesetzt, sodass 19 nach Ablauf des Codes wegfällt.
    So wird nun alles korrekt in der richtigen Reihenfolge rückgängig gemacht :)

    Falls das ganze einen interessiert^'^

    Hätte jemand einen Plan/Tipp wie ich "vorwärts/redo" realisieren könnte ohne viel Aufwand?

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

  • @Create Event:
    Du hast in den Kommentaren geschrieben, dass Du nicht sicher bist, ob das Create Event vor- oder nachher ausgeführt wird. Deshalb hätt ich

    GML-Quellcode

    1. object.objectid=global.undo[18,5];
    2. object.object_depth=global.undo[18,6];
    3. object.z=global.undo[18,4];
    4. object.position=global.undo[18,7];
    5. object.image_speed=global.undo[18,8];
    6. object.image_id=global.undo[18,9];
    einfach ins Create Event gepackt.

    Ein Beispiel für mein Kauderwelsch (auweia! noch mehr Kauderwelsch :wacko: :(
    Spoiler anzeigen
    1: Objekt manuell erstellen; [18, x] = [19, x]; ID in [19, 0] und Infos in [19, x] speichern;
    2..10: Objekt(e) manuell erstellen; [18, x] = ... (wie oben);
    11: Objekt manuell löschen; [18, x] = [19, x]; ID in [19, 1] und Infos in [19, x] speichern;
    12: Rückgängig; Objekt aus "11" wieder erstellen (Create Event); [18, x] = [19, x]; ID in [19, 0] und Infos in [19, x] speichern;
    Fehler: Da im Create Event die ID in [19, 0] gespeichert wird und das Create Event in der Undo-Prozedur aufgerufen wird, wird aus dem Undo quasi ein Redo des letzten gelöschen Objektes.

    Vorschlag Undo-Redo:
    Das Array mit den Infos zum erstellten/gelöschten Objekt gefällt mir sehr gut; ich würde jedoch statt den beiden ID in [x, 0] und [x, 1] einmal die ID des geänderten Objektes speichern, sowie, welche Aktion durchgeführt wurde. Das Array würde ich immer beibehalten, außer die durchgeführten Aktionen überschreiten die Größe des Arrays > dann einfach Inhalte verschieben. Bei Rückgängig keine Änderung am Array vornehmen, sodass die Infos für das Redo gespeichert bleiben.
    Beispiel: In [x, 0] speicherst Du welche Aktion durchgeführt wurde (0 für Erstellen, 1 für löschen, 2 für Verschieben - ich weiß nicht wie umfangreich Dein Editor ist...). In [x, 1] speicherst Du die ID des Objektes, welches gelöscht, verschoben, erstellt wurde. Wird eine manuelle Änderung gemacht, wird der Inhalt des Arrays verschoben, um Platz für die Änderung zu machen (was Du bereits realisiert hast). Anstatt jetzt das Array beim Rückgängig neu zu verschieben und zu füllen, legst Du Dir eine Variable an, welche die Aktuelle Position im Undo/Redo-Array speichert. Bei Rückgängig verringerst Du die Position, bei Wiederherstellen erhöhst Du diese. Anhand der gespeicherten Aktion in [x, 0] kannst Du eine Gegenaktion ausführen: gespeicherte Aktion = Löschen, Gegenaktion = Erstellen; gespeicherte Aktion = Verschieben, Gegenaktion = gleiche Verschiebung x -1.

    Zur besseren Verständnis ein Bild:


    Edit: Kleines Beispiel:
    Im Undo/Redo-Array werden (nur im Beispiel) Aktion, ID, X-Pos und Y-Pos gespeichert.
    Unser Array hat 5 Einträge platz. Bei jeder manuellen Aktion werden die Felder des Undo-Bereiches - also die Felder kleiner der aktuellen Arrayposition - ans Ende des Arrays verschoben, wodurch der Redo-Bereich überschrieben wird. Die Position des Arrays wird ans Ende gesetzt.

    Aktion 0 = Erstellen -> Gegenaktion 1; Aktion 1 = Löschen -> Gegenaktion 0;

    Es werden 5 Objekte erstellt und analog dazu die Informationen in die letzten 5 Felder/Spalten des Arrays gespeichert.

    urArray[0, 0] = 0; urArray[0, 1] = id1; urArray[0, 2] = x1; urArray[0, 3] = y1;
    urArray[1, 0] = 0; urArray[1, 1] = id2; urArray[1, 2] = x2; urArray[1, 3] = y2;
    urArray[2, 0] = 0; urArray[2, 1] = id3; urArray[2, 2] = x3; urArray[2, 3] = y3;
    urArray[3, 0] = 0; urArray[3, 1] = id4; urArray[3, 2] = x4; urArray[3, 3] = y4;
    urArray[4, 0] = 0; urArray[4, 1] = id5; urArray[4, 2] = x5; urArray[4, 3] = y5;
    Pos = 4;

    Nun wird das Objekt mit id3 manuell gelöscht und somit das Array aktualisiert.

    urArray[0, 0] = 0; urArray[0, 1] = id2; urArray[0, 2] = x2; urArray[0, 3] = y2;
    urArray[1, 0] = 0; urArray[1, 1] = id3; urArray[1, 2] = x3; urArray[1, 3] = y3;
    urArray[2, 0] = 0; urArray[2, 1] = id4; urArray[2, 2] = x4; urArray[2, 3] = y4;
    urArray[3, 0] = 0; urArray[3, 1] = id5; urArray[3, 2] = x5; urArray[3, 3] = y5;
    urArray[4, 0] = 1; urArray[4, 1] = id3; urArray[4, 2] = x3; urArray[4, 3] = y3;
    Pos = 4;
    das Objekt mit id1 wurde aus dem Array entfernt und kann somit mit Undo nicht mehr gelöscht werden.

    Wird nun ein Undo ausgeführt, wird eine Gegenaktion zum Feld an der Position "Pos" durchgeführt.
    urArray[Pos, 0] = 1 (= Löschen) -> Gegenaktion = Erstellen
    -> also muss ein Objekt an der Stelle x3, y3 mit id3 erstellt werden. Nachher wird Pos um 1 vermindert.
    Pos = 3;

    Erneutes Undo
    urArray[Pos, 0] = 0 (= Erstellen) -> Gegenaktion = Löschen
    -> das Objekt mit id5 auf Position x5, y5 wird gelöscht.
    Pos = 2;

    Erneutes Undo
    urArray[Pos, 0] = 0 (= Erstellen) -> Gegenaktion = Löschen
    -> das Objekt mit id4 auf Position x4, y4 wird gelöscht.
    Pos = 1;

    Bei einem Redo kann dann die Aktion ausgeführt werden, welche im Feld mit Pos + 1 steht.
    Pos = 2;
    urArray[Pos, 0] = 0 (= Erstellen) -> Aktion = Erstellen
    -> es wird ein Objekt an der Stelle x4, y4 mit id4 erstellt.

    and so on...
    "Es gibt nie ein glückliches Ende, denn es endet nichts." - Schmendrick

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Klaus C. Haber ()

  • Danke für die sehr ausführliche Antwort :)
    Jetzt habe ich auch verstanden was du mit dem Create Event meintest.
    Aber der Teil hat sich schon geklärt [=

    Ich habe mir das ganze mal ordentlich durchgelesen und schon die Hälfte verstanden,
    aber ich werde erst morgen versuchen zu machen, da der Tag heute schon anstrengend war :p
    Ich werde es dann zuerst in einer Kopie meines Projektes ausprobieren bevor ich alles versaue.
    Ich meld mich dann hier wenn es was neues gibt :)

    EDIT:
    So dann meld ich mich mal.
    Ich habs nun einige zeit ausprobiert, und es kamen zwischendurch Fehler die ich Beheben musste, wodurch
    alles Nachfolgende dann nicht richtig funktioniert hat.
    Ich komm da wohl einfach zu durcheinander :(
    Dann werd ich mich wohl erstmal mit "Rückgängig" zufrieden geben :p


    mfg

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