Gamasutra: The Art & Business of Making Gamesspacer
View All     RSS
October 20, 2014
arrowPress Releases
October 20, 2014
PR Newswire
View All
View All     Submit Event





If you enjoy reading this site, you might also want to check out these UBM Tech sites:


 
Using abstractions and interfaces with Unity3D
by Victor Barcelo on 12/17/13 09:52:00 am   Featured Blogs

The following blog post, unless otherwise noted, was written by a member of Gamasutra’s community.
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.

 

Unity3D includes a component architecture paradigm. This allows us to attach code as classes that derive from MonoBehaviour to our GameObjects and treat them as script components.

With Unity3D GameObjects can communicate with scripts via SendMessage by just addressing the name of particular methods. We can also get the reference to the script with GetComponent. This brings us the safety of type checking but makes us rely on a concrete script reference which results in tight coupling (this can actually not be the case if the class inside the script inherits from some kind of abstraction, more on this later).

Besides the Unity3D component mojo we also have more classical approaches in the OO department that may suit our needs depending on the scenario.

One of them is to use an observer pattern based system, such as the one found in the Unity3D wiki, which is a fastest and cleaner alternative to Unity’s SendMessage. This solution is suitable if we wanted to communicate with a number of GameObjects without having to manage concrete references for each one.

We also have the alternative of using abstract references (abstract classes and interfaces). With these we can make our code less coupled and we can plug reusable logic without relying on concrete classes.

The following example shows how can you combine this approach with Unity's component based nature.

All the code and a working example can be found in this GitHub repository.

Abstracting SHUMP entities

If you try to categorize the most used type of logic in a SHUMP you may agree with me if I point “movement patterns”. Any type of projectiles, enemies and power ups will move not always in a linear fashion. You may have diagonal, senoidal or saw tooth movement patterns and you are likely to combine them to make complex sequences that can for example fit a boss encounter.

Let’s start by creating a suitable interface for our movement algorithms.

public interface IMovement
{
    void Move(GameObject entity);
}

Now let’s show two movement patterns that implement this interface.

using UnityEngine;

public class LinearMovement : IMovement
{
    public void Move(GameObject go)
    {
        go.transform.Translate(Time.deltaTime * 10, 0f, 0f);
    }
}
using UnityEngine;

public class SenoidalMovement : IMovement
{
    private const float amplitude = 1f;
    private const float frequency = 2f;

    public void Move(GameObject go)
    {
        float yMovement = amplitude * (Mathf.Sin(2 * Mathf.PI * frequency * Time.time) - Mathf.Sin(2 * Mathf.PI * frequency * (Time.time - Time.deltaTime)));
        go.transform.Translate(Time.deltaTime * 10f, yMovement, 0f);
    }
}

Neither the interface nor the movement logic need to be added to GameObjects since they don’t inherit from MonoBehaviour.

For this example our moving entity will be missiles, so let’s create a base class for them.

using UnityEngine;

public class BaseMissileBehaviour : MonoBehaviour
{
    private IMovement movementType;

    void Start()
    {
        Destroy(gameObject, 2f);
    }

    void Update()
    {
        movementType.Move(gameObject);
    }

    public void SetMovement(IMovement _movementType)
    {
        movementType = _movementType;
    }
}

This script has to be attached to a GameObject as a component. From here we can create a collection of prefabs to set up different missile visuals such as the model or particle systems.

Let's keep abstracting entities for the sake of reusability and create a weapon interface.

public interface IWeapon
{
    void Shoot();
}

Now let’s get concrete, I give you the laser cannon.

using UnityEngine;

public class LaserCannon : IWeapon
{
    private float fireDelay;
    private float timeSinceLastShoot;
    private GameObject owner;
    public GameObject projectilePrefab;

    public LaserCannon(float _fireDelay, GameObject _owner)
    {
        owner = _owner;
        fireDelay = _fireDelay;
    }

    public void Shoot()
    {
        if (Time.time > fireDelay + timeSinceLastShoot)
        {
            GameObject projectile =
                (GameObject)
                    GameObject.Instantiate(projectilePrefab, owner.transform.position, Quaternion.identity);
            projectile.GetComponent().SetMovement(new LinearMovement());
            timeSinceLastShoot = Time.time;
        }
    }
}

This way of structuring our code gives us the following benefits.

  • We can reuse movement logic, just as we did in BaseMissileBehavior we can plug this logic onto any moving entity. This way of using interfaces is known as strategy pattern.
  • We can encapsulate abstract entities (missiles) inside concrete entities (weapons). This let's us choose at which level we can assign attributes (like damage or fire delay in this example). In this case note that the overall configuration (which missile we use and which movement we plug inside them) takes place at the weapon level via its constructor.
  • We can ask another class to give us instances of predefined weapons by supplying a name (preferably enum) or id. This class could query a data source (e.g. XML or SQLite) for the specific configuration of the given weapon. This is an application of the factory pattern. You can apply the same idea to entities like enemies (tip: if you are continuously creating entities with a low life span add to your factory object pooling to avoid performance penalty).

