Avoid retyping code?

Hi, I am having a question.

Let’s say i have 2 gameObject. They have Collider2D (isTrigger on) so that when player enter the collider, and press ‘e’ they gameObject would call a function (which I name interact() ).

Most of the script would be the same, the only different is interact() function.

What should I do to avoid retyping code?

@Quangmeoo

There are a few ways to achieve this. I’d consider using inheritance:

Make a base script, inheriting from MonoBehaviour, with all the common methods coded in it and an abstract Interact() method. Then create a script for each gameObject, inheriting from your base script, and override the interact() method.

Inheritance is one way (as Mr-Men said), using SendMessage is another. SendMessage uses Unity’s messaging system and is more suited for component based design. So you simply create two scripts. The first one has an OnTriggerStay2D method where you can check if a certain key is pressed while the player is inside that trigger. If so you simply send a message to “Interact”. Another script can simply implement an “Interact” method which will be called automatically.

public class TriggerActivator : MonoBehaviour
{
    public KeyCode key = KeyCode.E;
    public string activationTag = "Player";
    void OnTriggerStay2D(Collider2D aOther)
    {
        if (aOther.gameObject.CompareTag(activationTag) && Input.GetKeyDown(key))
            SendMessage("OnInteract",aOther.gameObject, SendMessageOptions.DontRequireReceiver);
    }
}

This script, when attached to an object in the scene will send an “OnInteract” message to other scripts on the same object. So if you attach another script like this:

public class AmmoContainer : MonoBehaviour
{
    public void OnInteract(GameObject aOther)
    {
        Debug.Log("object '"+aOther.name+"' has interacted with this AmmoContainer at " + transform.position);
    }
}

Usually the passed gameobject will be the player object since by default we check for the tag “Player”.


Another way is to reverse the logic which is often the better way. So you would have the TriggerActivator on the player object and have it send a message to objects the player collides with. For this you would have to change the class slightly and of course attach it to the player instead of the actual object:

public class TriggerActivator : MonoBehaviour
{
    public KeyCode key = KeyCode.E;
    void OnTriggerStay2D(Collider2D aOther)
    {
        if (Input.GetKeyDown(key))
            aOther.gameObject.SendMessage("OnInteract",gameObject, SendMessageOptions.DontRequireReceiver);
    }
}

This has the advantage that you only need one instance of the TriggerActivator on the player. Every time you press the button “e” it will simply send a message to all objects that are current touching / overlapping with the player.


Instead of “messages” you could also use interfaces. Using interfaces has the advantage that you can’t misspell the “message name”. However it would require to use GetComponent on the target object to aquire that interface.

Example:

public interface IInteractable
{
    void Interact(GameObject aOther);
}

public class TriggerActivator : MonoBehaviour
{
    public KeyCode key = KeyCode.E;
    void OnTriggerStay2D(Collider2D aOther)
    {
        if (Input.GetKeyDown(key))
        {
            var interactable = aOther.GetComponent<IInteratable>();
            if (interactable != null)
                interactable.Interact(gameObject);
        }
    }
}

A script on the other object can now implement that interface:

public class AmmoContainer : MonoBehaviour, IInteractable
{
    public void Interact(GameObject aOther)
    {
        Debug.Log("object '"+aOther.name+"' has interacted with this AmmoContainer at " + transform.position);
    }
}

This will automatically call the Interact method on any class that implements that interface.