noobtuts

Unity Boo Tutorial

In this Tutorial we will learn how to use the Boo language to make games in Unity. If you are completely new to Boo, feel free to also read the BooPrimer.

Introduction

Let's talk about the Boo language really quick before we jump into the code. Boo is pretty much Python with types, based on .NET (just like C#). The big selling point of languages like Python and Boo is that we can solve problems with a clean syntax and fewer lines of code. Things like functional programming support, the colon operator (slicing) and ridiculously elegant code are the reason why Boo is awesome.

Creating a Boo Script in Unity

First things first. Unity 5 does still support Boo, but it removed the "Create Boo Script" option from the menu. So in order to create a Boo Script, we can either manually create a ".boo" file in our Assets folder or use a simple Editor Extension that allows us to create Boo Scripts again:
CreateBooScript.boo
Note: right click the link, select Save As.. and save it in a new Editor folder inside our Project's Assets folder.

Afterwards we can select Assets->Create->Boo Script from the top menu:
Create Boo Script
Note: our Editor Extension that allows us to create a Boo Script is actually written in Boo.

And then rename it to Test:
Test Script

Afterwards we will open the Script.

A MonoBehaviour Example

Let's create a simple MonoBehaviour class:

import UnityEngine

class Test(MonoBehaviour):
    def Start():
        Debug.Log("Started...")

Note: It's important to understand that Boo (just like Python) uses indentation instead of curly brackets.

After saving the Script, we can select the Main Camera and press Add Component->Scripts->Test in the Inspector. Afterwards we can see our Boo Script as component:
Test Script in Inspector

If we press Play then we can see our test message in the console:
Started message in Console

Accessing Components

We can access another component with GetComponent:

# Getting a Component
c = GetComponent[of Camera]()

# Accessing a Component's property
c.orthographic = true
GetComponent[of Camera]().orthographic = false

Note: the [of Type] construct is the equivalent of the <Type> construct in C#.

Using Variables and showing them in the Inspector

Here is how we can add a variable to our Test Script:

import UnityEngine

class Test(MonoBehaviour):
    n = 42
   
    def Start():
        Debug.Log("Started...")

We can (and often have to) define a type for it:

import UnityEngine

class Test(MonoBehaviour):
    n as int = 42
   
    def Start():
        Debug.Log("Started...")

We don't even have to assign a value yet:

import UnityEngine

class Test(MonoBehaviour):
    n as int
   
    def Start():
        Debug.Log("Started...")

We can also make it visible in the Inspector:

import UnityEngine

class Test(MonoBehaviour):
    # public makes it visible
    public n as int = 42
   
    # SerializeField makes it visible, too
    [SerializeField] m as int
   
    def Start():
        Debug.Log("Started...")

Here is how it looks:
N and M in Inspector

It also works for arrays:

import UnityEngine

class Test(MonoBehaviour):
    # An Array (can be seen in Inspector)
    public arr1 as (int)
    public arr2 = (0, 1, 2)
   
    def Start():
        Debug.Log("Started...")

Note: (int) means Array of int. Please note that we can't make Lists visible in the Inspector (yet).

Here is how it looks:
Array in Inspector

And a few more datatypes:

import UnityEngine

class Test(MonoBehaviour):
    # 16 bit integer
    a as short = 0

    # 32 bit integer
    b as int = 0
   
    # 32 bit floating point
    c as single = 0.0
   
    # 64 bit floating point
    d as double = 0.0
   
    # one byte
    e as byte = 0
   
    # a 2 byte utf-16 character
    f as char = 0
   
    # a string
    g as string = "test"
   
    # boolean
    h as bool = false
   
    def Start():
        Debug.Log("Started...")

Using Libraries

We can access Unity's libraries just like we do in C#. For example, here is how we can use Input:

h = Input.GetAxis("Horizontal")

And here is how we can use a function from a C# library:

r = Random.Range(0, 10)