And finally this is how an entity could make use of a weapon via an interface.

using System.Collections.Generic;
using UnityEngine;

public class PlayerShip : MonoBehaviour
{
    private IWeapon activeWeapon;
    private List weapons;

    void Start()
    {
        weapons = new List { new MissileLauncher(0.5f, gameObject), new LaserCannon(0.5f, gameObject) };
    }

    public void Control()
    {        
        if (Input.GetKey(KeyCode.Space))
        {
            SetWeapon(weapons[0]);
            activeWeapon.Shoot();
        }
        if (Input.GetKey(KeyCode.RightControl))
        {
            SetWeapon(weapons[1]);
            activeWeapon.Shoot();
        }
    }

    private void SetWeapon(IWeapon _weapon)
    {
        activeWeapon = _weapon;
    }
}

This is a good example since you can see how easy it is to switch concrete classes based on its interface during runtime.

You can use this approach to create a movement pattern based of multiple movement behaviors as we discussed early. You just have to cycle through a collection of movement implementations every x time which is something easily doable with coroutines.

GetComponent and abstractions

Actually GetComponent can fetch a script component by its superclass or interface implementation. I will show you an example of a scenario where you could use this, but first here you have some useful extension methods.

using System.Collections.Generic;
using System.Linq;
using UnityEngine;

internal static class ExtensionMethods
{
    public static T GetAbstract(this GameObject inObj) where T : class
    {
        return inObj.GetComponents().OfType().FirstOrDefault();
    }

    public static T GetInterface(this GameObject inObj) where T : class
    {
        if (!typeof(T).IsInterface)
        {
            Debug.LogError(typeof(T).ToString() + ": is not an actual interface!");
            return null;
        }
        return inObj.GetComponents().OfType().FirstOrDefault();
    }

    public static IEnumerable GetInterfaces(this GameObject inObj) where T : class
    {
        if (!typeof(T).IsInterface)
        {
            Debug.LogError(typeof(T).ToString() + ": is not an actual interface!");
            return Enumerable.Empty();
        }
        return inObj.GetComponents().OfType();
    }
}

Let's picture a typical mouse driven action rpg. You may click on a door and you player will move to its proximity and the door will open. You could create a ISOpenable interface with an Open method. Since different doors may have a different opening logic (sliding, rotating etc) you could reference any type of door by its interface.

To make this work you could check for every mouse click if the mouse is hovering a GameObject that implements ISOpenable and call a GetInterface().Open();

You could also use this approach to attack enemies with something like GetInterface().ApplyDamage(playerDamage); and so on.

Going further with abstractions

The art of plugging concrete classes on to abstractions is called dependency injection, and relies in the idea of inversion of control which is one of the core techniques of maintainable OO code.

If you want to go further in the practice of adding OO spice to your Unity3D development I recommend taking a look at StrangeIoC which is a framework that gives us a variety of tools, being one of them a cleaner mechanism to manage injections.


Related Jobs

Monochrome LLC
Monochrome LLC — Aptos, California, United States
[10.19.14]

Senior Programmer
Gearbox Software
Gearbox Software — Plano, Texas, United States
[10.17.14]

Server Programmer
Digital Extremes
Digital Extremes — London, Ontario, Canada
[10.17.14]

Generalist Programmers
Petroglyph Games
Petroglyph Games — Las Vegas, Nevada, United States
[10.17.14]

Network / Web Programmer






Comments


Ryan Christensen
profile image
Good stuff. I definitely prefer the Messenger way so that messaging is more publish/subscribe using delegates/eventhandlers, and messaging from non MonoBehaviours (note the spelling, Unity has forced us to be brits) and POCO classes is the same and standard. Messaging and event systems is a key part of decoupling, further is using generic data objects in the messaging signatures to minimize references for really drop-in-able components.

Ariel Gross
profile image
These types of articles are great brain candy for us burgeoning programmers. Thank you for sharing.

R. Hunter Gough
profile image
SHUMP?

Sami Turcotte
profile image
While I think your reasoning and motivation are fantastic, I think Unity needs to step up and support dependency injection in the editor itself. I have previously wrote about this and I just added a proof of concept based on your examples in this post: http://forum.unity3d.com/threads/210478-Editor-driven-dependency-
injection

Hopefully we can gather enough enthusiasm around this topic for Unity devs to take notice!

Victor Barcelo
profile image
That's quite interesenting, implementing somehing like that in the editor would be a step forward :)

Alex Zanfir
profile image
This Unity Course on PluralSight uses many of the practices here

http://pluralsight.com/training/Courses/TableOfContents/introduct
ion-game-development-unity

[User Banned]
profile image
This user violated Gamasutra’s Comment Guidelines and has been banned.


none
 
Comment: