Skip to content
Tags

7. Classes in Codea (what is this ‘self’ thing?)

March 22, 2013

This is about classes, and especially about how they differ in Codea.

Basics

If you are used to classes, skip down to the next section. There is stuff there you need to know.

Once you start programming, you soon find you’re using multiple copies of the same thing, whether it’s stars, spaceships or balls. Each of them may be the same or very similar, but if they’re scattered all over your screen, then you want an efficient way of keeping track of them.

This is what classes are good at – managing particular objects or activities or tasks. And the code in a class keeps to itself and doesn’t interfere with the main program or other classes, which reduces bugs. There isn’t space for me to explain classes fully, because this blog is about Codea, and there are thousands of web pages that explain them, already, but see if you can follow the rest of this post.

Classes in Codea

Suppose I want to draw a lot of balls of different colors that will move across the screen, and I decide to set up a class to manage them. To keep things clean (but this is not essential), I ask Codea for a new tab (plus sign at top right) and select a class and give it the name Ball. I get a new tab with this code:

Ball = class()

function Ball:init(x)
    -- you can accept and set parameters here
    self.x = x
end

function Ball:draw()
    -- Codea does not automatically call this method
end

function Ball:touched(touch)
    -- Codea does not automatically call this method
end

The first things to note are that

  • the class announces itself to Codea at the top
  • each function is preceded by Ball:
  • there is no need to tell Codea where the class ends
  • there are a couple of functions that tell you they won’t be called by Codea
  • the class code is in its own tab purely for your editing convenience*
  • it wouldn’t matter if all the code for your whole project was in one big tab

And the explanation for all of this is that once Codea knows the class exists, it treats any functions preceded by the class name as belonging to that class. So a function called Fred:draw() belongs to class Fred, no matter where you’ve put in in your code. In fact, you could shuffle up all your functions in a random order, and Code could still figure out what belonged where.

Note 1: make sure all your class functions have the class name as a prefix!

So let’s get to work. Assume the user will tell us how big to make the ball, the starting x and y position, and color. We’ll worry about movement later, baby steps first.

A class is like the main program, in that there is a setup function (called “init”) that runs when the class is created, and then a bunch of functions that do stuff when asked. The only function we know absolutely will be run is “init”.

The main program will ask the class to set up one ball at a time, so our class only ever deals with ONE ball at a time.

The size, initial x,y position and color are all things that are set up just once initially, and that’s what the init function is for, so let’s put them in there, like so

--d = diameter
-- x,y = initial x,y position
-- c = a color
function Ball:init(d,x,y,c)
    self.diameter=d
    self.x = x
    self.y=y
    self.colr=c
end

Wait a minute, what’s this self stuff? That really confused me for a while. The problem is that Lua usually only has two options for storing information:

diameter = d
local diameter=d

The first one is global, ie every time you set it, it overwrites any previous values, anywhere in the entire project. That’s not much good if we want to store details for lots of balls!

The second one is local to the function or loop we’re in, which means that if we want to use the diameter later in another function, it will be gone. That’s no good, either.

What we need is a way to store values not just for our ball class as a whole, but for a particular ball on its own, and that’s what “self” does. Effectively it says, this value applies to this ball only, and we will be able to use it in other functions, as you will see. If that still isn’t clear, read on..

How do we create the balls? Let’s go back to the main setup function, and do it there, like so

function setup()
    --set up two balls
    b1=Ball(100,50,300,color(128,128,128,255))
    b2=Ball(150,75,400,color(200,125,45,200))
end

So what we’ve done is created two “balls”, and labelled them b1 and b2. Any time we want to do anything with them, we refer to b1 and b2. We can check that they’ve stored the values we gave them, like this

print(b1.diameter)

and it should print out 100. What happens is that Codea realises that b1 belongs to Ball, and looks up diameter for the ‘self’ that is b1.

To finish explaining ‘self’, if you were programming a banking application, you would have a customer ID that would be used in all sorts of places, and you’d give it a variable name like ID in your code. Similarly ‘self’ is just a variable name for whatever ball happens to be using the Ball class.

You can also see a class as being like a storage locker, eg at a fitness centre, and our balls are people wanting to store clothes. Each person comes along, hands over their clothes and is given an id tag. Later, they can come back, show their id tag, and get their clothes back. So when we write b1 = Ball(…), we are handing over variable values to the class, which gives us back a unique id number, which is stored in b1. Note that b1 does not store all the class details – just the id number!

Getting back to our example, so far we are just using the Ball class to store a few details about a couple of balls.

Now we want to draw them on the screen, so let’s do that that in the main draw function.

function draw()
    background(195, 195, 201, 255)
    pushStyle() -- store style settings
    fill(b1.colr) --set color for b1
    ellipse(b1.x,b1.y,b1.diameter) --draw b1
    fill(b2.colr)
    ellipse(b2.x,b2.y,b2.diameter)
    popStyle() -- put back style settings
end

You can see it’s going to get tedious if we have to draw any more balls. We have a couple of options. One is to put the balls in a table, like this…

Creating them..

b={}
b[1]=Ball(100,50,300,color(128,128,128,255))
b[2]=Ball(150,75,400,color(200,125,45,200))

and drawing them..

