-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Undo and redo logic with generic atomic actions and auto component change undo/redo #2
Conversation
Looks good, would it be possible to warn (once) about changes to components which did not register undo? |
Thanks! |
I meant the latter, I'm not sure what first is about 🤔 |
Looking awesome! is there a way we can help with anything? like adding more features |
Theoretically yes. But we need to create a system to request changes to all components for all entities. That could be an expensive calculation, I think. OneFrameUndoIgnore is component for hiding entity from undo systems during two frames. Its need to no register changes, which made by Undo or Redo action |
Thanks! This plugin does not support automatic change logging for resources and for entity creation and destruction events. Probably some of these things should be added next. |
This looks really cool, and interesting to see your implementation of this. I tried myself to implement a commands based undo system in bevy for a 3rd party editor I'm working on, but ultimately gave up due to complications arising from things like other components storing entity IDs which, if deleted and reinserted in the undo system, would then have a different entity ID. How does this implementation get around this problem? |
Ugh. That's a very good question. The proposed Undo plugin solves this problem partially. If deletion of a group of entities was detected, Undo can be used to restore the whole group, and the plugin will also set up-to-date values of all fields with Entity type in all restored components by recursive function based on Reflect trait. So, ingroup entity links will be work after Undo action. However, if entities outside the deleted group had references to that group, they will not be restored as there is no created logic to restore Entity references. Although I have an idea how to do it a bit brootforce style. For example, register all components that may have references and when recreating an entity using Undo, run through all registered components with references, through all their fields with Entity type and if there is an id with a restored Entity, replace the value with up-to-date. |
You could also, perhaps, instead of actually deleting entities, delete all the components off the entity (so it's just an empty entity) and then replace all the components on an undo - that way the ID is reserved so it doesn't need to be manually replaced. |
The entity saving approach could indeed solve the problem of saving dependencies. However, if there are frequently created short-lived entities in the world ( for example particle system), it is actually possible to overflow the u32 index of the entity without deleting them. (Although it would take 111 hours for 10000 entities per second, this is still achievable). The proposed system has two stages for dealing with time durationally extended changes. The first stage is to register the change of a component using Change. As soon as a component is changed the entity is marked with ChangeMarker. The second system waits until all marked components are no longer in the changed state. Once this happens, the system waits two more frames (so as not to register changes to the stack due to a one-frame stop) and only then creates an event that a component of such an entity has been changed. The event is caught by the system that manages the entire Undo system stack. If multiple changes happen in a single frame or multiple frames in a row, they are logged as a single combined ManyChanges change. This allows you to undo an time extended change with a single Ctrl-Z. For example, such a combination is necessary because the change of position from parent to child is transferred with a delay of 1 frame from the Transform calculation to the PostUpdate schedule. And it also allows any other changes that have been passed down the reaction chain to be combined. |
ah cool, that sounds pretty great. Your system seems really cool and far beyond anything I was able to achieve! If you can implement a way to register entities that may hold entity references which would need to be updated, then I'd love to be able to start using your system in my own editor. The entity reference thing is important for me because my editor stores a graph data structure where each entity holds references to 'next' and 'previous' entities. Without entity ref updating things would very quickly break! |
@rewin123 I'm inclined to merge this; can you swap the crate name to match the new crate structure? |
Yeah. I just finished updating to the latest version of bevy and renamed crate in the process ^_^ |
}) | ||
.insert(Controller) | ||
.insert(UndoMarker) //Only entities with this marker will be able to undo | ||
.insert(OneFrameUndoIgnore::default()); // To prevert add "Transform add" change in change chain |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.insert(OneFrameUndoIgnore::default()); // To prevert add "Transform add" change in change chain | |
.insert(OneFrameUndoIgnore::default()); // To prevent adding "Transform add" change in change chain |
This code provides a lightweight solution with minimal dependencies for implementing automatic undo functionality in an editor. The key concept behind this library is to leverage Add, Changed, and RemovedComponent filters to dynamically create a chain of world state changes.
Core concept
All important world change can be describe as
These changes are then stored in the resource as
Vec<Arc<dyn EditorChange>>
, allowing seamless sequential undo operations through the implementation of the revert method within the EditorChange trait.Change chain and undo/redo demonstration from space_editor crate
Example usage
Minimal example
This is example from test, that restore two entities with parent-child relation after destruction
While the code is currently marked as a draft in the pull request, it lacks sufficient documentation and could benefit from improved code cleanliness. Despite these issues, the implemented functionality holds significant potential for future editor prototypes. I'm seeking assistance in determining the level of documentation required for the code to be accepted into the project.