For some libraries we have to use import (the equivalent to C#'s using):

import System.IO
File.Create("test.txt")

Class Member Access

We can use public, protected, private and static just like in C#:

public static def test():
    return 0

public def test2():
    return 0

Accessors

We can even use C#'s accessor construct:

v = Vector2.up
destination as Vector2:
    get:
        return v
    set:
        v = value

Creating a new GameObject

We don't have a new operator in Boo, but we can still create a new GameObject easily:

import UnityEngine

class Test(MonoBehaviour):    
    def Start():
        g = GameObject()

Using Function Parameters

Unlike Python, we actually have to use types most of the time. Let's say we want to use the OnCollisionEnter2D function. Here we always use a "variable as Type" construct:

import UnityEngine

class Test(MonoBehaviour):    
    def Start():
        g = GameObject()
   
    def OnCollisionEnter2D(coll as Collision2D):
        Debug.Log("CollisionEntered2D...")

For-Loops

We can use for to loop through each item in a list:

# loop through a list of items
for n in [1, 2, 3, 5, 7]:
    Debug.Log(n)

# equivalent to "for i = 0; i < 10; i++"
for i in range(10):
    Debug.Log(i)

Using

Just like in C#, we can use using to work with disposable objects:

using w = BinaryWriter(File.Open("test.txt", FileMode.Create)):
    w.Write(0)
    w.Write(1)

Callback Functions

Some of the Unity classes allow us to add our own callbacks so that Unity automatically calls them every few milliseconds.

One example is the EditorApplication.update Delegate. In C# it's as simple as this:

void MyUpdate() {
    // Do Stuff...
}
EditorApplication.update += MyUpdate;

It also works in Boo, but we have to use the Delegate.Combine function and some type casting:

import System

def MyUpdate():
    pass
   
EditorApplication.update = Delegate.Combine(EditorApplication.update, MyUpdate as EditorApplication.CallbackFunction) as EditorApplication.CallbackFunction

Boo Exclusive Features

Let's take a look at a few things that can't be done in C#.

Slicing aka the Colon Operator

Languages like Boo and Python are really good with lists. We can use slicing to extract parts of a list very easily:

l = ["a", "b", "c", "d"]
l[0] # => "a"
l[0:1] # => ["a"]
l[0:2] # => ["a", "b"]
l[1:3] # => ["b", "c"]
l[-1] # => "d"

List Comprehensions

List comprehensions are an incredibly powerful language construct that is often found in functional languages. It can be used to replace lots of for-loops and of course, Boo has it too:

# define a test list
l = [0, 1, 2, 3]

# increase each item
[x+1 for x as int in l] # => [1, 2, 3, 4]

# get all items that are < 2:
[x for x as int in l if x < 2] # => [0, 1]

# get all the even items:
[x for x as int in l if x % 2 == 0] # => [0, 2]

Higher Order Functions

While Boo doesn't come with many higher order functions by default, we can still define the 3 essential ones that are found in just about every functional programming language:

# apply a function to each item in a list
def map(fn as ICallable, coll):
    return [fn(x) for x in coll]

# apply a function to each two list entries recursively
def reduce(fn as ICallable, coll, initializer):
    for x in coll:
        initializer = fn(initializer, x)
    return initializer

# filter out items for which fn(x) returns true
def filter(fn as ICallable, coll):
    return [x for x in coll if fn(x)]

Those three functions are everything that we need to replace every long and ugly for-loop. Examples:

map({x as int|x+1}, [0, 1, 2, 3]) # => [1, 2, 3, 4]

reduce({x as int,y as int|x+y}, [0, 1, 2, 3], 0) # => 6

filter({x as int|x%2==0}, [0, 1, 2, 3]) # => [0, 2]

The { } constructs are lambdas, which are just functions defined in a short way. We can also use real functions if we want to:

def add(x as int, y as int):
    return x + y
reduce(add, [0, 1, 2, 3], 0) # => 6

We can even use reduce to create a useful sum function that sums up every element in a list:

def sum(lis):
    return reduce(add, lis)

sum([0, 1, 2, 3]) # => 6

Or the lambda version:

def sum(lis):
    return reduce({x as int, y as int | x+y}, lis, 0)

sum([0, 1, 2, 3]) # => 6

We only used a few very simple examples with numbers, but it's important to understand that higher order functions are a huge selling point for Boo and any other programming language that supports them. As mentioned above, we can replace every for-loop with map, reduce, filter and function compositions of them. This is what makes our source code so much more elegant and short.

Summary

We just learned how to use Boo to interact with Unity and we got a first impression of the ridiculously elegant syntax.

But why Boo?

There are lots of reasons why Boo is a great option for game development. We use a game engine like Unity because we want to focus on developing the game instead of developing the engine. It's the same concept that will make you fall in love with Boo. The language makes our code shorter, more elegant and allows us to focus on the game instead of having to write lots and lots of ugly code.

Note: also if you are into functional programming concepts, then Boo is the obvious choice when making games in Unity.