Experiencing odd behaviour with PrefabUtility.ApplyPrefabInstance()

Hello,

I’m currently trying to create prefabs via script but am running into some unexpected behaviour with PrefabUtility.ApplyPrefabInstance(). I am using the latest LTS version, Unity 2019.4.4.

I currently have a GameObject in a scene that I am trying to convert into a prefab asset in our project via script. That’s no problem, except what we also need to do is remove some unneeded components from the GameObject in the scene before we convert it into a prefab asset. The scene GameObject has some nested prefabs on it, so unfortunately I cannot just create an instance of the object and turn that into a prefab, otherwise all the nested prefab links are lost. And I cannot remove the components from the GameObject and create a prefab from that because we only want them removed on the prefab. So instead, in order to make changes to the prefab asset and retain its nested prefab links, we’re doing the following:

  1. Creating a prefab asset from the scene GameObject using PrefabUtility.SaveAsPrefabAsset().
  2. Creating an instance of the prefab asset using PrefabUtility.InstantiatePrefab().
  3. Removing the undesired components from the newly created prefab instance.
  4. Calling PrefabUtility.ApplyPrefabInstance() to the prefab instance, so that those changes are applied to the original prefab asset.
  5. Deleting the prefab instance.

Unfortunately this does not work. Doing this results in some very strange behaviour, with only some of the components being properly removed, and the rest being unchanged. If I remove the ApplyPrefabInstance() call and look at the prefab instance, I can see that all undesired components are removed, so I know I am removing the components correctly.

Am I doing something wrong? Is there a better approach to this?

Any help is much appreciated.

Kenny

It’s hard to know what’s going wrong based on this information. What you’re doing sounds sensible (it might be possible to simplify, but let’s focus on getting it to work for now). If you look at the Prefab instance with the components removed and try to manually apply it with the Apply button, but that work or not?

Thanks for the help.

Manually applying the removed components via the editor does work. It’s only the script that seems to have problems.

We did discover that rather than destroying the component on an instance of the prefab asset and calling ApplyPrefabInstance() to apply it to the root prefab asset, calling DestroyImmediate(Component, true) directly on the prefab asset works. It’s a workaround to avoid using ApplyPrefabInstance(), and it does not resolve the problem of adding components (of which we are also doing). Adding a component to a prefab instance and calling ApplyPrefabInstance() results in the same behaviour, where some objects within the prefab asset will have the component applied, and some won’t.

I should also mention that using PrefabUtility.ApplyRemovedComponent() and PrefabUtility.ApplyAddedComponent() do work, however they seem to execute slowly on my system. In a prefab with approx. 300 gameobjects (it’s actually two prefabs with 150 objects in each them), performing the action of calling ApplyRemovedComponent() takes about 135 secs, whereas calling DestroyImmediate() on the prefab takes about 2 secs. The hope is ApplyPrefabInstance() will be faster because we can do all our changes and call it only once.

That sounds really odd. If you call ApplyPrefabInstance with action set to UserAction, then it’s doing the exact same thing as clicking Apply All in the overrides dropdown. We would need a repro case to be able to investigate what you’re describing.

I think you might need to call SavePrefabAsset afterwards to ensure the change is saved to disk and not lost on the next reimport of the Prefab.

The DestroyImmediate might be so most faster because of not doing SavePrefabAsset. Regardless, when you’re doign multiple apply operations, each of them will trigger a save and reimport of the Prefab as well as importing everything that depends on the Prefab. You can avoid this happening multiple times by wrapping your multiple apply operations inside these BeginAssetEditing and StopAssetEditing:
https://docs.unity3d.com/ScriptReference/AssetDatabase.StartAssetEditing.html
https://docs.unity3d.com/ScriptReference/AssetDatabase.StopAssetEditing.html

Hopefully that will make it faster in cases where you do the approach of calling multiple apply methods after each other.

We use InteractionMode.AutomatedAction because we don’t want user dialog boxes to appear. Would that make a difference? I’m thinking the issue might be related to creating a prefab asset and applying modifications to it on the same frame, which can only be done through scripts. I’ll see if I can find the time to set up a repro case in the near future.

I forgot to mention that we do use SavePrefabAsset (but thanks for the reminder), and we do enclose them in StartAssetEditing and StopAssetEditing. Using those calls puts it at 135 secs, but without them it takes about 15 minutes.

Could be; I’m not sure. Since we haven’t solved the mystery yet I guess it’s worth trying out to see. Even if you can’t do it as an actual solution, it would narrow things down.

We did try it out. Unfortunately it didn’t seem to help.

All right, then I have no idea, since it’s the exact same call the Overrides window is doing when you click Apply All. We’d need a bug report with repro project to be able to investigate if you believe it’s a bug in Unity.

Yup, I’ll try and set up a repro when I have the chance.

1 Like