Objective: When an object’s OnTriggerEnter() event is raised, reduce it’s health based on the specific object it came in contact with.
One of the primary pillars of OOP, polymorphism, allows an object to implement a particular behavior while allowing the performance of that behavior to vary between objects. For example, dogs and cats are both animals which can speak, but when speaking, dogs will bark while cats meow. Interfaces are a great way to accomplish these behaviors across objects and create cleaner, more manageable code.
In my scene I currently have three objects which each interact with one another. An enemy, a harpoon, and a player to fire the harpoon towards the approaching enemy. When hit by the harpoon, the enemy needs to be able to reduce it’s health by the appropriate damage amount of that specific harpoon object.
To check for collisions on my enemy, I’m using OnTriggerEnter() and have the necessary collider and rigidbody components on each enemy and harpoon object. Without the use of interfaces, a simple, yet limited, way to verify when the enemy is hit would be through the use of tags. By tagging the harpoon, an if() statement comparing the tag can be quickly used within the enemy’s OnTriggerEnter() method to check for a collision:
The harpoon object contains a Harpoon script with a public DamageAmount property which returns the amount of damage it causes. That damage amount can then be passed into the enemy’s DamageReceived() method to reduce its health.
This implementation works well enough for a single object, but once more objects and features are added, then any classes needing to check for collisions can quickly become bloated, inefficient, and unmanageable:
This is where interfaces can really shine.
First, some key features of interfaces to acknowledge:
- interfaces may only contain methods and properties
- methods and properties cannot be implemented within the interface
- any class inheriting an interface MUST implement each of its methods and/or properties
- classes can inherit more than one interface
To start, I created a new script named IDamager to signify it’s an interface, and like with the original Harpoon script, the only interface member is a float property named DamageAmount.
Back in the Harpoon script, the IDamager interface is then inherited by the class. Once inherited, an error will appear until each member of the interface is implemented (i.e. the DamageAmount property).
To quickly implement the interface, right click the error, go to “Quick Actions and Refactorings…”, and then select “Implement interface”. The necessary methods and/or properties will then be automatically added to the class and ready to use.
With the public DamageAmount property, any class implementing the IDamager interface will have that variable available to access. Now instead of the enemy’s OnTriggerEnter() method having to compare multiple tags, it only cares whether the object it collided with implements the IDamager interface. If so, then that object’s respective DamageAmount can be passed into the enemy’s DamageReceived() method.
Just like dogs and cats having a base speak behavior that they individually perform, these harpoons and any other necessary objects can have their own DamageAmount without relying on an endless stream of if()…else if()… statements.