Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Issues with EditorApplication.update in OnValidate on prefabs when in stage. Solution inside.

Discussion in 'Prefabs' started by merkaba48, Jan 31, 2019.

  1. merkaba48

    merkaba48

    Joined:
    Dec 17, 2013
    Posts:
    6
    I'm just sharing a problem I had and my solution.

    I have an object with some editor-only script behaviours that are triggered in `OnValidate`. Because I use `SetActive()` and `DestroyImmediate()`, I can't use them directly in `OnValidate` as it spams warnings in the console, so instead I register a callback to `EditorApplication.update` then unregister the callback once it's handled.

    This works fine in the scene mode, but in the prefab staging mode I can get a lot of null exception errors as it's looking for things that don't exist...

    This was surprising as I didn't see why it would find a null reference to, say, a gameobject also in the prefab explicitly referenced by a public property on the script. It's there, it's in the stage.

    After some debug printing I ascertained that the problem was caused by OnValidate being called on a prefab not in the stage view, perhaps in the project hierarchy, or as a result of some behind-the-scenes work Unity does when in stage mode.

    Anyway, to cut a long story short, the solution was to use `PrefabStageUtility` in `OnValidate` to check a) the editor was in stage mode, and b) the gameobject `OnValidate` was running on was in that stage. If (a) was false, all was good, but if (a) was true and (b) was false, `OnValidate` was being called on the unseen prefab...so I could quickly return out of the validation and prevent errors.

    I would like to know why this problem occurs in the first place, but for now this is my solution (and I welcome any suggestions for better solutions):-

    Code (CSharp):
    1. void OnValidate()
    2. {
    3.     if (PrefabStageUtility.GetPrefabStage(gameObject) != PrefabStageUtility.GetCurrentStage())
    4.     {
    5.         return;
    6.     }
    7.     // We are in the stage view and this is the object being edited
    8.     EditorApplication.update += MyFunc;
    9. }
    (edit)

    In hindsight it might be enough to use
    if (PrefabStageUtility.GetCurrentStage() != PrefabStageUtility.GetPrefabStage(gameObject)
     
    Last edited: Jan 31, 2019
    bddckr likes this.
  2. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    676
    Had a similar problem. I like your approach, but the PrefabStageUtility class is experimental, so we can't be sure it will last into the future.

    What I did was, wherever I would have called SetActive(false), I set transform.localScale = Vector3.zero instead (and wherever I would have called SetActive(true), I set transform.localScale back to its actual size). At run time, in the Awake method, I call SetActive(false) on anything where transform.localScale == Vector3.zero. You could set a flag instead, if you really want anything with zero scale to remain active.