Custom GUIDs in Unity
- Introduction
- The Problem
- GUIDs
- The ExecuteAlways Attribute
- Editor APIs: SerializedProperty and SerializedObject
- C# Preprocessor Directives
- The nameof Expression
- Stop Constantly Changing GUIDs
- Handling Duplication
- Handling New GameObjects
- Handling Prefab Instances
- Handling Prefabs (EditorAPIs)
- Full Script
Introduction
If you’ve ever tried to save data for many entities in a video game, you’ve probably come across an approach that looks like this:
-
Associate each entity with a persistent, unique ID.
-
Create a key-value store (like a Python dictionary), using these IDs as keys and records with the relevant data for each entity as values.
-
Write the key-value store to disk / read the key-value store from disk as necessary.
It’s possible to go into significantly more detail for each of these steps, but this article will focus primarily on the first one: assigning persistent, unique IDs.
The Problem
Specifically, this article will explain how to create a MonoBehaviour script which, when attached to a GameObject, will assign it a unique ID that persists between different play sessions.
With a script like this, you would want these IDs to be unique even in the following situations:
-
A developer duplicates a GameObject (in the editor).
-
A developer converts a GameObject into a prefab (in the editor).
-
A developer instantiates a prefab (in the editor).
If a normal MonoBehaviour script were attached to the GameObject in any of these cases, then the fields on that script would be duplicated along with the GameObject.
You would have GameObjects with the same IDs as their prefabs, or duplicate GameObjects with identical IDs. This would make it impossible to save/load data for individual GameObjects.
GUIDs
First of all, let’s settle what a GUID is. In short, a GUID (also known as a UUID) is a 128-bit data structure that represents a globally unique ID.
As the name of this article implies, we’ll be using GUIDs as our IDs in this article.
Specifically, we’ll be (automatically) creating these GUIDs in the editor, so that the specific GUID value for each GameObject is part of its scene. This will keep these GUIDs the same every time the player plays the game.
Unity already uses GUIDs to refer to different assets in the editor (and you can actually see them in use if you look into the .meta or .asset files in a Unity project).
It’s worth nothing that Unity already creates unique IDs to refer to each GameObject. However, these IDs will change between player runtime and Editor sessions, making them useless for the purposes of saving/loading data.
The ExecuteAlways Attribute
In Unity, our C# scripts typically run during a player’s runtime session. That is, they execute code either when we play the game in the editor or when the player is running an actual build.
Since we’re creating GUIDs in the editor, we’ll need to execute some code in the
editor itself. That is, we’ll want the script to execute before we even start
the game, as we create prefabs or instantiate GameObjects while in the editor.
To achieve this, we’ll use
the ExecuteAlways
attribute.
It’s worth noting that there is an
older
and similar attribute called ExecuteInEditMode
, which has been deprecated.
Annoyingly, this deprecation warning only shows up in the middle of its
documentation (at least for Unity version 2021.3).
Example: ExecuteAlways
The syntax for the ExecuteAlways attribute is fairly straightforward. This example is taken from Unity’s documentation for the attribute:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;
[ExecuteAlways]
public class ExampleClass : MonoBehaviour
{
void Start()
{
if (Application.IsPlaying(gameObject))
{
// Play logic
}
else
{
// Editor logic
}
}
}
As seen on line 3, this class has been marked with ExecuteAlways
. As a result,
it now has the ability to execute code at both runtime and in the editor itself.
To execute different code between runtime and the editor, we can use the
Application.IsPlaying
method, passing the script’s own GameObject as an
argument. This method will return true if we’re in a play session, and it will
return false if we’re only in the editor.
Start, Awake, and Update
With a normal script, the Start
, Awake
, Update
methods execute when the scene is
loaded and every frame, respectively. Both of these methods only run during a
play session. With the ExecuteAlways
attribute, these methods will execute
in the editor as well as during a play session. Specifically:
-
In the editor,
Start
will execute when a script is first attached to a GameObject and when the scene is loaded. -
In the editor,
Awake
behaves similarly toStart
, except that it is executed first and it will execute even if the script component is disabled. -
In the editor,
Update
will be called whenever something in the scene is changed.
We’ll be using the Awake
and Update
methods in our script. Though we could
conceptually do everything in Update
, we’ll use Awake
to perform
particularly expensive operations as a performance optimization.
Script Usage: ExecuteAlways
Let’s start our actual script, using these features:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Using statements...
[ExecuteAlways]
public class CustomGUID : MonoBehavior
{
private void Awake()
{
if (!Application.IsPlaying(gameObject))
{
// Only in editor
}
}
private void Update()
{
if (!Application.IsPlaying(gameObject))
{
// Only in editor
}
}
}
In this script, both our Awake
and our Update
methods will only execute code
in the editor. They won’t execute any code when the player is actually playing
the game.
Editor APIs: SerializedProperty and SerializedObject
We need the Editor APIs to properly modify our GUID field from the editor (especially if the script is attached to a prefab asset).
But before we look at the Editor APIs, let’s first look at our guid
field.
Script Usage: String Field For GUID
We’ll store our GUIDs as strings, via a field:
1
2
3
4
5
6
7
8
9
// Using statements...
[ExecuteAlways]
public class CustomGUID : MonoBehaviour
{
public string guid;
// Awake and Update methods...
}
Though Guid
already exists as a part of
C#'s
standard library (and we’ll be using this type to generate our GUIDs), we’ll be
storing them as strings for a few reasons:
-
Unity’s built-in serialization doesn’t directly support
Guid
fields, but it does support strings. -
By directly serializing strings, we can easily view the GUIDs in the editor itself, seeing the typical hexadecimal representation for a GUID.
From here on, if I refer to the GUID of a GameObject, I mean the guid
field
of a CustomGUID
script that has been attached to that GameObject.
A Naive (and Somewhat Buggy) Approach
Under normal circumstances, you would update this guid
field by simply
assigning to it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Using statements...
[ExecuteAlways]
public class CustomGUID : MonoBehaviour
{
public string guid;
private void Awake()
{
if (!Application.IsPlaying(gameObject)) {
// Assign a new GUID as necessary
guid = Guid.NewGuid().ToString();
}
}
// Update method not implemented...
}
When simply assigning to GameObjects or duplicating them, this code will assign GUIDs properly.
However, when this CustomGUID script is attached to a prefab, an annoying bug
will appear: the script will be unable to properly read from and write to the
guid
field.
That is, when attached to a prefab asset, the script will always consider the
guid
to initially be null
, even if the prefab is actually storing a valid
GUID in the field. This makes it difficult to detect when we should generate a
new GUID (we’ll discuss the exact algorithm for this later).
Additionally, this approach will fail to write to a script’s field in a prefab asset. Even though it would seem like values have been assigned to these fields, those values would not persist in the prefab asset itself.
To solve these problems, we will use parts of the Unity API that are normally seen in custom editors: the SerializedObject and SerializedProperty classes.
Why Use SerializedObject and SerializedProperty?
In short, SerializedObject
and SerializedProperty
are how you’re supposed to
modify the fields of a MonoBehavior script when executing code in the Unity
editor itself. When used, they will automatically support key editor
functionality like undo and prefab overrides.
For our use case, we’re more interested in the fact that these classes avoid the prefab-related bugs mentioned earlier.
Editor API: SerializedObject
As its name might imply, SerializedObject
represents an object and will
indirectly allow us to modify the fields in this object. Specifically, it
represents an object that descends from the Object
class
provided
by Unity.
It’s worth noting that C# also provides an Object
class, and that
all
classes in C# inherit from this class. Somewhat confusingly, this Object
class is not the Object
class provided by Unity.
Both GameObjects and MonoBehavior scripts descend from Unity’s Object
class,
so we’ll be able to use SerializedObject
to represent the script that we want
to modify.
Concretely speaking, we’ll only directly use a SerializedObject
to gain access
to a SerializedProperty
.
Editor API: SerializedProperty
The SerializedProperty
class allows us to actually modify an object’s field.
However, in order to obtain it, we first need to create a SerializedObject
(which is why I described SerializedObject
first).
While using this part of the Editor API, we need to follow these 4 broad steps to modify an object’s field:
-
Obtain a
SerializedObject
that represents our MonoBehavior script. -
Obtain a
SerializedProperty
from theSerializedObject
that represents a specific field. -
Tell the
SerializedProperty
that you want to change the relevant field. -
Finalize your changes via the
SerializedObject
.
The following example code will show how to set our guid
field via these parts
of the Editor API:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Other using statements...
using UnityEditor;
[ExecuteAlways]
public class CustomGUID : MonoBehaviour
{
public string guid;
private void Awake()
{
if (!Application.IsPlaying(gameObject)) {
// For now, always assign a new GUID
// Step 1
SerializedObject serializedObject = new SerializedObject(this);
// Step 2
SerializedProperty guidProperty =
serializedObject.FindProperty("guid");
// Step 3
guidProperty.stringValue = Guid.NewGuid().ToString();
// Step 4
serializedObject.ApplyModifiedProperties();
}
}
// Update method not implemented...
}
The 4 statements in our Awake
method shown above correspond to the 4 steps
necessary for us to modify a field.
There are two potentially problematic details here:
-
On lines 18/19, we obtain a SerializedProperty by searching for a field with the name
"guid"
(via a string argument to a method). This may lead to bugs if we ever change the name of this field (which we will address later in this post). -
As seen on line 2, we have to write a
using UnityEditor
statement, otherwise we won’t have access toSerializedObject
andSerializedProperty
from the Editor API. This will lead to problems when making builds of our game (as we will shortly discuss).
Problem: We Can’t Build!
If you tried to build a project that uses SerializedObject
and
SerializedProperty
in a MonoBehavior script (as we demonstrated above), you
would run into an annoying problem: the project would fail to build entirely.
Our problem most directly originates with our use of the UnityEditor
namespace. We need this namespace to access SerializedObject
and
SerializedProperty
, but, as stated in its
documentation,
we can’t reference this namespace in scripts that are compiled for a
final build.
APIs in UnityEditor
are typically used in when writing custom Editors in
Unity. In our case, we’re not doing this. Instead, we’re using these APIs in a
MonoBehavior script that has been annotated with the ExecuteAlways
attribute.
As with normal MonoBehavior scripts, our script will be compiled when making a
build.
Here’s a key insight: even though our CustomGUID
script will be compiled for a
user-facing build, only part of its functionality needs to be available when the
game is running.
-
In the game itself: we need access to the GUID for each relevant GameObject. This does not require any of the Editor APIs. This essentially just requires the
guid
field. -
In the editor: we need to ensure that the GUIDs attached to GameObjects are unique, even when a GameObject is duplicated or instantiated from a prefab. This will require the Editor APIs, so that we can properly assign to the
guid
field from the editor itself.
With this in mind, we can make a key conclusion: we only need the Editor APIs while in the Unity editor itself.
C# Preprocessor Directives
In order to make our project build properly, we’ll be using something called C# preprocessor directives.
With this feature of C# (and its integration with Unity), we can avoid compiling the editor-specific part of our script in player-facing builds.
Example: C# Preprocessor Directives
Let’s look at a brief of example of the relevant syntax:
1
2
3
4
5
#if UNITY_EDITOR
Debug.Log("Hello, editor!");
#endif
In the example above, the #if UNITY_EDITOR
and #endif
lines are preprocessor
directives[1]. With these directives, the Debug.Log("Hello,
editor!")
line will only compile within the editor itself. When we make a full
build for the game, this line (and everything between these two directives)
won’t be compiled at all. From the compiler’s point of view, it’ll be as if
these lines simply aren’t in our source code.
This technique is called conditional compilation, as it only compiles parts of the code when certain conditions are true (like being in the Unity editor or not).
Script Usage: C# Preprocessor Directives
Now that we have an understanding of the syntax, let’s use C# preprocessor directives in our script itself:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Other using statements...
#if UNITY_EDITOR
using UnityEditor;
#endif
[ExecuteAlways]
public class CustomGUID : MonoBehaviour
{
public string guid;
#if UNITY_EDITOR
private void Awake()
{
if (!Application.IsPlaying(gameObject)) {
// For now, always assign a new GUID
SerializedObject serializedObject = new SerializedObject(this);
SerializedProperty guidProperty =
serializedObject.FindProperty("guid");
guidProperty.stringValue = Guid.NewGuid().ToString();
serializedObject.ApplyModifiedProperties();
}
}
#endif
// Update method not implemented...
}
There are currently 2 blocks of code that we conditionally compile, since both of them involve the Editor APIs:
-
The
using UnityEditor
statement. -
The
Awake
method.
When we’re in our editor, both of these sections of code will compile, ensuring that our script can assign different GUID values as necessary.
When making a final build, however, both sections of code will be ignored,
leaving our script with no references to UnityEditor
or anything in that
namespace. This will allow us to successfully create a final build. This final
build will still have access to the guid
field, as it is not surrouded by
preprocessor directives.
The nameof Expression
As we mentioned earlier, we have to obtain a
SerializedProperty by searching for a field with the name "guid"
. If we were
to change the name of our field (e.g. from "guid"
to "id"
), then we would
need to change the argument passed to the SerializedObject.FindProperty
method
as well.
Fortunately, we can automatically obtain the name of our guid
field using a
feature of C# called the
nameof
expression.
As its name would imply, this expression allows us to obtain the name of a variable, type, or field as a string constant.
Script Usage: nameof
Since this feature is rather simple, let’s use just use it in our script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Other using statements...
#if UNITY_EDITOR
using UnityEditor;
#endif
[ExecuteAlways]
public class CustomGUID : MonoBehaviour
{
public string guid;
#if UNITY_EDITOR
private void Awake()
{
if (!Application.IsPlaying(gameObject)) {
// For now, always assign a new GUID
SerializedObject serializedObject = new SerializedObject(this);
SerializedProperty guidProperty =
serializedObject.FindProperty(nameof(guid));
guidProperty.stringValue = Guid.NewGuid().ToString();
serializedObject.ApplyModifiedProperties();
}
}
#endif
// Update method not implemented...
}
Compared to our previous example, we’ve only changed one line: line 21.
Specifically, we changed our argument from the strong literal "guid"
to the
expression nameof(guid)
. This will have the same functionality while being
more robust.
If we were to rename the guid
field (perhaps to id
) in an IDE like Rider,
then our IDE would automatically use the new name on line 20 for us. By using
this feature, we no longer need to worry about changing the argument to
SerializedObject.FindProperty
.
Stop Constantly Changing GUIDs
Up to this point, you may have noticed a possible quirk in our implementation: we assign a new GUID to each GameObject every time we load a scene (or fully open up a prefab).
There are a few annoying issues with this approach:
-
Whenever leaving a scene, the Unity editor will ask if we want to save, even if it doesn’t seem like we’ve changed anything (as the GUIDs of our GameObjects will have changed).
-
Our commits in git will constantly show scenes and prefabs being changed, even if we didn’t apparently modify them (as the GUIDs of their GameObjects will have changed upon opening them).
To solve these issues, we’ll use a different approach entirely: we’ll only modify a GameObject’s GUID as necessary. We will begin by showing how this approach handles the duplication of GameObjects.
Handling Duplication
As we mentioned near the beginning of this post, we need to ensure that the GUID is different when a GameObject is duplicated.
Normally, when a GameObject is duplicated, all of the fields in its attached scripts are duplicated as well.
However, we want the guid
field in the CustomGUID
script of a GameObject
to not be duplicated (as this stores our GameObject’s GUID). How can we
achieve this?
Ideally, we would want to run code to generate a new GUID whenever the GameObject is duplicated. Unfortunately, Unity doesn’t provide any direct ways to achieve this.
This forum post seems to describe an indirect way to do this, but the approach shown there will also run code when a scene is first entered, so it’s not viable for our use case. (If we generated a new GUID whenever we entered a scene, then our GameObjects would change GUIDs constantly, leaving us with the annoying issues from earlier.)
Script Usage: Handling Duplication
Since we can’t run code specifically when a GameObject is being duplicated,
we’ll simply have each CustomGUID
script check the other GUIDs to see if our
current GUID is a duplicate:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Just the Awake method is shown here
#if UNITY_EDITOR
private void Awake()
{
if (!Application.IsPlaying(gameObject))
{
SerializedObject serializedObject = new SerializedObject(this);
SerializedProperty guidProperty =
serializedObject.FindProperty(nameof(guid));
// Potential performance problem, but fine for dozens of GameObjects
CustomGUID[] customGuids = FindObjectsOfType<CustomGUID>();
foreach (CustomGUID customGuid in customGuids)
{
if (customGuid.guid == guid && customGuid != this)
{
guidProperty.stringValue = NewGuid();
}
}
serializedObject.ApplyModifiedProperties();
}
}
#endif
Though this approach will correctly prevent duplicate GameObjects from having
the same GUIDs, it has a potential performance problem. Since each CustomGUID
will have to check all of our CustomGUID
scripts, this section of code will
have a run time of O(n2), which will be abysmal with large numbers of
CustomGUID
scripts (i.e. large numbers of GameObjects with CustomGUID
scripts attached).
Perhaps due to the speed of modern computers (and the fact that this code is in
Awake
rather than Update
), this performance is fine when you have dozens and
dozens of GameObjects in your scene. However, as you approach hundreds of
GameObjects, the performance problems become significantly worse, at least on
the gaming laptop I used for testing.
To solve this performance problem, you can cache the GUIDs that have been used so far. Using a Dictionary or a HashSet in a static variable for this cache will allow you to achieve rapid look-ups and avoid redundant insertions.
I won’t go into too much detail regarding this optimization, as this post is primarily focused on the core techniques and features I used to set up persistent GUIDs in Unity. The underlying approach is the same regardless of whether or not you cache currently-used GUIDs, and the code shown here is optimized enough for use as an explanatory example.
Handling New GameObjects
When first attaching a CustomGUID
script to a GameObject, we (perhaps
obviously) need to give it a GUID.
To avoid having a new GUID every time we load the scene, we’ll use null
as the
default value for our guid
field (null
is already the default value for a
string field) and then initialize guid
if it has not been initialized before:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Inside the CustomGUID class
#if UNITY_EDITOR
private void Update()
{
if (!Application.IsPlaying(gameObject))
{
SerializedObject serializedObject = new SerializedObject(this);
SerializedProperty guidProperty =
serializedObject.FindProperty(nameof(guid));
if (String.IsNullOrEmpty(guidProperty.stringValue))
{
guidProperty.stringValue = NewGuid();
}
serializedObject.ApplyModifiedProperties();
}
}
#endif
Additionally, since we’re now generating GUIDs in more than one place, let’s refactor that GUID-generation code into its own little method:
1
2
3
4
5
6
// Inside the CustomGUID class
private string NewGuid()
{
return Guid.NewGuid().ToString();
}
It’s fine for us to place this code in Update, since checking the GUID every time something in the scene changes is cheap—cheap enough to be negligible.
In the Update
method, we reuse many of the same features we saw before: Editor
APIs (SerializedObject
and SerializedProperty
), the nameof
expression, and
functionality related to ExecuteAttribute
(Application.IsPlaying
). Since
we’re familiar with these features already, our functionality is fairly
straightforward.
The only particularly new detail is the use of String.IsNullOrEmpty
. This is
just a little utility function from
C#'s
standard library which checks if a string is null
or empty.
Checking for an empty string is useful for debugging purposes, as it allows us
to force new GUIDs to be generated from the editor. Simply by clearing the
guid
field from the Inspector window, we can force this script to generate a
new GUID. This benefit is also only possible in the Update
method. If we were
to check for null
or empty in the Awake
method, then we wouldn’t see an
instant response, as we would have to wait until the Awake
method is next
called (probably the next time we leave and re-open the scene).
Handling Prefab Instances
We want to ensure that the GUID of our prefab is different from the GUID of any of its instances.
Fortunately, the Editor APIs provide a fairly straightforward way to accomplish this:
-
The
SerializedProperty.isInstantiatedPrefab
property tells us if our property is part of a script which is attached to a prefab instance. -
The
SerializedProperty.prefabOverride
property tells us if the property has been modified from the property’s value in its parent prefab (assuming that the property is attached to a prefab instance).
Script Usage: isInstantiatedPrefab and prefabOverride
Let’s use these 2 properties to check if the GUID of a prefab instance is the same as the GUID of its prefab:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Inside the CustomGUID class
#if UNITY_EDITOR
private void Update()
{
if (!Application.IsPlaying(gameObject))
{
SerializedObject serializedObject = new SerializedObject(this);
SerializedProperty guidProperty =
serializedObject.FindProperty(nameof(guid));
if (String.IsNullOrEmpty(guidProperty.stringValue) ||
(guidProperty.isInstantiatedPrefab && !guidProperty.prefabOverride))
{
guidProperty.stringValue = NewGuid();
}
serializedObject.ApplyModifiedProperties();
}
}
#endif
Our changes can be seen on line 14, with the addition of new conditions to our
if
statement.
By checking the negation of guidProperty.prefabOverride
specifically, we can
check if the guid
field in our prefab instance is the same as in our original
prefab asset.
Using this fact, this code directly checks if our script is part of a prefab
instance and if our guid
field is the same as the original prefab asset. If
so, it generates and assigns a new GUID.
Handling Prefabs (EditorAPIs)
Ideally, we want the GUID of a prefab itself to be null, to prevent us from saving/loading information for a prefab when we mean to be saving/loading information for its instances.
That is, if we ever accidentally access the GUID of a prefab (while intending to
access the GUID of one of its instances), we want a glaring null
value to show
us that we’re doing something wrong.
Ideally, we would want to execute some code whenever we create a new prefab. Unfortunately, as far as I can tell, Unity does not offer any way for us to do this.
As a result, I’ve adopted this approach: set the guid
field for a CustomGUID
script to null
as soon as we’ve detected that it’s attached to a prefab asset.
There are two places in the Unity editor where we might modify a prefab:
-
While we have a scene open, and we are only previewing the prefab (viewing the prefab solely in the inspector).
-
When we have the prefab opened fully, in its own special scene which exists just to edit that prefab.
To detect whether or not a CustomGUID
script (or any other script) is attached
to a prefab asset, we have to detect both of these cases.
PrefabStage and PrefabStageUtility
Despite my best efforts, I was only able to detect the second case, when we have the prefab fully opened in its own special scene.
To accomplish this, I used the PrefabStage
and PrefabStageUtility
classes,
which are part of the editor API.
-
A
PrefabStage
represents a special, prefab-specific scene that allows us to edit all of the scenes of a prefab. In the editor, we normally reach this scene by double-clicking on a prefab. -
PrefabStageUtility
contains multiple utility methods, one of which allows us to check if we’re currently in a special prefab scene.
Script Usage: PrefabStage and PrefabStageUtility
Let’s create a method which will check if our script is attached to a Prefab asset (while in a special prefab scene):
1
2
3
4
5
6
7
8
9
10
11
// Inside the CustomGUID class
#if UNITY_EDITOR
private bool IsPrefab()
{
PrefabStage currPrefabStage = PrefabStageUtility.GetCurrentPrefabStage();
return currPrefabStage != null &&
currPrefabStage.IsPartOfPrefabContents(gameObject);
}
#endif
These new Editor APIs are located in the UnityEditor.SceneManagement
namespace, so we’ll need to add a new conditionally-compiled using
statement:
1
2
3
4
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
#endif UNITY_EDITOR
Now, let’s use this IsPrefab
method in our Update
method:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Inside the CustomGUID class
#if UNITY_EDITOR
private void Update()
{
if (!Application.IsPlaying(gameObject))
{
SerializedObject serializedObject = new SerializedObject(this);
SerializedProperty guidProperty =
serializedObject.FindProperty(nameof(guid));
if (IsPrefab())
{
guidProperty.stringValue = null;
}
else if (String.IsNullOrEmpty(guidProperty.stringValue) ||
(guidProperty.isInstantiatedPrefab &&!guidProperty.prefabOverride))
{
guidProperty.stringValue = NewGuid();
}
serializedObject.ApplyModifiedProperties();
}
}
#endif
The major changes here come on lines 13-16, where we check if we’re in a full
prefab scene and set the GUID to null
accordingly.
It’s important that line 17 be an else if
rather than another if
statement,
otherwise we’ll end up generating a new GUID immediately after setting our field
to null
.
Now, whenever we fully open a prefab in the Unity editor, its GUID will be
automatically set to null
.
What About Prefab Previews?
As I alluded to earlier, I never managed to detect the first prefab case (which is when we have a normal scene open in the editor and a prefab previewed in the Inspector window).
Though this is not ideal, it’s still acceptable, and the script is still usable.
Even if we never open a prefab fully and the prefab continues to have a
non-null
GUID, our changes from the Handling Prefab Instances section will
prevent us from facing any bugs outright.
In the future, as I continue to use this script, I may discover how to handle this case.
Full Script
Download here for the full script.
Hopefully, this post helps you understand how this script operates, in case you ever want to modify it.
In C#, the compiler doesn’t use a separate preprocessor, but it does process each of these directives as if there were a separate program running before the rest of the compiler.