Skip to content

31. A lesson from the roller coaster demo

April 12, 2013

I probably shouldn’t have done this, but I had a look at the roller coaster demo code to see how much of it I could figure out. As I expected, the math is fearsome, but I picked up some interesting things to share with you. The first one – using a table of functions instead of a class, was so interesting, that it takes up all of this post.

Tables of classes

The first interesting thing is the Colour tab. It is not a class, but a table of functions. This is a very powerful concept, so let’s explore it. First we’ll look at how it works, then we’ll ask the question – when would you do this instead of using a class? And why do it at all?

Colour = {} --defines a table Colours

Colour.ModifyAlpha = false  --adds an item to the table

function Colour.blend(cc,t,c) --adds a function to the table
    --code here
end

First, we define a table as normal. We can add properties to it, like ModifyAlpha above (if this were a class, we would be writing self.ModifyAlpha in the init function). It’s cool that you can add properties outside of functions.

Then we can add functions, like blend above, because tables can contain anything (except nil). Below is how we might use that function in our code – note how we use a dot, not a colon, in Colour.blend. I’ll explain a little later.

function Test()
    c = Colour.blend(color1,0.5,color2)
end

The advantage of this “table” approach is that all the colour code is contained in its own table, whether it be constants like ModifyAlpha, or functions like blend. It was written so it could be dropped into any project without clashing with the existing code. Now imagine if you hadn’t put this code into a table, and you include it in a project which already has a blend function. You have a nasty conflict and a possible mess. By wrapping it up in its own table, you seal it off from any other code. And it is extremely simple to do.

So now we have an alternative to classes. But when should you use each of them? Let’s take two situations to illustrate.

Standard function library
Suppose we write a function library that does complicated calculations. Every function takes some parameters and returns a result. The library

  • doesn’t need to “remember” anything because each time you call a function, you provide everything it needs.
  • is unique, ie there is only one copy
  • doesn’t belong to any particular object
  • behaves exactly the same no matter who or what is calling it

.
The table approach above suits this library very well. To see why, let’s look at a different example.

Individual function libraries
Suppose we want to bounce some balls around the screen. Although they will be much the same, and they will all behave the same way in terms of bounciness and gravity, they might each have their own colour, and they will each need to know their x,y position, angle and speed at all times.

So now we need a set of functions that describe how a single ball behaves, and then we need to get it to manage each ball. We could do this with a table of functions as above, but we would have to hold a table of ball positions, velocities etc in our Main tab, and we would be continually passing through all this data to the functions in our library, because the library wouldn’t be remembering anything.

It makes much more sense to store the data for each ball in its own separate copy of the library, then our code is far cleaner. So we create a ball class, and make a copy for each ball and send through the data for that ball.

Any time we want to do anything with a ball, we call the function we want, using a colon – eg ball[3]:draw() – so that the class knows which ball is talking to it (remember from my posts 6&7, when you use a colon, all you are doing is passing through a hidden variable “self” that lets Codea know which ball is calling).

So when should you use tables or classes?

So we can sum it up like this –

  • if your function library needs to know who is calling it, because it is storing information for multiple objects, then you use a class
  • If your function library doesn’t store data for objects, and doesn’t care who is calling, because the function parameters give it everything it needs, then a table of functions works fine

.
To illustrate the amazing flexibility of Codea, you can mix classes and tables together. If you really want to understand this, have a look at the example below, where there are two volume functions,

  • volume defined with a colon
  • volume2, defined with a dot

Neither function uses self. All the function needs to know is radius.

Ball=class{}
    function Ball:volume(r) --colon
        return 4/3*math.pi*r*r*r
    end
    function Ball.volume2(r) --dot
       return 4/3*math.pi*r*r*r
    end

--Main
function setup()
    b1=Ball()
    --now do some test prints to see what happens
    print('1.colon',b1:volume(10)) --success
    print('2.dot',b1.volume2(10)) --success 
    print('3.dot+no self',b1.volume(10)) --error
    print('4.colon+self(first)',b1:volume(nil,10))--error
    print('5.colon+self(last)',b1:volume(10,nil))--success 
end

Taking the print lines in order

  1. call volume with a colon, normally – success

  2. call volume2, with the dot – success

So – I can mix dot and colon functions in a class, but being mischievous, I want to know if I can call volume (defined with a colon) using a dot – so the remaining print lines are experiments to try to understand how Code deals with this.

  1. call volume with a dot instead of a colon. Error! The reason is that Codea is expecting a “self” variable (the volume function has a colon, remember), and allocated my parameter 10 to self, leaving nothing for r, so the error was that r was nil.

  2. ok, let’s include a dummy self variable to keep Codea happy. Apparently, self is included as the first variable, so we’ll include a nil variable before our radius. Error! Our radius is still nil. Hmm

  3. Let’s swap the variables around and our radius first, and the dummy self variable second. Success!

So the result is that you can mix dot and colon functions in a class, and even call colon functions with dots, as long as you’re very careful how you use them.

I don’t know about you, but I learned something from all this, especially the usefulness of tables for storing function libraries. And I’ve only scratched the surface of the roller coaster.

Advertisement

From → Programming

3 Comments
  1. Briarfox permalink

    Thanks CoolCodea! I recently started using a table to hold my custom library function this helped resolve naming conflicts. I have a question for you. I’ve bee writing my functions in my table differently.

    I use:
    hlib = {}
    hlib.testFunction = function(x)
    print(x)
    end

    whats the difference from your method above? As in:

    hlib = {}
    function hlib.testFunction(x)
    print(x)
    end

    Thanks

    • No real difference as far as I know, except I think the method used in the roller coaster is a little easier to read

Trackbacks & Pingbacks

  1. 85. A practical example showing the value of classes | coolcodea

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: