Call method from unknown class

Dear programming experts,

I’ve been coding in Unity for a year and have only just discovered the Component-based system’s potential. However, I’ve run into trouble trying to use it fully.

The concept: I have a class called “InteractableObject.cs” which holds the basics for the player to interact with certain game objects. When added to a game object, the player can press E while inside its collider, and InteractableObject.cs will call its method “ActivateInteractable()”. What exactly this method does, however, depends on which other Component(s) the game object holds. For instance, if the game object is a bed, it also contains a Bed.cs component. I want to set it up so that, when ActivateInteractable() in InteractableObject.cs is called, it calls ActivateInteractable() in the other class, e.g. in Bed.cs.

The problem: This is easy to achieve as long as InteractableObject.cs knows what the other script is. I would:

  1. assign it in InteractableObject.cs by declaring it as a variable (e.g. “Bed bed;”),
  2. call GetComponent() (e.g. GetComponent();),
  3. then call bed.ActivateInteractable() from the ActivateInteractable() function inside InteractableObject.cs.
    Unfortunately, this isn’t feasable because InteractableObject.cs doesn’t know what the other script is. It could be Bed.cs, but also Chair.cs, Elevator.cs, NPC.cs, or some other class. In ActivateInteractable() inside InteractableObject.cs, I want to essentially call WhichEverClassTheInteractableIsInThisCase.ActivateInteractable().

That way, in my opinion, I’m using the Component system the way it is intended to be used: I’m adding a Component with a certain functionality (here: being activated by pressing E) to a game object which holds several components that make up its unique behavior. For these components to interact, however, they need to know what the other components are, which seems like a huge disadvantage to me. One I’m sure can be dealt with easily, but I don’t know how - do you guys?

Thanks a lot in advance!

Something needs to do a similar kind of activity regardless of context but the context causes part of that activity to vary. You have a strategy pattern problem.

Generally, the way people solve this problem is to create an abstraction (e.g., an abstract base class or an interface) and make all of the variants implement that abstraction.

In your case, you need a way to publish the correct implementation of an abstraction. So I would make something like a container behavior.

public interface InteractionTarget {
  void Interact();
}

public class InteractionContainer : MonoBehaviour {
  public InteractionTarget Target;
}

public class Bed : MonoBehaviour, InteractionTarget {
  public void Interact() {
    // whatever a bed does
  }
}

public class InteractionConsumer : MonoBehaviour {
  // I'm assuming this is called from some collision
  void InteractWith(GameObject go) {
    var interactionTarget = go.GetComponent<InteractionContainer>().Target;
    interactionTarget.Interact();
  }
}

There are several approaches to this. One is to use Unity’s SendMessage system. It’s the most flexible as you only need to know the method name. However it has some downsides. It’s rather slow compared to a direct call. Also since it’s based on a string you don’t get a compiler error if you mistype it.

However in most cases since you only call the method on certain events the speed is quite irrelevant.

Another way is to use normal OOP. So you could use a base class with a virtual method which you can override in the derived classes. Instead of a base class you can also use interfaces. Personally i prefer interfaces over base classes. A base class is useful if you have a certain type of component which all share the same fields / functionality with only slight variations. The base class allows you to define the common things there so all components derived from the base class have those as well.

interfaces are more flexible than baseclasses. Unlike base classes a class can implement several interfaces. Imagine you have an interface like this:

public interface IInteractableObject
{
    void Activate();
}

Now you can create several components which implement this interface:

public class Bed : MonoBehaviour, IInteractableObject
{
    public void Activate()
    {
        Debug.Log("Activate Bed");
    }
}

// or

public class Chair : MonoBehaviour, IInteractableObject
{
    public void Activate()
    {
        Debug.Log("Activate Chair");
    }
}

Now to actually use the interface you can simply do;

someGameObject.GetComponent<IInteractableObject>().Activate();

This will work for any component that implements this interface. Unfortunately Unity can’t serialize interface references in the inspector. This would work with base classes. So if you have a base class like this:

public abstract class InteractableObject : MonoBehaviour
{
    public abstract void Activate();
}

public class Bed : InteractableObject
{
    public override void Activate()
    {
        Debug.Log("Activate Bed");
    }
}

In another class you can now define a public variable of type “InteractableObject” where you can assign any class that is derived from InteractableObject:

public InteractableObject someObj;
// ...
someObj.Activate();

You could make an interface, or use reflection.