How do I make my own UNDO/REDO system?

2 favourites
From the Asset Store
Surena system
$126 USD
30% off
A system for storing and processing information online
  • Hi All,

    As I keep working on my software/application I was wondering if there is a way to make an UNDO and REDO?

    For example:

    When I deleted an instance, or change it's position, can I code that when I press 'CTRL+Z' it will undo the last action?

    I'm not sure how to do that or if I'm on the right direction at all but I'm guessing that it could be done using the System save/load local action? based on a specific slot? ...it's just a rough guess I don't really know.

    But hopefully it's not something that will clash with my current Save/Load system.

    or maybe there is a different way to do it?

    If it's too complicated to accomplish I'll probably skip that feature but I'm very curious if it's doable, and how exactly.

    Thanks ahead!

    Tagged:

  • Yes, you could do that using specific saving slots or using a history array, that is, an array in which you store the last X states as JSON.

    I like the first method.

    It would be something like this:

    And you would call that function before executing any action you want to be undoable.

    Hope this helps...

    Cheers!

  • Yep, that's how I did this in a couple of projects. The are a few things you should be aware of -

    Make sure you don't save too often, for example when an object is dragged, don't save on every tick, save only on drop event.

    Add NoSave behavior to all objects you don't need to save, to decrease the size of saved data and saving/loading time.

    And also this potentially can create hundreds of saves and use a lot of browser storage. So I advise to limit the number of undo steps and recycle/reuse the save slots.

  • I would keep the undo list in memory using an array - not save it to local storage.

    since you seem to be building some kind of level editor, you are going to have a lot of objects, and there are lots of things you will be doing to those objects: move, size, z-order, rotate, create, delete, etc.

    every time some action has finished, insert a new entry into the array.

    you will need to store things like: object UID, ObjectTypeName, Action Performed, the old value, the new value for that action.

    since I know you want to have several objects selected, that makes it more complicated because then undo/redo has to apply changes to the whole group, so each entry in the array will have to include all the details from each object and know it was part of a group action.

    the only reason to save the undo list to local storage would be if you want to close the game, open it again later and still be able to undo things you did in the previous session. That is also possible - saving/loading an array is an easier way to do this than any alternatives I can think of...

  • Thank you so much everyone for your advice, I'm glad that it is possible to do! :)

    I didn't try what brunopalermo suggested yet, as I'm not sure if this is the best way or not yet, but thank you for the example, I may try it if everything else will fail! (there gonna be lots of head-scratching for me).

    Since I'm a C3 noob, all the Array and Save data in general still seems very complicated to do (for me), but your explanations made lots of sense to me because my brain can understand basic logic easily but nothing more advanced from my experience...

    Both: local save or array seems to work as most of what I will do is tweak objects and their instances, MANY OF THEM.

    Thanks to dop2000 help from my previous thread I already have a working Save As (naming file) and Load buttons that works great, to be honest I couldn't understand it completely with the JSON, AJAX and how to code it, so I copy past what dop2000 suggested to make it work, and it did!

    I already excluded what shouldn't be saved, things like the UI for example.

    Just like AllanR said, I will do many changes and tweaks to many instances that appears on the screen so I don't really need to save the UNDO / REDO for future sessions, so I either need the slots to be deleted when EXIT the software.

    Also limit the UNDO so it will work on lower-end old machines will be a good advantage for the software and the users, I think 20 max UNDO will do the job, if not I can go for 10 max.

    BTW - I'm not using Chrome to for C3, I'm using the Desktop build. my goal is to make it work for PC Windows / Mac / Linux hopefully it will be possible I have no idea...

    Again, I have no clue how to even start code such thing technically, I wonder if it is very complex to achieve or around the same idea of the "Save As" system which without copy past I couldn't make it work to be honest because I don't understand all the JSON, AJAX, and all data related code in general it's still confusing to me.

    Any help, guide, tutorial will be VERY helpful thank you so much everyone!

  • an Undo system will actually take very little memory - small text entries in an array take a negligible amount of room even on old devices.

    and the code will work the same whether you allow 10 undo's or 1000.

    the trickiest part is how to handle deleting objects. The undo array would save the UID of the object that is changing, however when you undo a delete action you would have to create a new instance of the object and that would give it a different UID. Any previous actions in the undo array would have to know they now apply to the new UID. The alternative would be to not actually destroy the object - just move it to an off-screen holding area in case it gets brought back.

    I will take a crack at it later today - starting from the select / drag and drop example I was playing around with yesterday...

  • Thanks for the explanation and help, I will wait to see what you'll come up with! :)

    OFF TOPIC: (hope I will be able to apply the UNDO/REDO system to this)

    I used a different Drag n Drop solution for selecting Objects with much simplistic way for now, so my code will probably won't fit to what you're doing since you'll do it based on your previous code.

    My Drag n Drop solution based on a "Pan_Tool" object that have 3 modes:

    1st Mode is turning OFF (invisible) the tool

    2nd Mode is MOVING ALL instances on the layout

    3rd Mode is MOVING ONLY selected instances (based on SetColor) so all the colored can be moved.

    For now I press 'SPACEBAR' to change (cycle) between the modes if I need to move more than 1 instance.

    This simple "Pan_Tool" is always centered on the layout, After I Drag it to anywhere and DROP it, it will get back to the center. I know it's not the most efficient way but it was the only creative way I could came up with for a very simple code that my brain can actually understand.

    Back to Topic:

    I hope that I'll be able to follow it and apply it to my code even that it is very different.

    Thanks ahead for your kind help AllanR!

  • I did a test of an undo system with some editor I came up with.

    dropbox.com/s/y0yt072j0nyfbst/undo_test.capx

    Here are some thoughts.

    * For doing undo with creating/deleting sprites it works fairly well to do your own id system instead of using uid's. Basically a global variable nextID that you assign to an instance variable id, and increment every time you create an object.

    * Here I saved the id,x,y and angle of all the selected objects for every undo point. You could go more compact by just saving a list of ids and a change in position or angle for things like moving and rotating.

    * you could also merge consecutive move or rotate undo points as a way to make stuff more compact. It would depend on how you want editing done though.

  • nice ROJOhound! I like the way you did the rotate, and duplicate. I got around the UID issue by not actually deleting the objects - just moving them off-screen. Eventually at save time they would be destroyed and the undo stack purged.

  • OH MY! very impressive example R0J0hound! thank you for taking the time to make this.

    The selection, duplicate and rotation feels very "Windows'ish" which feels really good!

    To be honest I don't know if I can replicate this complex code to my project, because it is VERY different from my current code, but of course I can try because it is very useful!

    I already know that I may get issues because:

    1. Obviously, by looking at the code I don't understand most of it (blame on me).

    2. In my current project I click to create objects in random variations based on frames (you can see on the clean example I posted) the layout starts with no objects, so I have no idea how to make your code to work with creating different objects instead of pressing "a" to create a specific 1 type of object because of the all confusing UID which is new to me as well.

    But you and Allan made me very curious about these different solutions, it's amazing!

  • UPDATE:

    I just tried to follow step by step from R0J0hound and create each event one by one to my project (disabled my old code) just for sake of curiosity, but I got confused on the very first part with the functions because the new C3 functions are built in and when I try to replicate how it's done in the C2 version code it's not working the same because the function is actually an object so part of the code for example one action is related to it as an object: Function.Call("new id") which I cannot do in C3 since it's not an object.

    Wish I could understand this all thing and code such thing and learn from it, but I guess it is much more complicated for my simple logical thinking brain. too bad for me because I was really impressed of how it works.

    I guess I will have to make my software much more primitive after all.

    The reason I want to actually understand at least some of the code is that I will be able to tweak and change it when needed, but unfortunately I'm not a programmer but just an animator who can think nothing more than logic things in my brain. (If Then Else... = Result!).

    I'll keep trying to find some other ways to make my software... usable so the users won't get annoyed of how primitive it's functioning.

    Please feel free to share any suggestions, tips, tutorials or anything helpful for to help me out are more than welcome! :)

  • AllanR thanks, there are probably pros and cons of either way. I see in that other topic you did a redo too, which is pretty slick.

    Alon

    The example was mainly to try out various ideas. On one hand it’s a control scheme that I did for fun and the other part is a way to do undo. It was mainly to maybe give ideas, the bulk of it would be much different with different setups.

    The main contribution was to use a id variable instead of uid.

    Anyways the undo system is simply this in a nutshell. Right before modifying stuff save it to a list somewhere. Then when you want to undo, you take that list to restore how things were.

    My events do tend to be pretty dense and admittedly I use lots of tricks to make things more compact or make some things simpler. Tricks are mainly:

    Using text as a list and using tokenat to get values.

    Using pick by uid to get around picking a newly created instance.

    Using groups to scope away some variables to keep things a bit more organized.

    I also don’t use c3 but the way to call functions is just different but can be used in the same way.

    Probably the simplest way to do save as a beginner is to just save/load state I guess.

  • Sorry about the long post and about my bad English, I'm just sharing my experience so far:

    First of all, thanks for the detailed reply R0J0hound what you did for fun, was SUPER inspiring and made me try all day trying to replicate it (with many fails so I kind of gave up) mainly because of the Function didn't work the way I tried and didn't understand how to replicate this action, creating a Function was easy as a Click on the events but using it as the "Sprite" like you did "Function.call" or trying something else was too much for me to follow.

    I like how you managed the all code, organized it in groups (this is why I LOVE comments and groups so much) you really did a nice job!

    Before that I followed a lot of help from AllanR which not only SAVED THE DAY! but also was very creative and I'm trying to combine the both ways of you guys because at the end they worked great!

    The status right now for me is that my project file which became kind of big and messy, is not using a specific code to make a working UNDO/REDO and the other features from my other post which is the main idea so everything will work together.

    I'm trying to learn and understand but I must admit, my brain is not getting much more than basic logic, the other technical programming code and math are gibberish to me, no matter how much I try to understand... and I do have this problem that I LOVE TO LEARN so I can use things like that and upgrade them or tweak them if needed.

    SAVE SLOTS for UNDO/REDO:

    You guys suggested the option to use slots for alternative way, if it's not too complicated maybe I can try it myself and understand how it works by doing it.

    The thing is that I have different events such as drag, drop, resize, rotate, and more to come..

    So I'm trying to understand the basic idea:

    1 - Before any action, SAVE to a slot, for example: "DragSlot" will save before I drag the object.

    2 - Event: pressing CTRL+Z will load "DragSlot"

    But what if I want to undo ANY LAST action, not just dragging (DragSlot)? this is where I get confused,

    Looking at your examples doesn't really translate to logic for because as I said I'm not a programmer, that's why I'm asking stupid questions, after I tried to understand with no luck

    Do I have to use an ARRAY (I understand how Array works, but only on the paper not on C3 or how to use it after Setting it up) maybe I'm an Array'phobic!

    I believe that it is related to the objects UID using 1D array but it's only a guess my brain trying to follow the logic of it, also I have no clue how to translate such thing to actual C3 code.

    I still don't know if I need 1 slot for the UNDO or more? and if more... how do I use them?

    UNDO for size, undo for drag, undo for drop and so on? will this work?

    I'm so sorry for the stupid questions, I'm just trying to understand the nonsense programming events that I see and trying to translate it to more Human-Like logic sense but I guess it's not that simple after all.

    If you guys can explain it with screenshot or some other visual way it will be easier for me to follow, of course if you want I just rather learn instead of copy/past code.

    Thanks ahead for any help!

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • I made a demo which uses System Save/Load actions for undo/redo.

    dropbox.com/s/11udoddljxk9urs/UndoDemo.capx

  • dop2000 Thank you so much for taking the time making this!

    I just had a quick look at your example and not only it's very well documented with everything it is very clean and organized for me to follow!

    The only problem I may run into, when I'll try to replicate this type of code to my current project

    will be the Functions, since it's not an object anymore but a built-in, it works a bit different as "Object.whatever" if there are any on the code (I didn't look for deep yet).

    I do have some questions before I start inserting this way to my project

    1 - Is it limited to X Redo/Undo per session?

    and if not, is it taking a lot of memory because I will create and tweak and move probably hundreds of objects and I would like to make it work if possible also on older machines with less memory.

    2 - Is it easy or complicated to limit to X UNDO/REDO per session?

    3 - Since I'm using the system Save/Load on for saving: "whatever.myfile" will it save the undo/redo from the current session? because I don't need it to save UNDO's of course like most software.

    I'll probably have more questions later when I'll start to put this code on my project but for now I just asked the above before I started looking on it.

    Once again, thank you so much for the help! :)

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)