Head First into Unity with UnityScript

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
(It's String (Mono's String class) and not string: Added link to Mono's docs on string functions)
(Each .js File Implements A Class (By Default))
Line 205: Line 205:
  
 
=== Each .js File Implements A Class (By Default) ===
 
=== Each .js File Implements A Class (By Default) ===
 +
 +
In general, sticking a bunch of variable and function definitions such as:
 +
 +
var x : int;
 +
function y(){}
 +
 +
Is exactly equivalent to putting something like this in the same file:
 +
 +
<javascript>class Foo extends MonoBehaviour {
 +
  var x : int;
 +
  function y(){}
 +
}</javascript>
 +
 +
But, you can also declare more than one class in the same file, which is particularly useful where you need utility classes (or "structs") which aren't descended from MonoBehaviour.
 +
 +
e.g.
 +
 +
<javascript>class ButtonState {
 +
  var currentState : int;
 +
  var offset : Vector2;
 +
}</javascript>
 +
 +
If you do something like declare a subclass of MonoBehaviour in a file with a mismatched name (including different capitalization) you're asking for trouble.
  
 
'''It's important to understand that when you write a behavior script in JavaScript you are actually writing a class implementation''', where:
 
'''It's important to understand that when you write a behavior script in JavaScript you are actually writing a class implementation''', where:
Line 237: Line 260:
 
   // this is a global function. Other scripts in the same scene can call foo.FooBar();
 
   // this is a global function. Other scripts in the same scene can call foo.FooBar();
 
}</javascript>
 
}</javascript>
 +
 +
'''Calling Inherited Methods'''
 +
 +
'''super()''' is the inherited constructor, and '''super''' is "this" treated as a member of the superclass (so super.foo() calls the superclass's version of foo).
  
 
=== Some Early Versions of Unity Do Not Support switch() or eval() ===
 
=== Some Early Versions of Unity Do Not Support switch() or eval() ===

Revision as of 04:26, 5 October 2010

Written by Tonio Loewald (a.k.a. podperson)

Note: this tutorial assumes you know the basics of JavaScript and programming. We're not going to go over all that stuff from scratch. If you don't know anything at all about programming, well ... you should probably get a book oriented at learning to program, or take a class. The fundamentals of programming are a major topic in and of themselves.

Contents

Unity's "JavaScript" vs. the JavaScript you probably know

It's Fast

Unity JavaScript is compiled (and fast, which is excellent) but not so dynamic as JavaScript in browsers (which is interpreted).

Seriously, there is no speed difference between JavaScript, C#, and Boo (in Unity, that is). There are other pros and cons to each, but not speed.

Note: if you've been following recent developments in the browser wars you'll know that this statement is not strictly true. JavaScript is in fact JIT compiled on all modern browsers, and rather impressively fast on some (FireFox, Safari, and Opera). Even so it doesn't support strict typing (even as an option) which is very costly for performance (every time you perform arithmetic operations you need to check what your operands are and convert them if necessary). If the ECMAScript standard is modified (as Adobe has lobbied for) to allow for explicit type declarations, it's possible that appropriately written JavaScript will be able to execute an order of magnitude faster.

Even so, as things stand, "real" JavaScript is still about two orders of magnitude slower than Unity's JavaScript, even on Squirrelfish Extreme.

Subclassing is Less Annoying than Real JavaScript

There's no messing around with _prototype in Unity JavaScript. You simply do this:

Here's a class:

<javascript>// Foo.js var foo = "hello, world"

function doEet () {

 // does nothing, intended to be overridden

}</javascript>

Here's a subclass:

<javascript>// PrintingFoo.js class PrintingFoo extends Foo {

  function doEet() {
   print( foo );
 }

}</javascript>


Virtual functions can be used to override functions

In Unity's javascript you can create virtual functions.

<javascript> class Foo {

    virtual function DoSomething () 
    {
        Debug.Log("from base class");
    }

}

