Closed rewin123 closed 1 month ago
I added an example, I think it's clear further how to use it
Looks good, would it be possible to warn (once) about changes to components which did not register undo?
Thanks! Do you want throw warnings about changes to components whose changes are hidden with OneFrameUndoIgnore? Or warn about changes in all components that are not registered via auto_reflected_undo?
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
I meant the latter, I'm not sure what first is about 🤔
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
Looking awesome! is there a way we can help with anything? like adding more features
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. Another question I have, is how (if at all) does your system deal with the fact that modifications could happen over multiple frames, e.g. dragging the position of something using a gizmo - as you wouldn't want the undo stack filling up with every frame the thing was modified. Complex relationships are always a challenge when building commands undo systems, the way I'm planning on implementing undo (unless I end up being able to use your system!) is just serialising the bevy world every time an undo point needs to be set - I haven't tried this approach yet though, so I don't know how well it will work!
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
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?
@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 ^_^
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.