Search Unity

Script access to Prefabs

Discussion in 'Prefabs' started by SteenLund, Sep 24, 2018.

  1. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    @hippocoder
    What kind of access are you looking for? What are your use case/user stories?
     
    hippocoder likes this.
  2. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I'd love script access for controlling the hierarchy - it could be we would like to indicate colours for prefabs, because (no offence intended to new users or for people with colour blind impairments) we don't have a problem with colour coding... and it would be fairly up to us then to choose how something presented instead of Unity telling us how the customisable editor should be presented.
    (Basically - the old behaviour, with some variation to colour - not vital but... these eyes can barely make out the icons properly but colour is fine).

    Another nice thing would be to be able to open prefabs for editing and close them for editing via script. Maybe it can be done - but I didn't see the immediate way to do this. This is part of my request in the other thread - to be able to quickly edit a whole bunch of them, so the user would just click the prefab and we can automatically click edit for them, and when they select another prefab we can open that for them too - quicker tweaking.
    (Like before)

    I guess I'm just a fan of how fast the workflow was before, and how simple, so my script access is me saying "I want to work how I did before and I'm willing to do the work to make that happen rather than ask Unity to do a lot of uncertain stuff".

    I don't want to interfere with Unity's plans - but after using prefabs (our main project is on 2018.3) in production, I have to say it's actually slower going for the simple use case even though we definitely will use advanced features when it makes sense in this project.
     
    Deeeds likes this.
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Off the top of my head:
    1: If we're doing a change to the way a script works in a way that requires changing the objects the script is attached to, we need to iterate all scenes and all prefabs in order to apply those changes.
    For scenes, that's easy; open scenes one-by-one, FindObjectsOfType, edit, save scenes.
    For prefabs (now), that's easy, AssetDataBase.FindAssets("t:prefab"), GetComponentsInChildren, edit, SetDirty.
    This gets much more complex when we can't treat prefabs as GameObjects.

    2: We might want to make utilities to edit prefabs. Say I'm making a game with a bunch of similar prefabs - possibly variants in the new system. If a lot of them have the same component, it might be useful for the designers if I make an isolation mode window for that component, so you can see and edit that component on all the prefabs at the same time.
    This is easy now; grab all the relevant prefabs, create an Editor targeting that component on each prefab, and draw those Editors one by one in a custom editor window. It's pretty much a five minute job.
    As far as I can understand, you can't do this with the new system, because you cannot edit several prefabs through script at the same time.

    So, it seems like you can edit prefabs through scripts. You're supposed to:
    - Call PrefabUtility.LoadPrefabContents
    - Edit the returned object
    - Call PrefabUtility.SavePrefabAsset
    - Call PrefabUtility.UnloadPrefabContents

    This works if you're editing a single prefab in a set way, so use case 1 still works. I'm worried about the performance of loading and unloading prefab contents into a dummy scene, though. The test runner works kinda in the same way, and that's incredibly slow if you have a large multi-scene setup open.
    As far as I can tell, use case 2 is impossible in the new setup, since we don't have access to prefabs as GameObjects outside of the prefab mode.


    I realize that I'm making a major assumption here - that calling PrefabUtility.LoadPrefabContents on several prefabs in succession. It might be that this is supported, in which case 2 would work. It'd still require opening the prefab scene, which for sure is a downside, but perhaps an acceptable one.



    Now, for a solution, it would be nice if we had a similar API to what the new PrefabUtility is offering, but without the whole prefab scene thing. Something similar to how asset importers work would be ideal:

    Code (csharp):
    1. PrefabImporter importer = (PrefabImporter) AssetImporter.GetAtPath(path);
    2. rootGameObject = importer.GetRootGO();
    3. // edit rootGameObject and stuff on it directly, or through an Editor, or through a SerializedObject.
    4. importer.SaveAndReimport();
    With that scaffolding in place, you could automate things and create prefab editing utilities in a much cleaner way. It'd not be as clean as just grabbing a prefab and editing it as a GameObject directly, but it's completely workable.
     
    hippocoder likes this.
  4. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    You can do something like this
    Code (CSharp):
    1.  
    2. public class PrefabEditor
    3. {
    4.     [MenuItem("Prefabs/Change Static")]
    5.     static void ChangeStatic()
    6.     {
    7.         var go = Selection.activeGameObject;
    8.         if (PrefabUtility.IsPartOfPrefabAsset(go))
    9.         {
    10.             var prefabPath = AssetDatabase.GetAssetPath(go);
    11.             var prefabRoot = PrefabUtility.LoadPrefabContents(prefabPath);
    12.  
    13.             GameObjectUtility.SetStaticEditorFlags(prefabRoot, StaticEditorFlags.LightmapStatic);
    14.  
    15.             PrefabUtility.SaveAsPrefabAsset(prefabRoot, prefabPath);
    16.             PrefabUtility.UnloadPrefabContents(prefabRoot);
    17.         }
    18.     }
    19. }
    20.  
    21.  
    to load a prefab, modify it (include rearranging the hierarchy) and write it back.

    Coming in a beta soon is the ability to multi select and apply. So if you really know what you are doing you can drag a bunch of prefabs into the scene. Modify each of them, multi select and do Apply All. Revert All also works with multi selection
     
    hippocoder likes this.
  5. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Thanks a lot - much much appreciated. I love the up coming multi edit feature, but please consider not having us drag it out into the scene - the old way of just quickly editing in project view is going to be sorely missed and it would probably shut everyone up. Just top-level in project with the button still there for deep editing....

    <3
     
    Deeeds likes this.
  6. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    @Baste

    LoadPrefabContents only support opening one prefabs at a time, but it is fast because we don't unload your scenes like the Test runner does
     
  7. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    If you don't need to rearrange the hierarchy you can find the root gameobject of the prefab asset using
    Code (csharp):
    1. AssetDatabase.LoadMainAssetAtPath("Assets/MyPrefab.prefab")
    then you can modify properties, but not delete nor insert nor rearrange stuff. Afterwards do
    Code (csharp):
    1. AssetDatabase.SaveAssets()
    .

    But please be careful :)
     
    NikolaNikolov and hippocoder like this.
  8. Deeeds

    Deeeds

    Joined:
    Mar 15, 2018
    Posts:
    739
    When that speed and fluidity of experimentation, creativity, discovery and expressiveness was part and parcel of what makes (made) Unity special, it's a tragic step backwards to compromise any of it to "improve" prefabs.

    Much more so when prefabs are the signature paradigm of Unity.

    I'd prefer they take their time and get it right, starting with consideration of creative flow. It's this design based usage of the prefab workflow which seems to have been least considered in adding to what can (and now can't) be done with and to prefabs.
     
  9. guavaman

    guavaman

    Joined:
    Nov 20, 2009
    Posts:
    5,632
    How does one trigger the editor to enter "Open Prefab" mode from a script? This new workflow now prevents the Rewired Editor from working on prefabs (the prescribed workflow) because the prefab must be "open" before it can be edited. Selecting a prefab and trying to edit it without explicitly opening it results in the object reference to that component becoming null the first time any data is changed in the prefab.

    This is a very common scenario: Have the Rewired Editor window already open, then select the Rewired Input Manager prefab you want to work on. This no longer works because it has to be opened first. I am going to have to add code to detect when a prefab is selected but not "open" and prevent them from editing anything and display an "Open" button that will open the prefab. And I need to have code that will automatically "open" the prefab in the editor when they click on the "Launch Rewired Editor" button, and probably some way to close it as well.

    And what happens if I don't explicitly save or unload the prefab? For example, if they leave the Rewired Editor window open, how am I to detect they're finished editing the prefab so I can close it? Or perhaps they just press Play immediately while still using the editor...
     
    Last edited: Jan 24, 2019
  10. Ben-BearFish

    Ben-BearFish

    Joined:
    Sep 6, 2011
    Posts:
    1,204
    Does anyone know if PrefabUtility.LoadPrefabContents() is allowed to be called in Unity Editor Play mode? Or does that cause weird behavior?