//SubFoo.js class SubFoo extends Foo {

    virtual function DoSomething() 
    {
         Debug.Log("from sub class");
    }

}

//Elsewhere var foo : Foo = new SubFoo(); foo.DoSomething();//prints from sub class</javascript>

If you want to call the base class's method then you can use the keyword super. We could get SubFoo to call the function in Foo by writing:

<javascript> class SubFoo extends Foo {

    virtual function DoSomething()
    {
         super.DoSomething();
         Debug.Log("from sub class");
    }

}

//Elsewhere var foo : Foo = new SubFoo(); foo.DoSomething();//prints "from base class" and "from sub class"</javascript>

Consider Writing Mixins and Helpers Instead of Subclassing

It's very easy to write classes that know about one another and cooperate, and this is probably a better and more maintainable way of specializing objects in Unity than subclassing.

E.g.

<javascript>/* Foo.js */ var bar : Bar;

function Start(){

 bar = gameObject.GetComponent(Bar);

}

function doEet(){

 // do my own thing
 if( bar ){
   bar.doEet();
 }

}

/* Bar.js */

 function doEet(){
   // do something special
 }

</javascript>

It's String (Mono's String class) and not string

var x : String;

Most of the string functionality you know and love from JavaScript is still there, but it's differently capitalized.


A note on how to "Split" Strings

You have to use the following syntax for splitting with characters : <javascript> var qualifiedName = "System.Integer myInt";

var name = qualifiedName.Split(" "[0]); </javascript> name[1] will contain "myInt" after the split.


For a list of available string functions, see Mono's documentation on the String class (http://go-mono.com/docs/monodoc.ashx?link=T%3aSystem.String%2f*)

Strings Are Always Denoted With Double Quotes and Always Interpolated

In Unity, Strings are denoted with double quote '"' characters. Single quotes are not allowed. (This is a very nasty adjustment for long-time JavaScript hackers, where single quotes are more efficient than double -- but it makes sense in Unity because the code is compiled, eliminating the need for strings that don't get preprocessed.)

(Interpolated is just a fancy way of saying "\n" gets converted to a newline, etc.)

<javascript>var a = 'fred'; // works in JavaScript, error in Unity var b = 'fred\n'; // does NOT convert the \n to a newline character</javascript>

<javascript>var a = "fred"; // good in both Unity and JavaScript, no performance hit in Unity</javascript>

You Must Declare Variables Before Using Them

You must declare variables before using them. You can, and generally should, explicitly declare variables as having types (helps code to run faster, detects some errors at compile-time -- which is excellent, and others at run time -- which is less excellent).

<javascript>a = "fred"; // works in JavaScript, error in Unity</javascript>

<javascript>var a = "fred"; // a is now a string variable containing 'fred' var b: String; // b is now a string variable, with no assigned value b = "wilma"; var c; // c is now a dynamically typed variable with no assigned value c = "barney"; c = 17;</javascript>

a) You can (and often should) explicitly scope variables as private, static, etc.

b) Unity will implicitly type a variable if you assign it a value when you declare it. So:

<javascript>var a = "fred"; // a is now of type String a = 5; // ERROR! -- a is a String var b : String = "fred"; // redundant</javascript>

But: <javascript>var a; // a is dynamically typed; a = "fred"; // works a = 5; // works</javascript>

Method (and Class) Names are Generally Capitalized

Method (and class) names are generally capitalized, except when they aren't. (It's confusing.) Basically, Unity's JavaScript is living in a .NET naming convention world (where methods are CamelCase and properties are camelCase), but is also trying to be like JavaScript (which, like C, is strongly biased towards lowercase and camelCase for everything).

e.g. in JavaScript typeof("fred") == 'string', but in Unity the type you write var a: String;

Lots More Types, Two Kinds of Array, No Object Syntax Sugar