for i=1,#b do
    fill(b[i].colr)
    ellipse(b[i].x,b[i].y,b[i].diameter)
end

but there is another way – the ball class can handle it, like this:

Ball:draw()
    pushStyle() -- store style settings
    fill(self.colr) --set color
    ellipse(self.x,self.y,self.diameter)
    popStyle() -- put back style settings
end

Note that because this function is within our class, we use the “self” properties we stored earlier. When the drawing occurs later, we will need to do it separately for each ball, and Codea will look up the correct values for whichever ‘self’ applies. This is like our storage locker example, with people showing their tags to get their clothes back.

However, Codea is not going to call this function by itself. It only calls the main draw function. So what we need to do in the main draw function is this (assuming we have a table holding balls)

function draw()
   for i=1,#b do
       b[i]:draw()
   end
end

Some key points

There are a few really important things to note here.

First, if you’re thinking this looks more complicated, and why would you bother – well, wait and see when we get the Ball class to do more stuff.

Second is that we call the Ball’s draw routine for each ball by prefixing it with the ball object (ie array item), ie b1:draw() will draw ball b1. This is how Ball knows which ball we want to draw, and which ‘self’ we are talking about. (It is valid to write Ball:draw(), but then you won’t be referring to any particular ball, the “self” keyword will be nil and you’ll get errors). So if you call any Ball functions by prefixing them with a particular ball object, then the class will work with that ball’s “self” data.

Another important point is that we put a colon between b[i] and draw, not a dot. If you get it wrong, you’ll get an unclear error and puzzle over it.

Don’t forget the colon, any time you’re calling a class function!

See bottom for for more detail that will make things clearer.

Here is something else that is just as important. Suppose that for some reason, drawing the ball was complicated, and we had set up a function to help us within the Ball class, called Ball:drawEllipse(). It doesn’t matter what it does, I’m focussing on how you use it.

So we’d have something like this

Ball:draw()
    pushStyle() -- store style settings
    fill(self.colr) --set color
    Ball:drawEllipse() ------------ this is the new line we've added
    popStyle() -- put back style settings
end

This will not work properly. Remember that when you call Ball:draw() from Main, you don’t say Ball:draw(), but b:draw(), where b is the particular ball you want to draw. In the same way, when you are in Ball:draw, trying to draw that same ball, you need to tell drawEllipse which ball to draw. Because you are inside the Ball class, you can use self as the ball identified. So the correct line of code is

self:drawEllipse()

Next time we’ll make the Ball class more interesting, and I’ll provide full code at the end.

Addendum – a bit more detail on using colons with classes.

There is a logical reason for using a colon when calling a class function. Please try to understand this, because it will make things so much clearer.

When we call a function that is in a class, and we use a dot, eg b.doSomething(), I might expect the class to be told that b (one particular ball) is calling it. But Lua, the language behind Codea, doesn’t work like that. It doesn’t care that it could pass through some details about b if it wanted. Instead, doSomething won’t know which ball is talking to it, so any statements using “self” will cause an error. (If doSomething doesn’t need any “self” info, the function will probably work fine).

There is a way you could get around this, by passing the name of your ball as a parameter in the function. So this: b.doSomething(b) will allow the function to use all the “self” info, because now it knows which ball is calling it.

And now we come to the colon. Because it is useful to tell a class who is calling it, Lua provides a shortcut. If you use a colon, Lia secretly includes the caller’s name as the first parameter – just as we did in the previous paragraph.

So b.doSomething(b) is exactly the same as b:doSomething() , and the colon is simply a signal to Codea to provide the caller’s name for us. As one writer put it

self is not a keyword. It’s not magical. It’s not managed by the language. It’s just the name of the first argument passed to your function.

Remember I said that in the ball example, b1 only stored an id number and nothing else. The colon tells Codea to pass that id number through as the first parameter, and this is how the class knows which ball is talking to it. And any time you write self in your class code, you are saying, get the value you stored for the id number I have passed to you.

As a final example, suppose we wrote a little class function to provide the current x value of the ball

function Ball:Get_x()
    return self.x
end

This is roughly what happens behind the scenes. When you call the function using a colon, the id number is passed through to the class, and it uses the id number to look up the x value in its data table.

function Ball.Get_x(id)
    return Data.x[id]
end

The little program below shows the use of the colon. The last print statement will give an error, because using a dot without providing the object name means that any references to self will crash the program.

function setup()

    a=test(10)  --create a class variable a and store x=10
    print('colon',a:getX()) --correct Codea approach with colon
    print('dot',a.getX(a)) --alternative with 'dot', including a as parameter
    print('dot, no param',a.getX()) -- just use dot, no parameters - will crash
end

test=class() -- little class that just sets value of x

function test:init(x)
    self.x=x
end

function test:getX() -- return x value
    return self.x
end

 

 

 

From → Programming

8 Comments
  1. MST permalink

    Excellent post.
    Seeing the different approaches is very helpful.
    Tnx!

  2. John permalink

    How would you print out the diameter of the ball once it is in a table?

  3. Anonymous permalink

    I am so thankful this post happened. I will be looking through more no doubt. Great explanation.

Trackbacks & Pingbacks

  1. 85. A practical example showing the value of classes | coolcodea
  2. 169. Why tables and classes are so useful | coolcodea

Leave a comment