Codea Game - 1
I’m beginning to work on an application in Codea for iPad. Codea is a lovely implementation of Lua for the iPad. You can build real apps that do things, and even upload them somehow, convert them to Xcode, and deploy them as real iPad apps. The very enjoyable game CargoBot was written entirely in Codea.
My app idea is to have a little iPad application that models interesting things about a team’s practices, and shows what happens under those circumstances. Chet and I plan to use it in talks and classes, in the same way we use the Excel graphs in our justly famous Small Card Game. Besides, Codea is quite nice and it seems like a fun thing to work on for a while. I thought you might like to follow along and see what I do.
Lua has a few unit testing frameworks, but it is quite difficult to import code into Codea, and anyway when I’m learning I like to stay close to the metal. You can access Lua’s internal tables, so a kind of reflection seems to be possible, which suggests we may be able to build a little testing framework in due time. Another issue is that the application is mostly graphical in nature. I’m not at all sure how to automate that kind of testing. Perhaps I’ll figure it out as we do forward. If not, we’ll see what happens.
Part of the app I have in mind will be a display of one or more graphs showing what is happening. I plan to start there. The basic idea is that there will be a frame on the iPad screen that will show the graph. I’m not sure where this will be, or how big it will be, so I’m going to build a few tiny little windowing objects.
This sounds a bit too much like infrastructure to make me happy, but since the main value of the application will be to display these graphs, it’s not far from a spike through the entire app. In the next few hours of work, I plan to get a small simulator object calculating some velocity measure and displaying its graph. Since displaying things is the bulk of the app, and since I really only expect a few hours of work before something interesting is going on, and since I need to get Codea’s graphics model understood, I decided to start here. I’ll try not to over-generalize or anything bad like that.
If you can build the infrastructure you need in a few hours, I’ll OK it. It’s weeks and months on infrastructure that bugs me. Don’t do that.
Codea graphics has basic transformation matrices, so it can translate,scale, clip, and so on. I’ll use those to make a Frame that knows how to draw itself, and then draws anything it contains, which will be the simulator objects, or graph objects they deliver, or something. My guess is the Frame will hold the simulator objects directly, but we’ll see what the code tells us.
Because I don’t have any tests yet, and because this app is very graphical, and because it’s what I do anyway, I’ll be proceeding in tiny steps, at least at first. Probably I’ll get confident soon and do some big step and fall off a cliff. Wait and see. We’ll begin with Codea’s starting Main file, with one line added. Take a look and I’ll talk below about how this works.
-- Game Spike
-- Use this function to perform your initial setup
function setup()
end
-- This function gets called once every frame function draw() -- This sets a dark background color background(40, 40, 50)
-- This sets the line thickness strokeWidth(5)
-- Do your drawing here
rect(10, 10, 200, 150)
end
The Codea Main has just three parts. Two of them are shown here. There is a setup() function, where you define any objects or data you want to work on. There is a touched() function, not shown here, that is triggered if someone touches the screen. And there is a draw() function. Codea runs your setup() once, and then calls draw() 60 times per second. In your draw() function, you do whatever calculations are needed to draw your objects, make them appear to move around, and so on. Codea double-buffers your screen, so you can do rather smooth animation with little difficulty.
I added the call to the rect() function, which just draws a rectangle starting at (10,10), 200 wide and 150 high. This rectangle is drawn 60 times a second, since nothing else is happening. We don’t mind that. It works fine.
That was the simplest step I could think of toward a frame object: just draw something. Now let’s move on. I’ll create a new class, Frame, and just make it draw the rectangle. I’ll move the rectangle to a new position, just to be sure it’s really working.
Frame = class()
function Frame:init() end
function Frame:draw()
rect(30, 50, 200, 150)
end
Codea generated the class, and I put in the rect call, at a new location. I also have to change the main program, so that it will create a Frame and forward draw() to it. At this moment, I’m not sure just how Frame = class() works, but then I wasn’t sure how new foo = new Foo(); worked either. Maybe I’m still not sure, come to think of it. For now, it’s a magic incantation that sets up a class. We’re learning the language. Later on we may dig into how it works.
-- Game Spike
-- Use this function to perform your initial setup
function setup()
frame = Frame()
end
-- This function gets called once every frame
function draw()
background(40, 40, 50)
strokeWidth(5)
frame.draw()
end
I took out the boilerplate comments, as they have done their job, giving me some hints. We build a Frame object called frame, and in the draw() function, we tell the frame to draw itself. This works as well, drawing the frame in the new location.
Could I have built all this in one go? Sure, I’m a smart guy. But this way I have a better chance of finding out if there is anything wrong. Not a perfect chance. There is something wrong already but it doesn’t show up. It soon will.
Now, the Frame needs to begin to do its job. A Frame should have a defined offset and size, within which it will draw its children. i think for now I’ll have it draw its own rectangle, then draw children inside, so I’ll start there.
First we add x, y, width, height to frame, and draw the frame at those values manually …
Frame = class()
function Frame:init(x, y, width, height) self.x = x self.y = y self.height = height self.width = width end
function Frame:draw()
rect(self.x, self.y, self.width, self.height)
end
This is pretty straightforward. We add the initialization values to the Frame creation call (not shown), in init() we store them in the self instance, and we use them in the draw() when that is called. Let me say a word or two more about that. Lua (the Codea language) doesn’t really have classes. I’m building this code by rote, having not looked in detail at how classes work. Basically, though, the class() function returns something, probably a function or a table. I’ll look it up someday soon. That function, Frame() knows to construct an instance structure of some kind, probably a table, and give it a self variable.
The lines like function Frame:init(…) build functions inside the Frame, and when the instance structure is accessed, the appropriate function will be called. As I say, right now I’m just doing this by rote. It appears to me that when we write frame.x, what happens is that the frame structure, a table kind of thing, is indexed by the dot function, and whatever is there gets used. I think frame.foo is the same as frame[‘foo’], basically an indexing function.
I’m curious enough to dig into this later, and I’ll pass on what I learn. There will be things coming up where we may need to know more about how all this works. What is good enough for now is to learn the magic incantations and use them.
EXCEPT THAT THIS PROGRAM DOESN’T WORK!?!?!?
Instead, it blows up on the rect() line, with some message that sounds like self is nil. I don’t see how this could be. So I Google the message and learn that you don’t say frame.draw(), you say frame:draw(). That’s what tells Codea to set up a value “self” for the object, through which it can index into its own variables via self.x and so on. So this makes me raise the priority of actually reading the Lua manual a bit higher but right now we are on a mission.
Next step is to save the state of any transformations that may already be in place, although there aren’t any, then translate to the desired offset, then draw a rectangle from (0,0) to (width,height). Then we’ll pop the transform state. When we go to drawing our sub-objects, we’ll put the draw calls inside the push / pop, so they will all operate according to whatever transforms we have in place. It goes like this:
Frame = class()
function Frame:init(x, y, width, height) self.x = x self.y = y self.height = height self.width = width end
function Frame:draw() pushMatrix() translate(self.x, self.y) rect(0, 0, self.width, self.height) popMatrix() end
Note that the rect() call now uses 0,0, since we have picked up the x and y in the translate. As I look at that now, I wonder whether x and y are the best names. I’ll think about that.
This works as intended. Now, what we are working toward is that our frame will have a list of other elements who can draw themselves, and frame will translate them, scale them, clip them, whatever. So let’s draw a couple of lines, inside Frame for now, which will grow up into smarter objects. i’ll keep one inside the frame, and let one go out, so i can play with scaling a bit.
This is the same approach I took before. First I try something in line, then I refactor it out into whatever object it seems to want to be. I’m reminded of Keith Braithwaite’s “TDD as if you really mean it” courses, where he makes you write everything inside the tests until it becomes clear what you really need. What’s interesting to me is that moving in these tiny steps seems to me to work better. Yes, I’m smart enough to write more than two lines of code at a time. But why not find out, every two lines, whether things are working as I expect?
Anyway, I’ll just add a couple of connected lines, representing a graph:
Frame = class()
function Frame:init(x, y, width, height) self.x = x self.y = y self.height = height self.width = width end
function Frame:draw() pushMatrix() translate(self.x, self.y) rect(0, 0, self.width, self.height) line(0, 0, 75, 100) line(75, 100, 400, 300) popMatrix() end
This works ALMOST as intended. Take a look at the picture. It looks to me as if the beginning of the line starts outside the rectangle. The second line does extend outside, as planned. My intention is to impose a scale factor in the frame, that will keep the graph inside.
But I’m not sure at all what I’ll do about the dot. It looks like the rect is drawn INSIDE its given coords. i have seen that done in other systems, so it might be true here.
I pause for a quick experiment. I draw from 0,0 to width, 0; from there to width,height; from there to height,0, and from there back to 0,0. And guess what … that rectangle is completely outside the one drawn by the rect function. It appears that rect() draws just inside the given coordinates. That’s interesting.
For now, I try a little hack. I’ll draw the rectangle, then translate up a bit, then scale as planned. It turns out that strokeWidth seems to scale, so I’ll scale that too.
Frame = class()
function Frame:init(x, y, width, height) self.x = x self.y = y self.height = height self.width = width end
function Frame:draw() pushMatrix() translate(self.x, self.y) rect(0, 0, self.width, self.height) translate(5,5) scale(self.width/500, self.height/500) strokeWidth(5/(self.width/500)) line(0, 0, 75, 100) line(75, 100, 400, 300) popMatrix() end
This works as intended, and it’s a good place to stop. Especially since this is where I stopped when I was programming. These articles take longer to do than the code. I need a better scheme. Ideas welcome. Here’s a pic of the final output. Well, the output so far. See you next time!