JavaScript has, in essence, three types: number, string, and Object (with functions and arrays in essence being Objects). Unity's JavaScript has many more types, including:

Objects, which are NOT interchangeable with arrays, or Arrays (which are somewhat like JavaScript's objects, but not dynamic): <javascript>var a = new Object(); // works a.fred = "wilma"; // runtime exception!</javascript>

native arrays (which are not associative or dynamic): <javascript>var a = [1, 2, 3]; a.Push(4); // ERROR -- won't work!</javascript>

If you want to make an array of a specific type: <javascript>public var friendsOfCarlotta : Transform[];</javascript>

Mono Arrays (which are associative and dynamic, but not with the same syntax sugar as Objects): <javascript>var a = new Array(); a.Push(4); // This works</javascript>

You can convert Mono Arrays to native arrays (which are faster but less flexible) using ToBuiltIn(ArrayType), e.g.:

<javascript>var a = new Array(); a.Push(1); a.Push(3.1415926535); a.Push(17); var b = a.ToBuiltin(float);</javascript>

integer types (including int, uint32, etc.)

float

And Unity's many built-in classes (e.g. Vector3)

a) Unity's String class lacks many of the nicer features of JavaScript's strings.

b) Unity's internal arrays are far less flexible than JavaScript's arrays or objects. In general, you'll probably want to use Mono's Array object if you want flexibility, and Unity's internal arrays if you want performance.

Each .js File Implements A Class (By Default)

In general, sticking a bunch of variable and function definitions such as:

var x : int; function y(){}

Is exactly equivalent to putting something like this in the same file:

<javascript>class Foo extends MonoBehaviour {

 var x : int;
 function y(){}

}</javascript>

But, you can also declare more than one class in the same file, which is particularly useful where you need utility classes (or "structs") which aren't descended from MonoBehaviour.

e.g.

<javascript>class ButtonState {

 var currentState : int;
 var offset : Vector2;

}</javascript>

If you do something like declare a subclass of MonoBehaviour in a file with a mismatched name (including different capitalization) you're asking for trouble.

It's important to understand that when you write a behavior script in JavaScript you are actually writing a class implementation, where:

a) The name of the class is the name of the script file (so if it's foo.js you can instance it elsewhere by saying var x = new foo()).

b) Certain "magic" method names will in fact implement event handlers (e.g. Start(), FixedUpdate() etc.). In any event, a function declaration is a method of the class you've written.

c) Code written outside function definitions inside your file are executing in the class's body. Variables declared in it are members of the class.

d) static functions and variables in a class are, in essence, class functions and variables.

This is all FAR more elegant than implementing classes in "real" JavaScript, but also somewhat restrictive ... mostly in a good way (you can't arbitrarily wire objects together the way you can in "real" JavaScript).

For example, if I create a new behavior and name it foo, the file will be named foo.js. Let's suppose foo.js looks like this: <javascript>public name : String; // when you drag the behavior onto a gameobject, these values will be visible and editable public age : int; // other scripts which have a reference to this object (e.g. if they're attached to the same object) can see public functions private favoriteColor : Color; // private members are NOT visible to other scripts, even if they have a reference to this object public bestFriend : foo; // you can assign a value to bestFriend by dragging a gameObject with an attached copy of the foo behavior to this property. This will give you access to bestFriend's public methods and members static faction : String; // static properties are visible globally, so another script can look at foo.faction

function Update(){

 // this function will be called every frame by Unity, so it's actually an event handler
 var t = transform; // transform is a property inherited from the gameObject the behavior is attached to

}

function Bar(){

 // this is just a function, if you don't call it yourself, it will never do anything

}

static function FooBar(){

 // this is a global function. Other scripts in the same scene can call foo.FooBar();

}</javascript>

Calling Inherited Methods

