Component Decorator

Hey everybody. Whatado?

I have an inherited component structure.

Let’s say I have the components:

Selectable : MonoBehaviour

DesktopSelectable : Selectable

XRSelectable: Selectable

Now. I need some kind of Decorator script that takes a GameObject and decorates it with components depending on the use case.

I would like a ScriptableObject asset to give me the flexibility I need. I already have a PrefabMap scriptable object:

public class PrefabMap : ScriptableObject
{
    class PrefabItem
    {
        public string Key;
        public GameObject Prefab;
     }

     public PrefabItem[ ] prefabs;

     public GameObject Find(string key)
     {
           // bla bla bla
           return prefab;
      }

}

So I can just do

GameObject instance = Instantiate(prefabMap.Find("Platforms.XR.OculusQuest.Left")

And that gives me soooo much flexibility. I can create various PrefabMap assets and assign them to scripts that deal with instantiation.

Now I would like to do the same for Components, and have a ComponentDecorator that gets a ComponentMap asset as input.

But there are at least two problems: I can’t drag and drop a script from the project into the Component slot. And the method is gameObject.AddComponent()

What should I do?

You should probably rethink your design. Do you NEED that “flexibility”? It isn’t free. And reliance on strings makes your code vulnerable to typos, because you effectively bypass compile-time checks.

YOu’re trying to use inheritance based approach similar to classic C#. Unity is geared towards component based design instead.

That means it works best when you use shallow inheritance (meaning any component will be directly inheriting from MonoBehavior in most cases, and in rare cases it will be a “grandchild” of monobehavior).

Normally, most of the things in unity game can be done by spawning a prefab, spawning a prefab and tweaking it a little, and need for any sort of “Component Decorator” never arises.

1 Like

Agree with the above, using magic strings seems fragile as all hell.

What you want would be akin to serialising System.Type, which, naturally needs custom serialisation and is more effort than it’s worth.

You can also reference prefabs by a component on its root game object, and get that component as the return of Instantiate() instead of just the game object.

GetComponent() also works with interfaces if you want to be really compositional.

The string is just an example. The string is actually part of a ScriptableObject. So I can configure the string in the Editor.
There is no single hardcoded string in my code, and in my project it appears only once, managed by an Editor Script.

Even if I don’t use inheritance, I still need to decorate my gameobject with different components based on different use cases.
I still need a Selectable for Desktop, and a Selectable for XR. I don’t want to write an all-purpose Selectable, because sometimes I do not need XR, and sometimes I do not need Desktop. And I rarely need both.

You absolutely can. Just make a MonoScript field:

public MonoScript Script;

Then to add the component to something you do:

someGameObject.AddComponent(Script.GetClass());

While the MonoScript type is quite useful for editor scripts, note that it is an editor only type. So the MonoScript type can only be used at edit time and must not be used in a runtime script.

ps:
If you’re looking for a type selection or a field to drag and drop monoscripts, have a look at my answer over here. The “MonoScriptPropertyDrawer” works for string fields decorated with the MonoScript attribute. It stores the fully qualified type name in the string.

2 Likes