Using a script as a member variable in another script.

Hey,

I will preface this by saying I am using C# for my scripts.

I have a script called ButtonAction. It is derived from MonoBehaviour. It has a function named Respond().

I have a script called LoadScene. It is derived from ButtonAction. It also has a funciton named Respond().

I have a script called PlaySound. It is derived from ButtonAction. It also has a function named Respond().

I have a game object. It has a script named StaticObject attached to it.

In the script StaticObject, I want to have a public variable. This variable must, essentially, be a script. More specifically, a ButtonAction script. More specifically still, the variable must hold a script derived from ButtonAction.

In the script StaticObject, I wish to call the Respond() function of the aforementioned member variable, and have the appropriate function be called.

This is to achieve a system where I can have a Prefab that has the StaticObject script attached to it and I can make many instances of said Prefab but have each one execute a different script when clicked without braking the instance link to the prefab.

Any guidance in achieving this would be appreciated.

Do you mean that you want an easy way to change the implementation of respond? Unity offer limited existing help for this, since what I guess you want to do is make an option to choose an implementation detail for your component (Strategy Pattern).

If you have a known and fixed set of options, you could do with an enum, and add corresponding component to your object during awake. This is a bit quicker to code than the other solutions since you only need to create an enum, a public variable for it and a switch that add the right component to the object.

If it’s not known during code design time and enums aren’t an options, you could make an editor inspector script that use reflection to find all derived classes and present them as options. You’d then store the name of that class and use AddComponent with the string or type argument overload.

If I misunderstood you and you just need to be able to link to an existing game object that has a ButtonAction script on it, just add a public variable of that type.

public ButtonAction action;

Update to comment discussion:

public class Test : MonoBehaviour
{
    public enum ClickOptions { ClickMethod1, ClickMethod2, ClickMethod3 }
    public ClickOptions click;

    void OnGUI()
    {
        if (GUI.Button(new Rect(0, 0, 64, 64), "Click me"))
            SendMessage(click.ToString());
    }

    void ClickMethod1() { Debug.Log("Clicked"); }
    void ClickMethod2() { Debug.Log("Clicketyclick"); }
    void ClickMethod3() { Debug.Log("Click click click"); }
}

This doesn’t add much flexibility in regard to scripts. But you could easily do the same with AddComponent(click.ToString()) if the ClickOptions enum contain name of classes you want to support, and send a message later.

Edit: Read the comments. This doesn’t work.

I thought I’d update the situation. I now have the exact behaviour I wanted.

I create an empty game object in my scene. I attach this script to it:

using UnityEngine;
using System.Collections;

public class TheScript : MonoBehaviour 
{    	
	// Use this for initialization
	void Start () {}    	
	// Update is called once per frame
	void Update () {}
	
	void OnGUI()
	{
		if(GUI.Button(new Rect(x, y, 64, 64), Tex))
		{
			gameObject.AddComponent(a.GetClass());
			gameObject.GetComponent(a.GetClass()).SendMessage("Respond");
			Object.Destroy(gameObject.GetComponent(a.GetClass()));
		}
	}
	
	public Texture2D Tex;
	public UnityEditor.MonoScript a;
	public int x;
	public int y;
}

I have written this base class script:

using UnityEngine;
using System.Collections;

public abstract class BaseClass : MonoBehaviour 
{    
	// Use this for initialization
	void Start () {}    	
	// Update is called once per frame
	void Update () {}
	
	abstract public void Respond();
}

…from which these are derived:
using UnityEngine;
using System.Collections;

public class DerivedA : BaseClass 
{
	// Use this for initialization
	void Start () {}
	// Update is called once per frame
	void Update () {}
	
	override public void Respond()
	{
		print("DerivedA");
	}
}

…and:
using UnityEngine;
using System.Collections;

public class DerivedA : BaseClass 
{
	// Use this for initialization
	void Start () {}
	// Update is called once per frame
	void Update () {}
	
	override public void Respond()
	{
		print("DerivedA");
	}
}

So now, when I drag either DerivedA or DerivedB onto the “A” member variable of TheScript in the inspector, the appropriate “Respond” funciton is called. I am posting this to clarify my original question and posting a solution so that if someone else needs to do this later, they can google it and hopefully find this solution. It may also be common knowledge, but if it is, it’s not easy to find. Hopefully this post makes it a little easier.

I would, however, like to get some feedback on the code if possible. It may be somewhat naieve in its execution and could be done more safely, efficiently or easily.