super() is the inherited constructor, and super is "this" treated as a member of the superclass (so super.foo() calls the superclass's version of foo).

Some Early Versions of Unity Do Not Support switch() or eval()

Some early versions of Unity did not support JavaScript switch() statements, but both Unity 1.6 and Unity 2.0 do.

a) Unity 1.x does not support eval, but Unity 2.x does -- possibly only in the dev environment (?). Doesn't matter -- Don't use eval.

Switch statements do not run on

In real JavaScript, as in C, switch statements run on unless you insert break statements. In Unity each case needs its own break.

<javascript>switch(x){

 case 0: // error in Unity, you need a break before the next case
 case 1:
   // does the same thing for 0 and 1
   break;
 case 2:
   // does something different for 2
   break;

}</javascript>

Semicolons Are Not Optional

Semicolons are generally optional in JavaScript (which has some ferocious logic to determine when a statement ends) but very much not optional in Unity.

<javascript>var foo = 3 // OK in JavaScript but an error in Unity foo += 17</javascript>

No Anonymous Functions

You can't define functions quite as freely in Unity.

<javascript>function foo(){} // OK in both var bar = function(){} // error in Unity</javascript>

Note that most of JavaScript's other niceness w.r.t. functions is retained, e.g.

<javascript>function foo(x){ print(x); } var bar = foo; bar("test"); // works just fine in Unity</javascript>

Math is Mathf; Math.abs is Mathf.Abs

JavaScript's annoying Math library (because, really, stuff like abs() should just be in the language) becomes Unity's (also annoying) Mathf library. And remember, method names are usually capitalized (in Unity) so Math.abs() becomes Mathf.Abs().

Using Mono (.NET)

The JavaScript runtime environment makes extensive use of Mono (the open source clone of .NET). In fact, JavaScript is implemented in Boo, which is a language that runs on the Mono virtual machine and compiles to its native code. Many of a typical JavaScript runtime environment (e.g. String and Math libraries) are provided by Mono. You can tell when you're using Mono classes because their capitalization convention is CamelCase for class names (e.g. String) and methods (e.g. Mathf.Sin()) but camelCase for properties vs. JavaScript's which is camelCase for everything.

In order to use the Mono libraries you'll need to import them, e.g.

<javascript>import System; import System.IO;</javascript>

To find documentation on the libraries, the best place is probably http://www.go-mono.com/docs/

Mono Types

When a Mono function requires a char as an input, you can obtain one by simply indexing a string. E.g. if you wanted to pass the lowercase a as a char, you'd write:

<javascript>"a"[0]</javascript>

E.g. when using the String.Replace() function, you would use:

<javascript>var s : String = "Whatever_it_may_be"; s = s.Replace("_"[0], " "[0]); // replace all the underscores with spaces</javascript>

When working with Mono Arrays (which are more flexible than builtins) you can easily convert them to the faster builtin arrays using ToBuiltIn(Type):

<javascript>fastArray : SomeType[] = monoArray.ToBuiltin(SomeType);</javascript>

Finding Class Documentation

When you're looking at the documentation on the go-mono site, be aware that a huge amount of the stuff you're likely to care about lives under Class Library/System.

Debugging

Scripting errors will show in Unity as a "red x icon" in the window status bar. Click on the icon to bring up the console, showing a list of errors, which should be both informative and lead you to the line in the script that caused the problem.

Unity also generates useful warnings (with a "blue ! icon") e.g. telling you a variable you've declared isn't used anywhere. Striving to write code that never generates warnings is a very useful habit.

The print() function will produce messages in the status bar and console. You can also use Log("insert message here"); and Debug.Log("insert message here");

Debug.Break(); pauses the game in the editor at that exact point. It's very useful if you want to examine the state of objects when a particular situation occurs (e.g. you're trying to figure out what's going wrong when a particular object collides with another).

Although Unity does not have a conventional "stop, watch, and step" debugger, the editing GUI is completely live when running projects in the development environment (e.g. instances created at runtime appear in the browsers and you can click on them and look at their internal state).

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox