It turns out that Undo.RecordObject is not a magic bullet solution for all undo situations. There are some changes to an object that can be accurately reverted thanks to RecordObject, but sometimes some changes lead to a corrupted object upon undo.
Here is an attempt to remedy the situation:
CodePudding user response:
Reason
This is because Undo will combine changes together. And it seems like that only the oldest modification is reserved when Unity tries to combine array operations. And it seems like that Unity collapses all operations that change the size of an array because it treats them as the same type of operation.
Undo operations are automatically combined together based on events.
At present, it can be determined that the MouseDown event can automatically split these undo operations, but MouseDrag will not.
Reproduce
Let's see a simple example, these are what Unity is recorded:
Add 3 elements 1, 2, 3 by click.
size 0->1
size 1->2
size 2->3
Remove them by dragging from 3 to 1.
size 3->2, array[2] 3->3
size 2->1, array[1] 2->2
size 1->0, array[0] 1->1
Perform an undo action.
Here since the operations in step 2 are performed by dragging, they are combined, and Unity only reserves the oldest size modification
size 3->2, array[2] 3->3
, the action will just dosize 2->3, array[2] 3->3
, the final result you will get is[0, 0, 3]
.By the way in step 2 if you remove elements from 1 to 3, undo will revert to the desired result.
size 3->2, array[0] 1->2, array[1] 2->3, array[2] 3->3
size 2->1, array[0] 2->3, array[1] 3->3
size 1->0, array[0] 3->3
Solution
Here's some approaches to avoid the problem.
Don't record objects in MouseDrag event.
Manually increase undo group index.
private void RemoveCell(ListObj obj, int index) { Undo.IncrementCurrentGroup(); Undo.RecordObject(obj, "remove cell"); obj.content.Remove(index); }
Use RegisterCompleteObjectUndo.