Search Unity

How do I edit prefabs from scripts?

Discussion in 'Prefabs' started by Baste, May 28, 2019.

  1. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    This API is a disaster:

    Code (csharp):
    1. var prefabAsset = Selection.activeObject;
    2.  
    3. var root = PrefabUtility.LoadPrefabContents(AssetDatabase.GetAssetPath(prefabAsset));
    4. foreach (var comp in root.GetComponentsInChildren<SomeScript>(true)) {
    5.     Debug.Log("Destroying on " + comp.gameObject.name);
    6.     Object.DestroyImmediate(comp);
    7. }
    8.  
    9. // How do I get the thing to save?
    10. // This doesn't do anything
    11. EditorUtility.SetDirty(root);
    12. EditorSceneManager.MarkSceneDirty(root.scene);
    13.  
    14. // This throws ArgumentException: Can't save a Prefab instance
    15. PrefabUtility.SavePrefabAsset(root);
    16.  
    17. // This nullrefs; the root object doesn't have a prefab stage:
    18. EditorSceneManager.MarkSceneDirty(PrefabStageUtility.GetPrefabStage(root).scene);
    19.  
    20. PrefabUtility.UnloadPrefabContents(root);
    There's a million methods, they all have mile long names, and none of them seems to do what their name implies, and they don't work.


    Again, the API is a disaster:
    - It's very concerned with somewhat opaque internals.
    - Methods are documented for Unity employees, not external users. Here's SavePrefabAsset: "Use this function to write a Prefab from the Library folder back to the Assets folder as a source asset.". I have no clue what you mean by that. I've worked with the engine for years.
    - Very simple tasks - like editing a prefab from script - doesn't have an obvious way to achieve them.
    - Checking if something is a prefab or not is the least intuitive thing in the world, because if you open a prefab in the prefab stage, it's "not a prefab". This is the definition of leaky abstractions.

    The things the new prefab system does with nesting and variants are great. But the API is really bad. I can't get anything done with it before I become too frustrated and give up. It seems easier to write code that parses the yaml file and edits it than to do this the proper way.
     
  2. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,442
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    The solution is pretty straight forwards - mark the correct gameobjects dirty. But there's so much Prefab crud to wade through that I can't see that, because I'm wondering which of a hundred methods I got wrong.
     
    hippocoder, NeatWolf and halley like this.
  4. Mads-Nyholm

    Mads-Nyholm

    Unity Technologies

    Joined:
    Aug 19, 2013
    Posts:
    219
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    It helps, but note the general criticism of this thread. There's two method that are named almost exactly the same thing, that does very different things. Even if I read those exact docs, I'm going to remember that there's a method named something with "save", "prefab" and "asset" in it.

    It's also not something I'm going to remember, so every time I'm writing code against this api I'm going to have to remember if I'm "saving a prefab" or if I'm "saving as a prefab".

    There's in general a huge problem where the API is super-concerned about what object we have - is it a prefab instance, is it a prefab, is it a nested prefab, etc - but all of those things are just typed as GameObjects. So our code don't have any type-strong way of handling any of this.


    If you want the new prefabs to have a good API, you'll need to stop referencing the docs and internal abstractions and instead think about what's something that the receiving users can understand.
    A good start would be to introduce a Prefab type that wraps prefab assets and allow us to be very clear about what we're working with.
     
  6. greengremline

    greengremline

    Joined:
    Sep 16, 2015
    Posts:
    183
    Yeah this is really bad, if you're forcing users to remember this kind of thing its a sign your API isn't very clear. A good API should be as clear as possible so that users of it don't have to constantly do mental gymnastics each time they want to do something, aka "reduce the cognitive load"
     
    Last edited: May 29, 2019
  7. josh_rake

    josh_rake

    Joined:
    Jan 4, 2017
    Posts:
    15
    The solution only works partially.
    If you modify properties on the root of the prefab, it works.
    But not if you touch children.

    Any way to make it work?
    I agree with the above, I've wasted hours on such a simple thing. Would have been faster to write some yaml edition code.
     
    learner_CL likes this.
  8. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    We do note it.

    But changing API that is shipped is not something we take lightly. The feedback would have been timed very well in the 6 months period where the new Prefabs functionality was publicly available to test prior to release. Getting this feedback only after release make it a lot more difficult for us to be able to address it. Not that it's impossible, but it has much higher overhead both for ourselves and users, which realistically decreases the cost-benefit of this issue compared to other things we could spend our resources on improving.
     
    not_a_valid_username likes this.
  9. toastertub

    toastertub

    Joined:
    Oct 28, 2013
    Posts:
    25
    "Baste, May 28, 2019"
     
  10. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    The improved Prefabs functionality, including the APIs in question, were released in Unity 2018.3 on December 13, 2018.
     
  11. OlegPavlov

    OlegPavlov

    Joined:
    Jun 22, 2014
    Posts:
    13
    If the API is such a mess, at least give us code snippets explaining how to use it.
     
  12. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    The API follows a more or less consistent logic - in that sense it isn't a mess, but we recognize it can be confusing.

    There's many different ways to use the API to achieve different things. Each method is like a building block. So we can't provide example snippets for every thing you can do with it. However, we do have some code snippets, like the one here Mads linked to originally:
    https://docs.unity3d.com/ScriptReference/PrefabUtility.LoadPrefabContents.html

    There's also various examples here:
    https://github.com/Unity-Technologies/PrefabAPIExamples
     
    codestage and OlegPavlov like this.
  13. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    I found that the boilerplate becomes much more manageable with a custom-made scope. The example code from LoadPrefabContents gets reduced down to:

    Code (csharp):
    1. [MenuItem("Examples/Add BoxCollider to Prefab Asset")]
    2. static void AddBoxColliderToPrefab()
    3. {
    4.     // Get the Prefab Asset root GameObject and its asset path.
    5.     GameObject assetRoot = Selection.activeObject as GameObject;
    6.     string assetPath = AssetDatabase.GetAssetPath(assetRoot);
    7.  
    8.     // Modify prefab contents and save it back to the Prefab Asset
    9.     using (var editScope = new EditPrefabAssetScope(assetPath))
    10.     {
    11.         editScope.prefabRoot.AddComponent<BoxCollider>();
    12.     }
    13. }
    Prefab Scope code:
    Code (csharp):
    1. public class EditPrefabAssetScope : IDisposable {
    2.  
    3.     public readonly string assetPath;
    4.     public readonly GameObject prefabRoot;
    5.  
    6.     public EditPrefabAssetScope(string assetPath) {
    7.         this.assetPath = assetPath;
    8.         prefabRoot = PrefabUtility.LoadPrefabContents(assetPath);
    9.     }
    10.  
    11.     public void Dispose() {
    12.         PrefabUtility.SaveAsPrefabAsset(prefabRoot, assetPath);
    13.         PrefabUtility.UnloadPrefabContents(prefabRoot);
    14.     }
    15. }
     
    _geo__, q8sr, YegorStepanov and 34 others like this.
  14. Mads-Nyholm

    Mads-Nyholm

    Unity Technologies

    Joined:
    Aug 19, 2013
    Posts:
    219
    Hi Baste, this is a fine wrapper for the LoadPrefabContents, SaveAsPrefabAsset and UnloadPrefabContents. We will consider adding it to our API.

    UPDATE: This was added in 2020.1 as:
    PrefabUtility.EditPrefabContentsScope
    .
    See also: https://docs.unity3d.com/ScriptReference/PrefabUtility.EditPrefabContentsScope.html
     
    Last edited: May 27, 2021
  15. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Feel free to do that!
     
  16. k76

    k76

    Joined:
    Oct 12, 2014
    Posts:
    12
    This is exactly what I was looking for and worked like a charm. Thank you for making something to cut through the chaos of AssetDatabase and PrefabUtility.
     
  17. Recon03

    Recon03

    Joined:
    Aug 5, 2013
    Posts:
    845

    I been here since the early days of Unity..this sadly has always been an issue, when they introduce new things, they want us to tell them right away, well, Unity needs also to understand many of us, are actually working on releasing games, and we don't always see these issues, until we use them, so we don't have countless hours testing every thing..... I wish we did....if Unity made games like Unreal, we wouldn't have as many issues like this, which I find to be obvious.....and agree with this OP 100%...

    ..( and yes I use Unreal for 15 years... So, Unity, I believe its smart to listen to these users, as its something many have said for years, its not new.... so I agree with this user and the OP....


    The API has always been bad, and I spent a lot of my time helping new users with code snippets, something I do, because I enjoy helping, but Unity should be doing this.....NOT the users...So, it wouldn't take long to add some of these snippets...

    PS: Not saying this to bash Unity, but to hopefully improve....because I find you guys have went in a better direction in the last few years....but this would help a lot .

    Thanks!
     
  18. dvalles

    dvalles

    Joined:
    Nov 15, 2014
    Posts:
    11
    @Baste Epic wrapper Baste. You're a saint and a scholar. I gotta start using IDisposable.
     
  19. Alshan

    Alshan

    Joined:
    Apr 1, 2017
    Posts:
    2
    Agreed! What an elegant solution.
     
  20. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    With @Baste 's permission the wrapper has been included in 2020.1 as part of PrefabUtility :)
     
  21. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,363
  22. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,073
    As part of description says, and as i understand it ;) .
    It basically saves the GameObject prefab that you loaded into memory (usually to make some changes) into file on disk - *.prefab kind of file.
     
  23. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    I've been using an approach like this in an Editor script to modify prefabs. The example below loads a prefab, looks for a UI Text named "Test", changes the text to "changed", flags the prefab as changed and saves it.

    GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/prefabs/whatever.prefab");
    prefab.transform.Find("Test").GetComponent<Text>().text = "changed";
    EditorUtility.SetDirty(prefab);
    AssetDatabase.SaveAssets();
     
  24. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,363
    in memory, like when you press open prefab?
     
  25. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    @laurentlavigne

    Yeah, I can easily see how this particular API is confusing and hard to understand for someone not intimately familiar with the implementation details of how Unity imports, loads and saves assets.

    This API should really not be used except for a very specific case. Which means in 99% of the use cases users are probably not going to need this.

    With the introduction of nested prefabs, prefabs are now treated differently as it is actually processed during import and the result is written to the Library folder. This means the asset in the Assets folder it considered a source asset and no longer directly modifiable. Using AssetDatabase APIs to load the prefabs actually loads the data from the Library folder now which is completely separate from the file in the Assets folder and any changes made to the objects loaded via AssetDatabase API are not automatically written back to the source asset.

    Here is the use case:

    Code (CSharp):
    1. var myPrefab = (GameObject)AssetDatabase.LoadMainAssetAtPath("Assets/myprefab.prefab");  // Loads the imported prefab asset into memory (not the source, even though you specified the path to the source asset)
    2.  
    3. // Modify a component on the loaded assets. This is again on the imported version of prefab and not actually on the source asset
    4. var comp = myPrefab.GetComponent<MyComponent>();
    5. comp.myProperty = "Foo";
    6. EditorUtility.SetDirty(comp);
    7.  
    8. // Write the modified version of the imported assets back to the source assets and reimport the source asset
    9. PrefabUtility.SavePrefabAsset(myPrefab);
    10.  
    11. // Alternatively use
    12. // AssetDatabase.SaveAssets();
    13. // Which will detect that the imported version of the prefab
    14. // has been marked dirty and will write it back to the source asset
    This is similar to how you would modify prefabs on disk before nested prefabs were introduced, but it is highly recommended to use
    PrefabUtility.LoadPrefabContents
    instead as this is a much safer way to edit prefabs.
     
    Last edited: Jun 30, 2020
    adamgolden likes this.
  26. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,363
    It looks awfully similar to SaveAsPrefabAsset. What's the difference?

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEditor;
    3. using UnityEditor.SceneManagement;
    4. using UnityEngine;
    5.  
    6. public class RemoveMissingMonoFromSelectedPrefabs : MonoBehaviour
    7. {
    8.     [MenuItem("Tools/Remove Missing Monos From Selected Prefabs")]
    9.     public static void CleanupSelectedPrefabs()
    10.     {
    11.         foreach (var s in Selection.gameObjects)
    12.         {
    13.             var assetPath = AssetDatabase.GetAssetPath(s);
    14.             var go = PrefabUtility.LoadPrefabContents(assetPath);
    15.             GameObjectUtility.RemoveMonoBehavioursWithMissingScript(go);
    16.             PrefabUtility.SaveAsPrefabAsset(go, assetPath);
    17.             PrefabUtility.UnloadPrefabContents(go);
    18.         }
    19.     }
    20. }
     
    KnightWhoSaysNi likes this.
  27. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    @laurentlavigne

    First of all your script is the recommended way to work directly with Prefab source files, stick to that and forget all about
    SavePrefabAsset
    :)

    The difference is that your script loads the source asset, modifies and writes back to the source asset. Your approach is very safe and actually allows for more than just changing properties as you are able to delete objects and reorder the hierarchy (unless it is a Variant Prefab.)

    My script reads the processed asset from the Library folder, modifies and writes the changes back to the source asset. Because it deals with the processed prefab there are a lot more restrictions but also a very big risk the data gets out of sync. By out of sync I mean you might change some property and you see this change in the scene view but when opening the prefab in prefab mode the change is not there, until you actually save your changes.

    I guess it is getting a little complicated. I would say as a rule of thumb, if you are using
    SavePrefabAsset
    you are doing it "wrong" and you should reconsider your approach.
     
    laurentlavigne likes this.
  28. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,363
    Ok I can't imagine a scenario where anyone would want to poke at Library if it's risky. Speed?

    Anyway, something I do understand is human error. I made the mistake of using that one before I saw it didn't work, I'm not the only one. They both sound the same and show up in intellisense.
    I urge you to add the warning in the documentation AND intellisense. I'd rename this one to something more arcane and start deprecation so when we type "savep" in intellij we get the safe one.
     
    a436t4ataf likes this.
  29. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
  30. nsxdavid

    nsxdavid

    Joined:
    Apr 6, 2009
    Posts:
    476
    Confirming that @Baste wrapper is in PrefabUtility in 2020.1. However they named it PrefabUtility.EditPrefabContentsScope() But otherwise works like the aforementioned wrapper.

    @SteenLund note that the PrefabUtility documentation doesn't mention this new nugget tho.
     
    Recon03, idbrii and alex_roboto like this.
  31. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    k76 likes this.
  32. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    Turns out you have to use the navigation bar on the left to find it. We will try to improve this.
    Screen Shot 2020-08-18 at 09.23.34.png
     
    a436t4ataf likes this.
  33. nsxdavid

    nsxdavid

    Joined:
    Apr 6, 2009
    Posts:
    476
    It does work like a charm tho!

    It would be nice if you include a good example, and at the top of PrefabUtility really call this thing out as the way to do prefab edits from script.
     
    a436t4ataf likes this.
  34. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    I know this isn't you in particular, you're just offering explanations of a general problem in Unity's tech stack. But to continue the conversation:

    In all fairness: there were showstopper bugs in nested prefabs until more than a year after it shipped, no?

    I know in my case: we literally couldn't test them, there was no way to run them on our projects. We had to avoid them in order to keep the projects stable!

    So I only started seriously working with these API's in 2020. I honestly don't know how you expected feedback on an API that wasn't usable: since there was no way for us to use it in a stable version of Unity (Unity policy: no backporting of stuff like this into stable Unity versions), and the unstable version kept corrupting projects (from forum threads: some people getting daily corruption of Unity scene files :( ).

    A 6-month cut-off date for feedback on API design doesn't work if the APIs are unusable for a year. Going forwards Unity needs a sane policy here, not a Catch-22 one.

    Perhaps: release backports of API's that have limited functionality but enable people to try out the API on a stable Unity platform.

    Or: do what many other companies do ... treat API design as something important, not something optional - it's not unusual for companies to require public API's to be signed-off by the most senior engineers or dedicated system architects, even product managers, (and for them to be held accountable if the shipped API's haven't been well-designed and thought through).

    (I would hope that the packages-based approach would cure this a bit - but there's so much chaos in how Unity is currently struggling to manage their own packages that it feels unlikely we can assume anything there)
     
    Walter_Hulsebos likes this.
  35. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    For completeness in this thread (and since the docs are still unintelligible / largely missing) - what's the correct way to deal with a Prefab that's already open in the stage? (e.g. user has opened it, so that it's what appears in the Hierarchy view rather than the scene).

    It seems we can treat it like a Scene (mark dirty?) - but should we be usign PrefabUtility methods instead for safeness?

    NB: I know that the original Scene *is still open* even though the Editor pretends it's closed, so there's some danger here - some Unity API methods don't work correctly because of this (last time I checked: undocumented, although I did submit a request to have that changed so maybe fixed by now) but maybe it's correct?
     
  36. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    From trial and error:

    Yes, prefabs are scenes.

    But no, the scene API has not been updated to support prefabs-that-are-scenes.

    So no, using the APIs still silently fails.

    But doing this works:

    Code (CSharp):
    1. EditorSceneManager.MarkSceneDirty( PrefabStageUtility.GetCurrentPrefabStage().scene );
    My code is littered with comments "This API call was broken by Unity sometime in 2018 and is still broken in 2020. They haven't documented the breaking changes. Results determined entirely by trial-and-error. If you edit this code, be careful".
     
    SI-Shawn likes this.
  37. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    What is or isn't a showstopper depends on people's needs. There were issues that were showstoppers to some people, sure.

    Unfortunately the situation is kind of inherently a Catch-22. A stable feature means stable APIs that don't keep changing. So we can't get feedback about APIs only after the feature is stabilized and use that to change the APIs. Feedback that is used to shape the final stable feature has to come before this stabilization has happened, in its nature.

    We can't backport an API without backporting the feature the API is for, and we don't backport features.

    It was. Please don't make assumptions about our internal processes or the people who are part of them. Feel free to criticise features, but don't make this personal; that is not ok.
     
  38. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Thanks - sorry, I wrote that last bit badly. In my head I was thinking about my personal past experiences at companies with adding/removing these kinds of checks. I didn't realise at the time how much it would come across as directed at Unity staff - I have no idea what the internal process is at Unity.
     
    runevision likes this.
  39. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Of course. But if the ongoing approach to API bugs is "if you don't tell us the problem in the first 6 months - when many of you can't even use it - then we won't accept the bugs" (simplification, but that appears to be the general policy in my experience?) then it's not really working.

    Do you believe the shift to packages is changing / will change this much? My udnerstanding was that things like this are one of the things a package-centric development process would greatly improve (because the packages can float across versions, allowing more testing forwards/backwards).

    The main package I'm working with at the moment is doing approx one release a year, in preview (I expected a release every 1-2 months, since it's in preview - but they haven't chosen that route), so while people outside Unity have provided lots of API feedback (and I personally have a lot of bugs that have been formally accepted by the Unity team, which is good) we have no idea what changes have been made (last release was 8 months ago) and so we have had no chance to comment on the post-fixes API design. From your description, and assuming other teams (like that one) are using the same approach, we'll have 6 months to get API bugs in after they release - but if there's no more releases during that 6 month period then I imagine a lot of API bugs will get through (i.e. anything that's a fix to a previous issue won't appear until the 6 month window is finished, in which case: no chance to see or review it).
     
  40. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    I'm not the best person to comment on this. Prefabs functionality won't be going to a package approach any time soon due to being tightly integrated into the C++ core of Unity.
     
  41. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    If Unity's attitude is that bad API's cannot be fixed once shipped, then you have to start specifically ask us to look at the API during the preview process. Like "hey, here's what the API looks like now, do you have any feedback?". That never happened, as far as I can remember, and when all of the press is about the editor features, those are what we test.

    I don't know what your internal processes are, but whatever they are they failed at making an API that's comfortable to work with. If I need to do something with prefabs from script now, I never feel like achieving it was easy, even when it's essentially just replicating a few clicks in the editor. And that's a shame! Nested prefabs is a net positive, but editor scripting for sure got worse as a part of the deal, and that wasn't necessary.


    If you're interested, I could suggest how to make it better, but it seems like the attitude is that the API is set in stone since it's shipped?
     
    learner_CL, Tortuap and a436t4ataf like this.
  42. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    Bad APIs can't be fixed, but new ones can be introduced and the bad ones made obsolete and potentially removed at some point.

    We are always interested in feedback about a feature's UX, UI and APIs
     
  43. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Sure, maybe to us, we've been taking for granted that testing a feature means the API too, but that it may not be interpreted that way by others. We can try to be more explicit about it.

    I mean, almost. In reality it's about weighting the pros and cons. And there are a lot of cons of changing an API that people are already using and relying on. Asset Store developers in particular are dealing a lot with these kinds of issues because they often need to support multiple versions at once. Packages is a similar kind of deal. We (at Unity in general) have gotten a lot of feedback about how it's often nearly impossible to keep up. And that's when doing it the "soft" way Steen talks about, where old APIs are made obsolete.

    Certain types of API changes can be handled by the API upgrader, but only specific kinds. And it still adds more compilation time for the user whenever the upgrader needs to run and upgrade scripts.

    So all in all, the pros of changing an API have to be very big for it to be worth the pain it also causes. But you're always welcome to share your feedback.
     
  44. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    On the asset-publisher side ... when an API is in a poor state *and* it's poorly documented (as is the case here: more that the old APIs that it invalidates haven't been updated to reflect the fact they no longer work correctly (I'm thinking of mark-dirty, scene-stuff, etc) not because their own code changed but because extra situations have been introduced that they haven't been upgraded to cover) *and* it's relatively new, then ... there's basically no downside to nuking it from our asset-store code and replacing it: in practice, the few of us using it in assets are running-scared already: we don't know whether we\'re using the API correclty, it makes no sense, our code is very hard to read and maintain (and we tend to accidentally break it during other updates).
     
    Aka_ToolBuddy likes this.
  45. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Also, to be clear (Because the above makes it sound like I hate the new APIs): the implementation of the new APIs is great and I like it: when I find the magic combination of obscure methods and classes that does what I need it to do, it's always satisfyingly few lines of code (often writable as a 1-liner, or verbosely in 3 lines). This is great!

    My issues have been:
    1. The choice of symbols (names + locations in the class hierarchy - with lots of confusing overlap between near-identically-named method calls in different classes)
    2. The documentation (since the names above are confusing, we're extra reliant on the docs, but the docs have been scattered: the info often isn't in the places it needs to be, it's in a different place that you have to "get lucky" and find - or its simply not there at al)
    3. Commonly used bits of legacy APIs (cf the OP!) were broken by adding these features and ... there's nothing to warn us of that. Trial and error: code that worked perfectly in Unity 4.x onwards suddenly stops working (because the concept of "scene" and "Gameobject" have been expanded and refined).

    The last one is particularly frustrating because it feels like Unity is saying "We don't want to cause too much change by continually modifying new APIs, we want legacy code to continue working ... BUT we're going to contradict ourselves by breaking our old APIs at the same time so that legacy code also stops working" (where the two instances of "legacy" there refer to different ages of code, so you could construct a rather lawyer-like argument that its fair.

    But in practice: e.g. someone who hasn't started using nested prefabs yet would quite reasonably write code in Unity 2019 that *should* work using classic APIs and will find it *does not* work, with nothing in the docs explaining "Oh, yeah, we broke these APIs, they no longer do what they conceptually used to, please use the new APIs, this API is now semi-obsolete (it only works in a subset of conditions AND you must manually check for those conditions BUT you cannot do it using this API, only the new API will give you the data to let you know if the old one is usable or not").
     
  46. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    The spam bots are getting more advanced, I see :D
     
  47. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    I think the reason why the Prefab API ends up looking like it does is because we interact with the Prefabs as if they were GameObjects. So instead of just having a Prefab type that can have properties we can check, we have to pass a reference to a GameObject somewhere and ask "yo, is this a prefab, and if it is, what kind?".

    That's not very C#! It's not very... anything. I can't think of any API's on top of my head that's build around passing raw references to something to ask for information about that reference.

    This wasn't so bad when prefabs was simpler, but by now it's becoming pretty bad. So I guess what went wrong was that the new API just kept on trucking in the same way as the old one, and inherited all of the ways the old one was already bad. We just didn't really notice that much back then, as we didn't really do anything very complex.

    This would all be much better if we just had the type Prefab. At runtime, it would contain a reference to the root of the prefab. At edit-time, there would be extension methods living in UnityEditor that gave access to all the information that we need.

    That would allow us to write runtime code like this:

    Code (csharp):
    1. public Prefab enemyPrefab;
    2.  
    3. void Start() {
    4.     Instantiate(enemyPrefab.root, spawnPoint, Quaternion.identiy);
    5. }
    And edit time code like this:

    Code (csharp):
    1. Prefab prefab = PrefabUtility.GetPrefabAtPath(path);
    2. Debug.Log("is variant: " + prefab.IsVariant());
    3.  
    4. GameObject[] instancesInScene = prefab.GetInstancesInScene(SceneManager.GetActiveScene());
    5. Debug.Log("there are " + instancesInScene.Length + " instances in the active scene");
    6.  
    7. while (prefab.IsVariant()) {
    8.     prefab = prefab.RootPrefab();
    9.     Debug.Log("prefab root lives at path " + prefab.GetPath());
    10. }
    11.  
    12. using (var editScope = Prefab.OpenForEdit()) {
    13.     var root = editScope.root;
    14.  
    15.     if (root.GetComponent<BoxCollider2D>() != null)
    16.         root.AddComponent<BoxCollider2d>();
    17. }
    Doesn't that just look so much more comfortable?

    It's probably not that much work - the Prefab wrapper would just call into the current PrefabUtility API to figure out how things work. We could make this ourselves.


    I mean, we obviously check out the API when the preview is for a feature where the main interaction mode or one of the main interaction modes is code - DOTS or the new localization system or whatever. Prefabs' main interaction mode is using the mouse in the editor windows, which is why at least I didn't at all think about looking at it before it was well shipped.

    Totally unrelated, but about that "potentially removed", can I please get to name field "rigidbody" or "collider" or "light" any time soon? I believe that the last time those didn't cause an error was in 2017, but the names are still there. Or will they stay there forever to have the API auto-updater work?
    And if you don't know, who do we complain to? :p
     
  48. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    (This post was written before Baste's message above.)

    Let's take this thread in a more concrete direction instead of the very general overall musings that are not very actionable.

    This thread is specifically about editing Prefab Assets. Baste made a suggestion for a better API. This was adopted into the official API. Documentation probably still need to be improved to make it more discoverable, but as for the API itself, it seems like the issue in this thread was actually addressed. So let's move on to the next concrete issue (in a separate thread) and see whether that's viable to address as well when weighing pros and cons.
     
  49. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    This is a much broader thing than the API itself. This relates to the architecture of the feature in general, which is to a large degree informed by how it's been for the past decade. If we had had the freedom to start things over from scratch in 2018.3, there's much we would have done differently, but we were not developing an all new feature; we were expanding functionality of an existing one.

    It looks like it's largely the same methods as there are currently available, just on a Prefab object instead of in a utility class. Whether it's better or not I guess is a matter of taste. Unity's API is traditionally based a lot around utility classes; it's just a choice that was made at one point.

    I'm curious where this wrapper would live. Would you store separate Assets for all these wrappers? And what about for Prefab instances? Or would you just create them inline on the fly based on a GameObject, when needed? And in that case, doesn't it end up being more verbose than just using utility methods on GameObjects to begin with? I'm curious because I'm not quite following. if you do try out making this wrapper yourself (and using it in an actual tool where it provides a benefit), I'm certainly interested in following the development.
     
  50. Tortuap

    Tortuap

    Joined:
    Dec 4, 2013
    Posts:
    137
    Maybe a concrete direction could be, as Baste started to do, to provide a simpler API for simple tasks (ie EditPrefabAssetScope).

    It could be externally, in a package or whatever, so that the core API is not changed and that a clear separation is made between the complicated core API, that confuses many people, and a simplified layer.

    What do you think ?
     
    ijmmai and Walter_Hulsebos like this.