Howdy,
This post is mainly about the question at the end, but needs some context first.
I recently started reading Robert Nystrom’s Game Programming Patterns book, and one of the patterns mentioned which caught my attention was the “Prototype” pattern. Nystrom summarises the key idea of the pattern as:
An object can spawn other objects similar to itself… Any monster can be used as a prototypal monster used to generate other versions of itself.
This instantly reminded me of the Object.Instantiate() method, which I’ve probably called a million times. However, reading the chapter made me think of other ways of using it. Especially when it comes to instantiating based on Prefabs.
When I use Instantiate() to create a new GameObject in a Scene, I would usually pass in a Prefab asset as the object to be cloned. It’s then often necessary for me to use several lines of code or a method to set values on the new GameObject using data and references from the Scene. (I’m sure as beginners we’ve all erroneously tried to avoid this by dragging Scene GameObject references into a Prefab, and been frustrated before learning why it doesn’t work)
My realisation while reading the book is that there could be a much simpler way. You could drag a Prefab instance into the Scene, initialise its references in the Inspector, and disable it to prevent it from doing anything. Then you pass that instance into Instantiate() instead, so that any new GameObjects already have Scene data references. You still have the convenience of the Prefab connection, but you no longer have to code the fetching of references from the Scene if all the instances are expected to share some reference.
Probably this is not a new idea to many of you, but it’s completely flown past me thus far. However, in trying this idea out I struggled with a bug that was tough to track down - my code only worked as intended if I applied the [SerializeField] attribute to a particular private (MonoBehaviour) variable. Without that attribute, the cloning produced weird results.
It was a nightmare to find the cause, partly because I thought I was only adding and removing this attribute so I could see data in the Inspector for my own debugging purposes. I did not consider that this attribute itself could be the cause of such a massive behavioural change in Play mode. I perhaps somewhat naively think of that attribute as more of an Editor thing, even if it does live in the UnityEngine namespace.
So, the question is basically, why exactly does this happen?
The obvious guess to me is that if the variable is private, and it is not serialized, then its value cannot be included in the cloning process, right? Are there any other important details, or reasons to be wary of the pattern described above? My use case involved cloning some GameObjects, then giving them children via Instantiate() too. I realised sometimes the children were cloned, sometimes they weren’t.
Finally, I had a look at the documentation pages for both Instantiate() and [SerializeField], but couldn’t find anything on either that might’ve forewarned me of this behaviour. I also had a quick reread of the Script Serialization manual page, but couldn’t find this detail. Would it be possible to get something about this in there?