What are the best practices for prefabs with variable components?

I am creating a system where I will load character prefabs in different scenes, and depending on the context, the actual GameObject may or may not need certain components. Specifically, in the game’s front end where the characters are loaded to be viewed and customized through the UI, I won’t need to have an input controller or any AI components that are necessary when the same character is loaded in the game. Other components, however, like character attributes or an animation controller, will be shared across contexts.

I have thought of a few potential approaches to the problem:

  1. Create a prefab that contains only the shared components and add any context-specific components in code. The key problem with this approach is that it does not expose the initialization of the context-specific components in the editor, which can be useful when a non-programmer wants the ability to edit the properties of a character. This data can be read from another data file, but then we lose some of the consistency gained by using the Unity editor as a universal tool.

  2. Create a prefab containing the shared components and separate prefabs containing the context-specific components; at run-time, instantiate both the root and context-specific prefab, with the latter as a child of the former. The issue here is the complication of code, in particular whenever a script needs to reference the root GameObject or other components it relies on (especially since there is no GetComponentInParent).

2a) Create a prefab containing the shared components and separate prefabs containing the context-specific components; at run-time, instantiate both the root and context-specific prefab, move the context-specific components to root GameObject, and delete the now redundant context-specific object. This solves all of the problems of the previous version, but it seems pretty messy, and feels like a hack.

  1. Create a separate prefab for each context, with each prefab containing the full set of components required for that context. The problems here are pretty obvious: massive file clutter and duplicate work required anytime a change is made to one of the shared components.

Are any of these considered appropriate practices? Are any of them the recommended best practice for this situation? Is there another solution I’m missing?

Thanks in advance!

1 Like

I would go for 3.). Simply because a prefab is made for that exact purpose: To deliver a set of components with properties and values you actually need for a specific reason.

Another example makes this clear:
I have a class called Enemy. Part of that class is a typical AI movement scheme, for example whether the enemy tries to rush towards the player trying to crash it, or whether it tries to dodge, run away or similar. Selection for the behavior is simply done with an enumeration which is populated to the editor.

That means, I have at least two different enemies which surely share some methods like finding waypoints and so on. But in general they are of different types.

I myself certainly don’t want to create just a single prefab “Enemy” for that, instantiate that, and select any applicable property/behavior/whatever through scripting. Instead I will at least have two different prefabs. “EnemyRusher” and “EnemyCoward”. Simply because each of these need different settings, have different behavior, and I want to be sure to be able to just pull one of them into the scene and have them work without having to cope with any other script.

1 Like

That makes sense for the example you are talking about, but now imagine the following scenario:

There is a front-end codex of all of the enemies, where I want to show the model and have them doing some of their normal in-game animations, but I obviously don’t want them using whatever AI they have in the game. Do I then also create prefabs for CodexEnemyRusher and CodexEnemyCoward?

That reminds be that I forgot one of the potential solutions:

  1. Create a master prefab with all of the components I might need in any context, and enable/disable or flat-out remove the components that are no needed based on the context. The issue I can see with this is that it doesn’t make it clear to a non-programmer looking at a prefab what information is used where (although ideally the component names are relatively self-commenting).

Actually that 4th approach you mentioned totally makes sense to me and I am using that quite often myself.
Sticking to my example with the enemies, I also have an EnemyManager in my scene which is prepopulated with the scene enemies. This manager constantly checks whether the scene enemies should actually be visible and perform their action - if not they or specific components of them get deactivated.

However for your codex example I would most likely still go for different prefabs and take the work of having to reset values anytime it changes - again because they serve different purposes.

Remember that you can also set default values in your scripts which will apply to your components as well. In case it was overwritten in the meantime you can always right click in the inspector and chose “Reset value to prefab”, even on single properties in single components which will get the default value from the component back.

Would option two be easier for you if I said

Debug.Log(transform.parent.GetComponent(Transform).name);

Assume / Test if the object has a parent, call in start function, cache the result and you’re good to go.

There really wouldn’t be a need to do this look-up as you likely know what the parent is when you instantiate the child and could simply set that value when you instantiate your child to save / create a bit of effort.

This is the method I was going to suggest. You only manage 1 prefab, which is ideal. I’m always very careful to program things that are easy for non-programmers to use and I think this is a fine solution.

If it’s still confusing you could even split the components up by context into separate dummy GameObjects that are part of a single prefab. So maybe your Character Prefab would actually be:

Character
→ SharedBehaviours (put all your common components on this sub-GameObject)
→ GameBehaviours (put all your in-game components on this sub-GameObject)
→ FrontEndBehaviours (put all your front-end components on this sub-GameObject)

But, even that kind of splitting would be unnecessary in my opinion unless you have some very complex setup with lots and lots of components. Trust your instincts! It sounds like you already have the designer/non-programmer in mind, which is a lot more than some programmers will do.

1 Like

The downside to the 4th approach is you’re still initializing every component on the prefab when you instantiate it unless the prefab has every component disabled by default. I would make separate prefabs.

1 Like

That’s true and might be an issue if the initialization routine is doing some intensive stuff. Also, if that initialization happens in the Awake method I think that still executes even if the component is disabled.

Anyway, to get around that you could just make the components self-aware of their context. So, in each MonoBehaviour’s Awake method, the very first thing to do is check if it’s in the frontend UI. If so, it can then return early, disable itself, or destroy itself. That may not be ideal and is arguably bad programming style, but I think it really depends on the situation.