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:
Note: our Editor Extension that allows us to create a Boo Script is actually written in Boo.
And then rename it to Test:
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:
If we press Play then we can see our test message in the 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:
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:
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.