GMS 2 Undo/Redo in game

RobotoSkunk

Member
Hi, I'm creating an editor for my video game and I want to add the option to be able to undo and redo actions, but I have no idea how to do it. Can someone help me?
 
Save the last thing you did in an array and just back step the array when you press the undo button. Or if you need more information saved, create an object with multiple arrays with information you need saved and place it in a room with persistence turned on.

Not sure if this is the most efficient way, but it is the easiest way I can think of for doing this at the moment.
 
I recommend reading about the Command Pattern:

http://gameprogrammingpatterns.com/command.html

These is a section specifically on undo/redo.

It gives a good overview of a way to approach the whole thing.

Basically, for every action the user makes, you need to save the exact information required to undo do it.

As @FlameRooster suggested, you'll want some sort of data structure and/or object system that you can save that information to.

So if the user moves an object from location A to location B, you would record the information:

1) Object Moved. ( An enum could be used to store the different types of actions the user can perform )
2) Original x and y location.

And save it to an array / data structure / unique instance.

Then when the user hits undo, you can perform the appropriate action of moving the object back to location A, then remove the undo information from the undo data structure.
 

RobotoSkunk

Member
I recommend reading about the Command Pattern:

http://gameprogrammingpatterns.com/command.html

These is a section specifically on undo/redo.

It gives a good overview of a way to approach the whole thing.

Basically, for every action the user makes, you need to save the exact information required to undo do it.

As @FlameRooster suggested, you'll want some sort of data structure and/or object system that you can save that information to.

So if the user moves an object from location A to location B, you would record the information:

1) Object Moved. ( An enum could be used to store the different types of actions the user can perform )
2) Original x and y location.

And save it to an array / data structure / unique instance.

Then when the user hits undo, you can perform the appropriate action of moving the object back to location A, then remove the undo information from the undo data structure.
Thanks for everything you said to me, it helps me quite seriously <3
Now I have another problem, the editor deactivates instances outside the camera to optimize and I do not know how to communicate the Undo/Redo with the deactivated instances

This is the editor:
 
Last edited:
Just before deactivating the instances, save all the instance ids to a data structure ( I use a ds_list ).

I have a top level object that all other objects use as a parent.

with (obj_parent)
{
if object is outside camera view
add instance id to ds_list
deactivate instance
}

And do the reverse to reactivate them.

Or you can just use the function instance_activate_all() followed by instance_deactivate region()
 

RobotoSkunk

Member
Just before deactivating the instances, save all the instance ids to a data structure ( I use a ds_list ).

I have a top level object that all other objects use as a parent.

with (obj_parent)
{
if object is outside camera view
add instance id to ds_list
deactivate instance
}

And do the reverse to reactivate them.

Or you can just use the function instance_activate_all() followed by instance_deactivate region()
And once saved in the list what should I do with the ID or how is that the code to do the Undo/Redo button?
 
So, let's see if I understand.

Let's say you previously moved an object from A to B.

You would have saved the following info somewhere:

Action : Move (e.g. eAction.Move)
PreviousX : prev_x (e.g. 25.5)
PreviousY : prev_y (e.g. 127.2)
Instance ID : moved_instance_id (e.g. 1000245)

Now, you are saying, what if the camera view had moved somewhere else on the level, and the instance that was moved has been deactivated, how would you apply the undo action to it?

Well, in this case, if you have access to the instance id (which we do : 1000245), you can still manipulate its values.

So when the player hits the undo button, you process the data above and just apply it to the instance using dot (.) notation.

Code:
// Grab the last instance that was added to the undo list
var _instance = global.undo_list[| ds_list_size(global.undo_list) - 1];

// Grab the undo information here - need to add code for this depending on how you implement it.

// [ Grab instance undo info here]
Code goes here...

// Perform the undo action
_instance.x = prev_x;
_instance.y = prev_y;

// Delete the instance from the undo list, because we've undone that action.
ds_list_delete(global.undo_list, ds_list_size(global.undo_list) - 1);

// Also clean up other undo information
The above code is just an example and needs more work before it will function.

-------

Here's how I would do it in more detail : What I would do is create an object for each type of action you want to undo.

obj_undo_move
// Create Event
Code:
undo_instance = noone;
undo_prev_x = 0;
undo_prev_y = 0;
User Event 0
Code:
undo_instance.x = undo_prev_x;
undo_instance.y = undo_prev_y;
Now, when the user moves an object, after they have finished moving, you would save the undo information

// Run this script after a user has just moved an object
Code:
var _object_undo = instance_create_layer(x, y, "Instances", obj_undo_move);

// NOTE : In the editor, you will need to detect when the user starts dragging the object in order to move it, save its original x and y position somewhere, and then, when they finish dragging and complete the move operation, use those values in the following code. Also need to record the moved instances id.

// Assume your main editor object controller is called obj_editor:
_object_undo.undo_instance = obj_editor.moved_object_id;
_object_undo.undo_prev_x = obj_editor.moved_object_original_x;
_object_undo.undo_prev_y = obj_editor.moved_object_original_y;

// Add the new undo instance to the end of a datastructure, i.e. a ds_list
ds_list_add(global.undo_list, _object_undo);
Then, when the user hits undo:
Code:
// Grab the most recent undo operation the user recorded.
var _undo_obj = global.undo_list[| ds_list_size(global.undo_list) - 1];

// Perform the undo operation by running the User Event 0 on the object.

with ( _undo_obj )
{
    event_user(0)
}
   
// Delete the undo object from the list.
ds_list_delete(global.undo_list, ds_list_size(global.undo_list) - 1);

// Destroy the undo object
instance_destroy(_undo_obj);
This is basically following the command pattern I mentioned.

For other actions, you would create separate objects, with the undo information also in their User Event 0.

Above code is not tested, just wrote it now, let me know if any problems.
 
Top