calling classes based on string name

Hello. I will admit right from the bat that I’m terrible at programming. However, I have ran into a problem and don’t know how to proceed.

I’m trying a data driven approach for NPC behaviour. I would like a user to be able to create an “action file”, which describes NPCs behaviour. It’s based on a very simple template:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

abstract public class ActionTemplate<T>
{
    public abstract void Fire(GameObject owner);
    // Use this for initialization
    void Start ()
    {
       
    }
   
    // Update is called once per frame
    void Update ()
    {
       
    }
}

For the sake of example, let’s say we have an action that changes NPC’s colour to blue.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class colorBlue : ActionTemplate<colorBlue> {
   

    public override void Fire(GameObject owner)
    {
        owner.gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.blue);
        Debug.Log("I'm firing blue");
        //change col to blue
    }

    // Use this for initialization
    void Start ()
    {
       
    }
   
    // Update is called once per frame
    void Update ()
    {
        Debug.Log("I'm firing update");
    }
}

I want the user to be able to add more of such classes which describe given behaviour. When NPC walks into a node, this behaviour from given file should be triggered. I imagined it like this: NPC walks into a collider. String with a class name is passed: for example “colorRed”, at which point “colorRed” class gets triggered.

What’s the best way to do this? Following approach works, but it leaves me with hard coded class names. I would like to have a string value passed instead of the class name here:

        int random = Random.Range(0, 3);
        Debug.Log(random);
        if (random == 0)
        {

            colorBlue newInstance = new colorBlue();
            newInstance.Fire(owner);
        }
        if (random == 1)
        {
            colorRed newInstance = new colorRed();
            newInstance.Fire(owner);
        }
        if (random == 2)
        {
            colorYellow newInstance = new colorYellow();
            newInstance.Fire(owner);
        }

Thanks for your help, and I hope I described this problem relatively clearly.

Sounds like the factory pattern may be what you’re looking for here. So you’d have a class that constructs the appropriate action type based on the name, like so:

public interface INPCAction
{
   void Fire (GameObject owner);
}

public static class NPCActionFactory
{
   public static INPCAction CreateAction (string type)
   {
      ...
   }
}

You might as well use an interface for this instead of an abstract class, since then you can derive the actions from anything you want, such as being a MonoBehaviour. Also note that a factory doesn’t have to be static, you could get one through DI or a service locator instead, but let’s keep it simple until more flexibility is needed.

Now as for what goes inside CreateAction, you have several alternatives:

  1. Manual switch statement, like what you have above. It would have to be manually updated, but at least it’s centralized in one place. Also, you can use nameof to make it more change-resilient: case nameof(colorBlue): return new colorBlue();
  2. Use Reflection to create an instance based on the name. Automatic and straightforward, but may not work on all platforms and not as performant if you’re creating these frequently.
  3. Have NPCActionFactory maintain a Dictionary<string,Func> and have each class register its own creation function in the static constructor (not the same as the normal constructor). Requires a bit of work for each class, but there’s no central place that has to keep being updated.
  4. Use Reflection in the editor to auto-generate a switch statement before building. This one is more complex, and I’d recommend starting with something else first, but it gives you the automatic-ness of reflection with the performance of a switch statement.