Friends Don’t Let Friends Use Interfaces on MonoBehaviour Objects

Interfaces are great and solve many problems in the world of programming, but recently I have come across a scenario where they cause more harm than good in Unity. I am currently developing a VR zombie shooter game which I will simply call ZNN VR since I have yet to decide on a final name yet. You can read up on the current state of the game here.

The Problem

Since we are targeting Vive and Oculus Touch for ZNN VR  we needed to somehow handle switching between the two. The most obvious answer is to detect which headset is being used and programmatically tell the game to use the appropriate game objects. However, everything external to that should be agnostic about which device we are using so we don’t complicate our code with unnecessary if conditions.

I originally decided to use interfaces since they are good at abstracting functionality and allowing other classes to define how they should handle each piece of functionality. For instance, the interface for the motion tracked controllers looked something like this:

public interface IMotionController {
    float TriggerPosition { get; set; }
    vector3 GetVelocity();
    vector3 GetRotationalVelocity();
    // And so on...
}

Then I would have two MonoBehavior classes, ViveMotionController and OculusMotionController, that implements IMotionController and anything that needs a reference to the controllers would just grab the reference to the IMotionController interface and call whatever it needs. Sounds great right? Not so much when you start using it.

First, anything that you plan to use in code, you will need to somehow get a reference to it. What would be nice is being able to cast a GameObject class to the desired interface and move on with life. However, since unity uses a component based system, it isn’t that easy. You have to get a list of every component on the object, iterate through each one and cast it to the interface and test to see if you found something that implements it. Here is what that looks like:

// ... some code above
GameObject objectToSearch;

IMotionController motionCtrl = null;
MonoBehaviour[] behaviours = objectToSearch.GetComponents<MonoBehabiour>();
for(int i = 0; i < behaviours.length; i++) {
    motionCtrl = (IMotionController)behaviours;
    if(motionCtrl != null) {
        break;
    }
}
// ... some code below

This is what you need to do EVERY TIME you need a reference to an interface. Sure, I could have generalized it and made it a static helper method somewhere in the project, but having to write this code in the first place felt like I was fighting the framework.

UPDATE: GetComponent<T> does work with interfaces as pointed out by @DFejzagic. I would like to see Unity update their documentation to mention this, but I still believe the negatives outweigh the positives in using interfaces on GameObjects.

I also ran into issues trying to share information that you typically get when you have reference to a normal GameObject class. Things like access to the transform, rigidbody, object name became increasingly tedious because I had to expose each new property on the interface and implement them on each class. The easy way to fix this would to just expose the GameObject reference through the interface, but that too felt like I was fighting the framework.

Why is this such an issue?

After running into these issues, I took a step back to reflect on why using interfaces on MonoBehaviour classes felt like I was fighting the framework. The answer is quite simple. Unity is a component based engine where each component is comprised of MonoBehaviour references. Interfaces are a means to apply different functionality to a single class. If you combine the two you are basically building components (interfaces) that are attached to components (MonoBehaviours) that are attached to GameObjects!

So in essence, I was solving a problem Unity already has solved with MonoBehaviours which is how they expect developers to add functionality to an object. Since fighting frameworks have always led to pain and suffering (okay, maybe that is a hyperbole) I decided to scrap all of the code I wrote relating to interfaces for something that works with the engine.

Polymorphism to the rescue!

Polymorphism was the perfect fit for this. If it’s been a while since your last programming class and you can’t quite remember exactly what that is, here is a quick refresher.

Polymorphism requires a base class which contains virtual methods along with classes that inherit from your base class. Here is how the example above would look as a base class:

public class MotionController : MonoBehaviour {
    public float TriggerPosition { get; set; }
    public virtual vector3 GetVelocity() { }
    public virtual vector3 GetRotationalVelocity() { }
    // And so on...
}

Notice how the class inherits from MonoBehaviour. That means we inherit all the functionality from MonoBehaviour and include the extra MotionController functionality.

So an implementation of this would look something like:

public class OculusMotionController : MotionController {
    public override vector3 GetVelocity() {
        return GetOculusTouchVelocity();
    }

    public override vector3 GetAngularVelocity() {
        return GetOculusTouchAngularVelocity();
    }

    public Update() {
        TriggerPosition = GetOculusTouchTriggerPosition();
        // more code
    }
    // ... and so on
}

As with the MotionController class inheriting the functionality of MonoBehaviour, OculusMotionController inherits all functionality of MotionController as well as MonoBehaviour. That means the only thing you have to define in the MotionController class is the object’s functionality and anything that has reference to it can get access to all of the properties GameObject provides like Rigidbody, the object name or even getting access to other components via GetComponent<>();

This brings me to my next point, and probably the biggest win for this over using interfaces. Getting a reference to one of these object is super simple. All I need to do is call myGameObject.GetComponent<MotionController>() and I have reference to the object no matter if it is of type ViveMotionController, OculusMotionController or something else I decide to support in the future.

After making the switch to using polymorphism over interfaces, my code shrunk down by a lot and made it much easier to follow to boot which is a win for future me who will have to support the code. Now don’t get me wrong, I still think interfaces are super useful, but if they eventually make their way onto a MonoBehaviour, I say avoid it at all costs and you will thank yourself later.

Have any of you run into similar issues with interfaces or other scenarios where you felt like you were fighting your game engine?

Comments

comments