Search Unity

Proper way to update a prefab using OnValidate

Discussion in 'Prefabs' started by Bigpete591, Nov 22, 2018.

  1. Bigpete591

    Bigpete591

    Joined:
    May 7, 2013
    Posts:
    60
    I'm trying to update the value of a prefab using the OnValidate method in the edit prefab scene. This code successfully sets the field, but it throws an error and deselects the prefab (but the prefab edit scene remains).

    Code (CSharp):
    1.     void OnValidate()
    2.     {
    3.         AssignAssetID();
    4.     }
    5.  
    6.     void AssignAssetID()
    7.     {
    8.         string path = AssetDatabase.GetAssetPath(gameObject);
    9.  
    10.         if (!string.IsNullOrEmpty(path))
    11.         {
    12.             var original = PrefabUtility.GetCorrespondingObjectFromOriginalSource(this);
    13.             SerializedObject serializedObject = new SerializedObject(original);
    14.             var prop = serializedObject.FindProperty("assetId");
    15.             prop.stringValue = AssetDatabase.AssetPathToGUID(path);
    16.             serializedObject.ApplyModifiedPropertiesWithoutUndo();          
    17.         }
    18.     }
    Error:
    Code (CSharp):
    1. PPtr cast failed when dereferencing! Casting from Transform to GameObject!
    2. UnityEditor.EditorApplication:Internal_CallUpdateFunctions()
     
    Last edited: Nov 22, 2018
  2. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Can't tell anything from this; there's not sufficient info. If you believe it's a bug, file a bug report, thanks.
     
  3. Bigpete591

    Bigpete591

    Joined:
    May 7, 2013
    Posts:
    60
    Are those the proper methods for updating a field from inside the prefab edit view?
     
  4. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Well, if you're in Prefab Mode, a script on the Prefab contents that call
    PrefabUtility.GetCorrespondingObjectFromOriginalSource(this)
    well get null back since there is no corresponding object for Prefab content - except if you're inside a Prefab Variant, or the script is on a nested Prefab.

    If it's not null, it will return an object in a Prefab Asset. You're then trying to modify that object directly, which is not supported. If you want to modify a Prefab Asset, you should use
    PrefabUtility.LoadPrefabContents and then save it back after modifying (see the link for details).

    Since I don't know exactly what you're trying to achieve I can't tell you what the right thing to do is. If you just want to modify the assetId property on the script itself, you can create the SerializedObject from the script itself
    this
    and not on
    original
    .

    But apart from all that, OnValidate is a method on MonoBehavior, a runtime class, and your code is editor code that uses Editor API. It's going to fail if you try to make a build.
     
  5. rastlin

    rastlin

    Joined:
    Jun 5, 2017
    Posts:
    127
    I have the same problem as Bigpete.

    The new API is terrible. It works fine in the scene objects, but in Prefab Mode, which is supposedly an isolated scene, most of the prefab API fails?

    I try to get the asset path of a prefab currently being edited in Prefab Mode in OnValidate of my mono behaviour. Tried all the following:

    Code (CSharp):
    1.  
    2.                 var p1 = PrefabUtility.GetNearestPrefabInstanceRoot(gameObject);
    3.                 UnityEngine.Object parentObject = PrefabUtility.GetCorrespondingObjectFromSource(gameObject);
    4.                 var p2 = PrefabUtility.GetCorrespondingObjectFromOriginalSource(this);
    5.                 var path = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(gameObject);
    6.                 var p4 = PrefabUtility.GetOutermostPrefabInstanceRoot(gameObject);
    7.                 if (parentObject == null)
    8.                 {
    9.                     parentObject = PrefabUtility.GetPrefabInstanceHandle(gameObject);
    10.                 }
    11.                 var asPath = AssetDatabase.GetAssetPath(gameObject);
    Everything returns null or empty string.

    When someone updates properties on my component in Prefab Model using standard editor, I need to be able to update some additional fields. In the past this worked without any issues even when editing the asset outside of a scene (using OnValidate). Now the new API is just not usable in this scenario.
     
    acidock likes this.