Sunday, October 4, 2009

Unity3D: Useful Tricks with Delegates

It's probably fair to say that Unity developers are quite a broad community skills wise. There is mix of first time game developers, seasoned professionals, programmer orientated folks and those of a more artistic nature. For me that's part of the beauty of Unity it has purpose at so many different levels.

I wasn't that clear on a target audience for this post but I figure it's going to be of more use to the less experienced coders amongst us.

As somewhat of an aside, I'd be curious to see the breakdown of Unity developers between those who exclusively use UnityScript (js) and those who predominantly use C#. I'm using a mix, initially I was sticking more with C#, since it's more similar to the language I know best which is C++. Now I find the terseness and succinctness of the UnityScript quite compelling and I've been using it a lot for the last few components I've work on. There's a downside to UnityScript for sure, but this isn't the topic I set out upon.

One thing I kind of miss when using UnityScript in/as a behavior is the ability to create interfaces. When I say interfaces I mean in the object oriented sense. Interfaces are great when you want to interact with a whole bunch of different objects in the same way. To be more explicit lets look at it in the Unity context.

So lets say I've got a GameObject which has an array of other game objects. (see screenshots).

Normally these would be all identical objects, but what if we want to their functionality to vary somewhat from item to item? In object oriented languages this would be the realm of an interface, but in a Unity behavior (even thought it's an object oriented language) we don't directly have the capability of using an interface. So what can we do?

We can use delegates. Delegates applied sparingly can somewhat mimic this nice object orientated characteristic. It's not quite as elegant but it works just fine.

First of all, it's reasonable to ask if you've never come across the term. “What is a delegate?” It's called something slightly different depending on the computing language are talking about. In C and C++ the equivalent functionality you'd term a function pointer. In my mind the simplest description would be that a delegate is essentially a variable that contains a function or a method. You can “call” the variable just a like a function, and you can assign a function to the variable. If it doesn't make sense now, you may want to read a bit further to see it in action. This might make it clearer.

Say for example we have a bunch of GameObjects. Sticking with some sort of familiar tradition lets call them widgets. So we've got a bunch of widget GameObjects, we've attached our widget script and they're all working nicely. Now if we'd like their behavior to vary a little, how are we going to do that?

First of all lets define a widget script with a delegate that gets called in place of the normal functionality. Once we've done that we can attach another script to each widget to further refine it's behavior. The second script will attach to any selected widget GameObject and assign its own function to the delegate in the primary widget script. In this way you can modify the behavior of the original widget method however you wish. Let's look at some example code of this description, I'm of the opinion it will be much easier to understand.

In my example I have a Master GameObject that contains an array of Widgets. The code of the example script is as such. Note the public array of widgets. This is exposed the inspector screen just as we like it. We assigned our selected widgets in the inspector (see screenshot).


/* Master.js */

public var widgets : Widget[];

function Update () {
for (var i = 0; widgets.Length; i++) {
widgets[i].DoSomething();
}
}


Next we create our Widget GameObjects and their associated widget script.

The widget script is kind of like our object orientated interface through which our Master game object interacts with the widgets. As far as the Master GameObject is concerned, all the widgets are identical.


/* Widget.js */

private var doSomethingDelegate = null;

function SetDoSomethingDelegate(func) {
doSomethingDelegate = func;
}

function DoSomething() {
if (doSomethingDelegate) {
doSomethingDelegate();
} else {
/*
Typical do something code ...
*/
}
}

function Update () {
}


For deviation of the widget behavior, let's look at the DoSomethingElse script, which will attach to a widget and this modify it's behavior when DoSomething is called. Note that the DoSomethingElse is assigned in this scripts Awake function. This assures it's ready to go when the action begins.


/* DoSomethingElse.js */
private var widget : Widget = null;

function Awake() {
widget = GetComponent(Widget);
widget.SetDoSomethingDelegate(DoSomethingElse);
}

function DoSomethingElse() {
/*
The brand new wacky functionality that is different to the other widgets
*/
}

function Update () {
}

@script RequireComponent(Widget)


Finally note the “RequireComponent” directive to really make it clear that this script depends on the Widget script being in place.

So as I hope you can see we can now modify a widget's behavior in a myriad of ways using this technique.

No comments:

Post a Comment