After writing
iFly and
Star Wars: Trench Run in UnityScript, I became painfully aware of the shortcomings associated with UnityScript. Mainly that it has no decent event / delegate system to leverage. Sure, you have event handler methods that exist in GameObject etc, but that’s a complete throw back to AS2/1 days. I tried porting an old AS2 event dispatcher class to UnityScript (which I did successfully) but had pointed out to me by someone at Unity that it would be painfully slow. He then suggested I consider c# and get involved with delegates. He was right.
So, for a while now, I’ve been doing c# and loving it. There were, however, some things that obviously caused me some discomfort and that’s why I’m writing this post. I’ll be adding to it when ever I find new topics that apply.
First, here is a link that provides the majority of the “differences” between UnityScript and c# – I recommend this one as it really covers the basics very well. I’ll be covering the not-so-obvious ones that made me scratch my head
http://answers.unity3d.com/questions/5507/what-are-the-syntax-differences-in-c-and-javascript
Alright then, here’s the first thing to look out for:
Number = float
Coming from AS3, you’re used to Number, int, uint and for the most part, you get what they do. When coming over to c#, there is no “Number” type. Instead, use float. Just think of it in the most simple terms: ”f”loat for “fraction”. Meaning, if you know you’re number will be fractional (like 3.16) then give it a type of float:
float myScale = .5f;
Note the “f” at the end of the declaration (.5f) – that’s how you cast it as a float. If you don’t, Unity compiler will give you an error. Same goes for passing floats as arguments, make sure you include the “f” cast:
Vector3 pos = new Vector3(.1f, .05f, 2); // you can add the "f" cast after a whole number if you like, but it's not necessary
If you’re doing some division or scaling math and you know the outcome *could* be fractional, both sides of the equation need to be floats or you’ll get a whole value in return. Simple enough you might think, but I’ll bet you scratch your head at least one time on this one where you can’t figure out *WHY* it’s not calculating a remainder for you:
float i = 15f;
float j = 10f;
float value = i / j; // 1.5f
If you want to parse a string into a float or int:
int value = int.Parse(stringValue);
float value = float.Parse(stringValue);
If you want a whole number and an int from a float, use FloorToInt() – I mention this one simply because of how AS3 just doesn’t give a crap, but c# does:
int value = Mathf.FloorToInt(float n);
Event Dispatching
In AS3, everything extends EventDispatcher practically. Everything can dispatch events, have listeners and everyone’s happy in their little event driven world. Then you come over to Unity and realize, we’ve taken a step back into AS1/2 land. There are event handlers on GameObjects and if you declare them, then they will receive the calls – very very onEnterFrame type of stuff here.
So, given my history and experience with AS2, I naturally dusted off an old EventDispatcher class that we used to use back in the day. Like I said above, I converted it, it worked, but was made to realize that in a performance setting, it’d be a pretty substantial bottle neck. Still, if you’d like to see how it’s done, here you go ( now at least, I won’t feel like the time I spent on it was wasted )
EventDispatcher.js
ListenerObject.js
But now, the real deal in c# for creating an event and dispatching goes something like this:
Declare a delegate for the event. Note that I’ve created it outside the class declaration – this is so it’s available to any class:
public delegate void LoadComplete(float value);
public class MyClass
{
//...
}
Now, create the event in MyClass:
public delegate void LoadComplete(float value);
public class MyClass
{
public event LoadComplete LoadCompleteEvent;
}
To add a listener, you add a new delegate instance with the handler to the LoadCompleteEvent event:
public delegate void LoadComplete(float value);
public class MyClass
{
public event LoadComplete LoadCompleteEvent;
public void Start()
{
LoadCompleteEvent += new LoadComplete(HandleLoadComplete);
}
// note how the argument signature matches the delegate declaration
private void HandleLoadComplete(float someValue)
{
// event handled here
}
}
To send out the event, first check to make sure the event is not null (has listeners), then call it like a method:
public void DoSomething()
{
if( LoadCompleteEvent != null ) LoadCompleteEvent(1.0f);
}
To remove a listener, just remove the handler from the event:
LoadCompleteEvent -= HandleLoadComplete;
Coroutines (replacement for setTimout, setInterval, Timer object)
In AS3, we’re used to setTimeout() or the Timer object to deal with waiting and looping stuff. In the old days, we used onEnterFrame and I can still remember the arguments over which was better – setInterval or onEnterFrame. It was a ridiculous argument, because in reality, it just depended on *what* you were doing when that even fired. Please don’t leave comments regarding this tired and exhausted discussion which has zero relevance these days
In UnityScript, you’ll be introduced to WaitForSeconds() which is really nice when coming from AS3. In c#, however, you get a bit of a rude awakening. You begin to realize how much UnityScript lets you get away with. Its insane.
So, here are the basics.
If you want to loop or wait a certain amount of time, you have to call a method that returns IEnumerator:
public IEnumerator DoMyBidding() {...};
Then, you have to have some sort of yield/return statement in this method or the compiler with barf all over your keyboard:
public IEnumerator DoMyBidding()
{
yield return WaitForSeconds(1.5f);
if( myConditionNotMet ) yield return null; // this is how you just simply return out if you no longer wish to continue.
}
If you want to loop something:
public IEnumerator MonitorMe()
{
while ( conditionNotMet )
{
// do something
yield return WaitForSeconds(.025f);
}
}
To call these methods, you have to use StartCoroutine():
public void Start()
{
StartCoroutine( MonitorMe() );
}
Well, for now, that’ll get you started, and as I remember/think of things, I’ll come back and update this post with more tid-bits. Enjoy!