I’ve been working on a very small tool to help design new stuff in the game I’m currently making and there is one particular problem I faced recently that took long time to solve so I want to share the answers I found in case someone else tries to do the same.
In the tool, I’m automatically adjusting position, rotation, color and references for some of the Game Objects being used inside the game. When changing float, int and Color variables, everything works properly, using the Undo.Record function that Unity provides, all changes made inside the editor are saved and can be seen in Play mode or after closing the Scene with no issues.
However, when trying to save changes for a reference inside one of those objects, that reference was lost when entering to Play Mode or when closing the Scene. If changes are not stored after editing something, the tool doesn’t make sense.
If you search on Google, Unity Forums and Stack Overflow for solutions, you will find a lot of posts that reference three different functions:
- Undo.RecordObject , which records changes to an object after being called
- Undo.FlushUndoRecordObjects, this is used to register the objects recorded with the previous function as undoable actions
- EditorUtility.SetDirty , which marks objects as dirty
After testing all of them in different ways and trying to understand how the work, I found the following:
This function is needed when recording the status of an instance, the way it works is: call it before changing properties of an object and the changes will be saved. In my case, an example:
Undo.RecordObject(obj, "Planet has changed" ); obj.planet = pivotObject;
Changing the reference to obj.planet to a new object (pivotObject). Using this function as you see it in the example, works for other properties but not for references to other objects.
People pointed out that this function should have been used after Undo.RecordObject and changing the property you wanted to change to get the data stored. This didn’t work.
In a different post, someone recommended to use this function in the same way as FlushUndoRecordObjects. So the code would be:
Undo.RecordObject(obj, "Planet has changed" ); obj.planet = pivotObject; EditorUtility.SetDirty(obj);
Which, after a lot of tests, worked. However, if you go to Unity’s current documentation, they clearly say the following:
Note: ”Prior to Unity 5.3, this was the primary method of marking objects as dirty. From 5.3 onwards, with the introduction of Multi-Scene Editing, this function should no-longer be used for modifying objects in scenes. Instead, you should use Undo.RecordObject prior to making changes to the object. This will mark the object’s Scene as dirty and provide an undo entry in the editor.”
And if you are using a version above 5.3, I think it is important fo follow the official documentation so I kept reading the documentation and found the following:
Important: To correctly handle instances where objectToUndo is an instance of a Prefab,PrefabUtility.RecordPrefabInstancePropertyModifications must be called after RecordObject.
I know it seems stupid but I didn’t notice that the object I was trying to change was in fact the instance of a prefab, I mean, I didn’t know there was an internal difference when it comes to the use of these specific functions.
So, in the end, I tested the following code that worked perfectly:
Undo.RecordObject(obj, "Planet has changed" ); obj.planet = pivotObject; PrefabUtility.RecordPrefabInstancePropertyModifications(obj);
I know it seems simple and stupid after carefully reading the documentation and seeing the solution but this small issue took a lot of development time, searching through different solutions from previous versions of Unity, current ones and with different approaches that didn’t work.
I hope this can help other developers to save some time when doing something similar to what I did. If you have questions or comments, don’t hesitate to reach out.
Leave a Reply
You must be logged in to post a comment.