Managing Many Similar Prefabs (Inheritance & Prefabs of Prefabs)

I am working on an action RPG

I am struggling to find the proper way to manage many similar prefabs. For example I have many different types of zombies. The controller script is different for each (i.e. ZombieDefault, ZombieBerserker, ZombieGiant which all inherit from Zombie). I tint them different colors and/or change their sizes. But other than that everything is the same (same animations, same ability/feat scripts, rigidbody, and many other scripts/components).

As a rule of thumb with programming you should never duplicate/copy anything. This generates more work, and also leaves room for error if you change something then you must remember to change it everywhere it’s copied. With c# and inheritance, and Unity’s components, you can avoid duplication. However, Unity doesn’t seem have any way to avoid duplication with regards to prefabs.

Anytime I change something about zombies I must repair every prefab manually. If I change certain things on the prefab (serialized field names, the order of components) then it breaks everything linked in the scene. It can get really bad… for example if insert/remove a physics layer than every prefab in the game’s layer will be incorrect and 100’s of prefabs need to be repaired. Or if I restructure monsters hierarchy then I must manually fix dozens and dozens of monsters.

Not sure how to properly manage this. Options:
1: Prefab of prefabs (in the works for Unity, but not done yet)
2: Build each instance of an object at runtime (i.e. add rigidbody, add scripts, add components). This doesn’t work because you can’t add UNET NetworkBehaviour scripts at runtime (it throws errors due to network mismatch). Also you can’t link via serialization to things in screen if you do this.
3: Somehow use scripts in the editor to build & manage all my similar prefabs (unsure if this can be done?)

Has anybody else ran into problems where you feel like there is no proper way to manage similar prefabs without duplication/copying which case they are no longer linked.

Any solutions? Thanks in advance.

The strategy most often used for this sort of thing is to have:
Main prefab (ZombieBerserker)
ZombieBerserker script knows it needs to instantiate prefabs ZombieBerserkerMesh and ZombiePathfinderAgent, load texture ZombieBerserkerTexture, whatever else you need to know
Each prefab is compartmentalized so it can be reused as you see fit
The ZombieBerserker instantiates all the bits and pieces it needs in Awake, making it largely indistinguishable in many ways from instantiating the prefab as a whole.

You could generalize the whole thing with a FauxNestedPrefab component: it has a reference to a prefab, and in Awake, it spawns that object in its own place in the hierarchy then destroys itself. The main limitation here is the inability to drag-link references between the nested prefabs, but them’s the breaks.

You could also (and this solution is admittedly a little extreme) implement each enemy as a Scene, which gets loaded additively when it’s spawned.

1 Like

The problems I have with adding all components during Awake is
A: You can’t link serialized data in the scene (unless all serialized the fields are in the 1 script that adds all the other components which isn’t currently the case for me)
B: You can’t add UNET NetworkBehaviour scripts at runtime during Awake or Start. It throws network mismatch errors.

What I am most interested in is instead of using a script (which allows for inheritance) to build the runtime gameObject during Awake by adding components… can the script build the prefab gameObject in the editor? This would fix both issues listed above. This seems like the best solution to me… if it’s possible with Unity. I imagine this would have issues with refreshing the prefab and breaking scene serialized links. I’ll look into this and share how it goes.

Regarding FauxNestedPrefab… as you say it doesn’t serialize to things in the scene which is a no-go.

Can you say a bit more about implementing each prefab as a Scene?

Regarding the NetworkBehaviour,
I have a “Unit” prefab that is literally just an empty gameobject with a Unit script, a NetworkBehaviour (and NavMeshAgent,…).

When the unit is spawned on the server, it sets UnitId on the Unit script, and on the client it loads the correct model - which is a prefab containing skinned mesh(es), particle systems and whatever else is needed to distinguish units from each other.

I use the drag-drop stuff stuff in unity rarely for this reason. Some UI things are exception, or stuff thats not a prefab and has a really simple setup (UIManager that needs access to its layers)…

Its sad that the prefab system is almost completely unusable for any larger project.
I mean how are you supposed to build some good UI with it when you can’t nest prefabs?
You’re forced to chose between having a “GenericDialog” as your prefab, or the contained “InputBoxPanel” or the buttons, … its horrible.

But once you get over the fact that the prefab system will only get you into trouble because its so limited, you can find ways to fix it yourself.
For example constructing your stuff manually like I do with my Unit script for enemies or like @StarManta described it.

1 Like

I mostly understand what you did with the Unit but not completely.
1: How do you use your Unit NetworkBehaviour? How do you use RPC’s/Commands/SyncVar’s? Or is this only to send the UnitId (in which case how to you handle other NetworkBehaviour scripts since those can’t be added after the UnitId is read during OnDeserialize without creating networkReader mismatch errors)?
2: How do you link serialized info in the scene? For example I link killable objects to switches. And link monsters to patrol paths. Or set the color index of a switch crystal. These would all be unique to the type of Unit.

