0
\$\begingroup\$

In Unity, I am trying to disable a Game Object in the scene by passing it through SerializeField. I have read elsewhere that this was previously not possible and is inadvisable, but I seem to be able to do it now. What are the problems with doing this?

\$\endgroup\$
2
  • 3
    \$\begingroup\$ Can you quote the specific advice against this that you found, saying that it is "inadvisable"? Without a source, we're left trying to read the mind of someone we know only via hearsay - not a great way to get good answers. When you try writing your code this way, do you encounter any unwanted outcomes? \$\endgroup\$
    – DMGregory
    Aug 4 at 20:58
  • \$\begingroup\$ "by passing it through SerializeField" How to do it ? have any teach !? \$\endgroup\$
    – TimChang
    Aug 5 at 3:22

2 Answers 2

4
\$\begingroup\$

If you have something like...

[SerializeField] GameObject myObject;

...then this is a private variable that's been flagged to be processed by Unity's serializer - the system that records information about an instance that needs to be saved in a scene or asset file, and populated when that scene or asset is loaded (deserialized). This is also the system that Unity uses to draw the Inspector interface for editing your object.

The upshot is that you can assign a value to this field in the Inspector, and that value will be saved so that it persists into the running game, or between runs of the editor so you don't lose any work.

So far this is just like what you get if you marked the variable public instead (since public fields are serialized by default - unless you use the [System.NonSerialized] attribute to exclude them), but with one major difference: a public field can be read and changed by any other script that can get a reference to this instance. In contrast, a private field can only be read or changed by code inside the same class (or struct).

This is a form of information hiding and encapsulation, making it so that most of the code in your project doesn't know this variable exists.

Why would we want to hide information? Lots of ink has been spilled on this, but in short:

  • It reduces the surface area for bugs. If under some circumstance this variable gets an invalid or inappropriate value assigned to it, causing a problem in your game, you want to be able to quickly identify the code that assigned that bad value.

    If the variable is private, you have only a small amount of code to search - typically just one file.

    If the variable is public, then the bad value might conceivably have come from anywhere in your project, so it can be more complex to reason about what's happening to it.

  • It makes the interface to this type simpler. When you're in another script that's trying to use this type, and you type its name or the name of an instance, auto-complete will give you a list of all the public members of the type. If the type is complicated, with lots of public members, then it can be harder to find the one you want - especially if you're unfamiliar with it. You might take a guess at which member you wanted and get it wrong, because there was a better way hiding in all that noise.

    By contrast, if we make private anything that external code shouldn't use directly, leaving just a few public members tailored to the intended use of this type, you make it very easy for even a naive coder to find the right members for what they want to do (say a junior team member, or even yourself in 6 months after you've forgotten how you coded this type to work). This helps the team work more efficiently, understand the system more clearly, and make fewer mistakes.

  • It lets you maintain "invariants". These are rules for what should always be true about the state of your game and its internal data, like "the items in the targets list are always sorted in order of priority" or "whenever the enemy is in 'attack' mode, its target variable is non-null". Having clear invariants makes your code easier to reason about, reduces bugs, and saves doing redundant checks to be sure you're working with valid state.

    If a field is public though, it's impossible to enforce an invariant that involves it. Other code could set it to some arbitrary value that violates the rules, allowing bugs to creep in.

  • It makes it easier to refactor code down the line. If later on, you decide that your platformer controller should expose inspector parameters for jumpHeight and timeToPeak, not jumpSpeed and jumpGravity, but those fields you're replacing were public, you might find code in other parts of your project is depending on them - so you have more code to alter to make this change, and again more chance to introduce bugs.

    By making these variables private from the start, you enforce some discipline about what's allowed to read from them, making it less likely that you create a tangled knot of dependencies that's tough to refactor later. Now the only references to these variables should be confined to one class, where it's much easier to make the change without introducing new bugs. If other scripts are reading them, they're doing so via getter methods, and you can just modify those methods to return a compatible value, even though the internal implementation has changed, and the consuming scripts are none the wiser.

    This discipline isn't free - it takes effort to maintain clean separations when adding extra dependencies would make for a quicker shortcut. So that is something to weigh here - considering the full lifecycle of this script and how long you need to be maintaining it / how much is likely to change in future - you could end up adding complexity to enforce this principle in a script that's ultimately thrown away without that effort paying off in future maintenance.

  • For the case of [SerializeField], it gives a strong hint to the human reader about how the field is meant to be used. If a variable is public, one might assume other scripts are meant to populate it. [SerializeField] (especially in the absence of [HideInInspector]) says "this is probably a designer tuning parameter set in the Inspector". When those hints are accurate, it's quicker for a reader to gain a clear understanding of the code.

So for this reason, hiding information by avoiding public where possible is generally considered a good practice in software engineering.

In contrast to the advice you've seen saying it's "inadvisable", I'd argue that changing public fields to [SerializeField] where possible is an improvement to encapsulation.

But it can also be a bit frustrating - particularly in early prototyping when everything is in flux. You might not be able to accurately predict what fields you or your team members will need access to from other scripts. If everything is private, coders will have to keep jumping into other files to make things public or add getter/setter methods just to get on with their work - something that can cause friction within the team. I alluded to this in the section on refactoring above - making a cleanly engineered interface is more work than just slapping public everywhere, even if the hope is it will pay off in faster development/fewer bugs in the long run. It's also a bit less conventional, and can confuse more junior coders who are used to just using public.

So, I wouldn't advise doing a global find-and-replace in your project or anything like that! But if you have variables that are purely for inspector configuration - especially if they're set once, and used only within the script itself, [SerializeFiled] is a great way to express that intention and reduce public clutter.

You can also use this auto-property variant, which still offers good encapsulation and defense of invariants, while making the field visible and read-only for other scripts to make decisions about:

[field:SerializeField]
public GameObject myObject { get; private set; }

This way the field appears in the Inspector and can be read by other scripts, but it can be written to only inside this script itself.

\$\endgroup\$
2
  • \$\begingroup\$ I can't second this enough. Exposing through properties is good coding practice but even more important in Unity. Separating public properties from private serialized fields makes the life so much easier at tracking external influences to current code. It is a bit more work and consideration but it pays to be able to separate design from code influences. \$\endgroup\$
    – Nikaas
    Aug 7 at 7:44
  • \$\begingroup\$ Thanks for the clarity you provided in this comment. Hugely appreciated. \$\endgroup\$ 2 days ago
0
\$\begingroup\$

In general there is no problem in that. But it could be. Problems could be local or at architecture level.

local if this GameObject is doing something critical and you just disable in the middle of something (its scripts, other sub Game Objects, physics, collision, etc.)

architecture If another part of the game is using that GameObject you want to disable, then you will most probably generate an error. So keep in mind to always check its status before using it

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .