5
votes

I have a gameobject as instance prefab in my scene. I have added a component that have a monobehaviour with a validation logic in OnValidate method. But I've noticed that the OnValidate method is also called when I'm in prefab mode.

So I want that some variables of an instance prefab are validated only when there are in my scene in editor mode because their values depends on other objects in the scene.

So I want to know how to avoid the OnValidate method from being called in prefab mode. Whether it is nested or not.

So I tried to write my method taking reference from here: https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStageUtility.cs but it failed when the prefab is nested in another prefab.

class Helper
{
    //It doesn't work. It failed when the prefab is nested in another prefab.
    public static bool PrefabModeIsActive(Gameobject object)
    {
        UnityEditor.Experimental.SceneManagement.PrefabStage prefabStage = UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetPrefabStage (gameObject);
        if(prefabStage != null)
            return true;
        if(UnityEditor.EditorUtility.IsPersistent(gameObject))
        {
            return true;
        }

        return false;
    }
}

MyMonobehaviour

class MyMonobehaviour : MonoBehaviour
{
    public MyScriptableObject entity;

# IF UNITY_EDITOR
    void OnValidate()
    {
        //So I expected that logic onvalidate is called only on instance prefab in my scene.
        if(!Helper.PrefabModeIsActive(gameObject) && entity == null)
        {
            entity = Resources.Load<MyScriptableObject>("MyAssetScene/" + SceneManager.GetActiveScene().name) as MyScriptableObject;
        }
    }
#endif
}

So I expect that onvalidate logic is called only on instance prefab on my scene.

UPDATE 1

A alternative solution that seems to work is check some values on scene in within the gameobject:

bool PrefabModeIsActive(Gameobject gameobject)
{
    return String.IsNullOrEmpty(gameObject.scene.path)
    && !String.IsNullOrEmpty(gameObject.scene.name);
}

but I'm not sure if if there are some case studies where this might not be true

2

2 Answers

1
votes

Here are a couple lines that I use in all my projects to avoid this annoying feature.

#if UNITY_EDITOR
using UnityEditor.Experimental.SceneManagement;
using UnityEditor;
#endif

void OnValidate()
    {
#if UNITY_EDITOR
        PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
        bool isValidPrefabStage = prefabStage != null && prefabStage.stageHandle.IsValid();
        bool prefabConnected = PrefabUtility.GetPrefabInstanceStatus(this.gameObject) == PrefabInstanceStatus.Connected;
        if (!isValidPrefabStage && prefabConnected)
        {
            //Variables you only want checked when in a Scene
        }
#endif
        //variables you want checked all the time (even in prefab mode)
   }

This does two things. One, it won't check for variables when in prefab mode AND it also won't check for variables before the prefab modes are valid. The second benefit you won't see happen a lot but sometimes when you open a new project, or delete your library folder and need to re-import assets you will trigger OnValidate() before preab are valid and even putting just the "isPrefabConnected" won't work.

0
votes

You can try using PrefabStageUtility's GetCurrentPrefabStage method.

Ex.

    bool PrefabModeIsActive()
    {
        return UnityEditor.Experimental.SceneManagement.
            PrefabStageUtility.GetCurrentPrefabStage() != null;
    }