Again the issue I have with building everything up at runtime during Awake/Start/OnDeserialize is
A: If the components don’t exist on the prefab in the inspector then you can’t link serialized fields in the scene.
B: UNET NetworkBehaviours throw errors when using AddComponent due to mismatch errors.

To me an ideal prefab solution needs to
1: Not have unlinked copied/duplicate issues (i.e. support inheritance of prefab of prefab or something similar)
2: Allow serialized data to be assigned in the inspector to link things in a scene
3: Have some form of robustness to where the prefab can be changed without breaking everything.
4: Be compatible with UNET (not add NetworkBehaviour components at runtime, be a registered prefab so “spawn” can work)

The real hangup for me is scene serialization linking robustness to prefab changes. I wish there was a way to manually define the serialized inputs (and outputs… plural! It’s a bummer how a script cannot have multiple serialized outputs. The only output can be the entire script. Ugh.) for a script as well as manually assign ID’s to components or child gameObjects in the hierarchy so their references aren’t lost if the hierarchy of the prefab is changed.

Grrrr!

  1. Unit is the NetworkBehaviour that contains all the needed stuff. There are no other behaviours.
  2. I will just answer how I would implement your examples, that would answer your question best I guess:
    a) To make some switch that when triggered kills some units, I would make the switch have some script that looks for the unit to kill in some specific region / matching some specific unit id (int).
    b) Controlling paths: My units can have a script attached to them, those scripts are literally just a monobehaviour (not network behaviour) with a string. The string is just c# which gets compiled at runtime.
    Inside the script I’d send commands to the NpcAIController (another script, also a monobehaviour, which also only runs on the server), for example MoveToCommand, UseCommand, AttackCommand, … (those are simple classes which only implement some common base class I made, not any components/Monobehaviours)
    c) The crystal would have an Entity script which is like Unit but does not have stuff like Health, but custom stuff.
    It depends if crystals are things that appear more often. If its just a one off thing then I’d send a “CustomValue” message over the network that contains json, in thise case “{“ColorIndex”:1}” and the cliend-side script would just interpret it.
    I didn’t think too much about that but it’s definitely possible with the system I have at the moment.
    In the worst case scenario, where maximum performance is needed, I’d just make a new subclass of Entity (like Unit is already a subclass of Entity) and implement the stuff there, that would add a new prefab to the client.
    At the moment I just have 2 prefabs for the whole game: Unit and Entity, and everything in the game is one of those.
    Entities can be elevators, ships, switches, …

If you want to know more, ask more detailed questions (more examples :P)

1 Like

I ended up using Editor scripts to generate my prefabs. In hindsight I should have done this a long time ago!
-Any prefabs that are unique I just build in inspector like normal
-Any prefabs that are have a lot of shared components/hierarchy I build using a script w/ inheritance.

It didn’t take as long as I thought and seems to work well
-It allows linking/serialization of prefabs to things in the scene via. Unity serialization
-The linked serialization to things in the scene does not break when the Prefab is adjusted using ReplacePrefabOptions.ReplaceNameBased (if you totally scrambled the hierarchy/components then I believe it would break)
-It allows me to use inheritance. For example PrefabGiantZombie inherits from PrefabZombie. Then I just override it’s size, mass, and damage.
-You get to see your prefabs (actual size/color/etc.) in the scene
-After you change your code that changes the prefabs you click a button in the editor to refresh the prefab(s).

It only took a day to set up and would have saved me a lot of time if I had done this from the start. If you have a large project with many similar prefabs I suggest doing this. I could upload a video or share code if anybody is interested.

I am not 100% sure I fully understand but I think I have a very similar system for my monsters. I have an editor script called spawn point and when you change the monster ID it generates a preview (with hide flags not edittable) in the scene as its child.

However I don’t see how I can easily override properties without making a new script for each type if object (I’d need a separate script for every monster, like overrideSekeltonValues, overrideSpiderValues,… Right?)

Yeah a new script for each type of prefab. For example I have…

public class ZombieGiantController : ZombieDirectController
{
    protected override void InitializeStatInitialMag()
    {
        base.InitializeStatInitialMag();
        statInitialMag.SetMag(Mass, 3f);
        statInitialMag.SetMag(HealthLimit, 2000f);
    }

    public override float defaultSize { get { return 3f; } } 
    protected override KeyMaterialRenderer editorKeyMaterialRenderer { get { return KeyMaterialRenderer.KeyZombieGrey; } }
}

This makes a GiantZombie prefab which is the same as a regular zombie but has
-more mass
-more health
-larger size
-different texture material