Friday
Jan212011

Put Some OOP in Your App -- Part 1: Creating a Class

If you’re like me, when you started using Corona and programming in Lua, you dove in and got right to coding. Maybe you looked into object-oriented programming in Lua, but soon realized it doesn’t have anything built into the language for it. And maybe, like me, you were a little intimidated by all the different methods other people have come up with for OOP in Lua, so you just skipped it for now and said, “I’ll get back to it later when I have more time.”

If that describes you, and you still haven’t used OOP in your Lua/Corona projects, this article might help. I’ll show you that there’s a very easy way to approach object-oriented programming, which can do wonders for organizing your code as your projects grow larger, not to mention improving the potential for reusability.

As I mentioned before, there are lots of ways to do OOP in Lua, but the simplest way, described in PiL, is the one I’m going to illustrate here.

Let's start with a simple example. I'm going to create a Number class. All it's going to do is store a number, increment it, decrement it, and return its value.

Number = {}
Number.__index = Number

On line 1, we create an object called Number and assign it an empty table. In Lua OOP, objects and classes are interchangeable. A class is really just an object (table) that we decide to call a class.

On line 2, we set the __index() metamethod for Number to the Number table itself. You can click here to learn more about what the __index() function does, but I'll explain a little bit more in a bit.

This sets up the basic class, but there's more we need to do. The most important part now is to write a constructor that will create instances of the class:

function Number:new(i)
	local o = {_i = i}
	setmetatable(o, self)
	return o
end

I call this function "new" because that's what PiL does, and it makes the most sense to me, but really you can call it whatever you like. If you'd rather create your objects using a function called createObject(), go for it. You should be familiar with the ":" calling notation by now, especially if you use Corona. If you're not, it's very simple. Calling o:func(a,b,c) is the same as calling o.func(o,a,b,c). And defining function o:func(a,b,c) is the same as function o.func(self,a,b,c) where "self" points to the object o.

The first line in the new() function creates the actual object "o". If there are values you want to initialize here, that's fine, but you can also just pass an empty {} here and set the values afterwards. The next line sets the metatable of the object to the metatable of the class. This is the key to inheritance.

OK so what's a metatable? It's a little complicated, but I'll try to keep it simple and to the point for this tutorial. A metatable is a set of methods that are attached to a table. The names of these "metamethods" are pre-defined by Lua, and always start with two underscores ("__"). You'll remember we set Number.__index = Number earlier. The __index metamethod is triggered internally by Lua when you try to access a table element that's not defined in the table. So if you create t = {x=1, y=2}, and then you try to access t.z, Lua won't find it, so it will look at t's metatable, and if __index is defined, it will use that to look for "z". It can either be a function you define yourself, or more commonly, it can simply be a pointer to another table, in which case that table will be searched for a "z" element. And here's the cool part -- Lua keeps on going: if that second table doesn't have a "z" element, but it does have __index defined, Lua will look there. It will keep on looking until it comes to a "z" element, or it comes to a table without an __index metamethod defined. As I said, this is key to inheritance.

Whew! That was a lot of explanation. So setting the metatable for our new object means that whatever functions we define for the Number class, you can call those functions by pointing to the object instead of the class.

Finally, to finish the definition of new(), we simply return the object we just created.

Now let's define some utility functions for our new class:

function Number:value()
	return self._i
end

function Number:increment()
	self._i = self._i + 1
end

function Number:decrement()
	self._i = self._i - 1
end

function Number:test(title)
	print(title)
	print(self:value())
	self:increment(); print(self:value())
	self:increment(); print(self:value())
	self:decrement(); print(self:value())
	print()
end

That's it for our definition of the Number class. Again, this isn't meant to be useful; just illustrative. Here's an example of how we might use this class:

local num
num = Number:new()
num:test("Number")

When we execute this, we get the following result:

Number
1
2
3
2

This is exactly what we expect. First it prints the initial value, then it calls increment() twice, followed by decrement() once. Looks great!

You may have noticed that within the Number class, I store the value in a table element called "_i". I use the underscore for reason. Lua lacks one very important feature of OOP: information hiding. If you've programmed in almost any object-oriented language, you'll be familiar with the idea of hiding internal variables from outside code, and creating "getters" and "setters" to interface with those variables. Lua does not have privacy. There are ways you can build it in, but they have drawbacks.

Do you need to be concerned about the privacy of your internal variables? I think so, yes. Your code is less likely to have bugs if you're careful about how you access your class variables. If you have a class that represents a bank account, you only want other code to take money out of the account by using a withdraw() function. You don't want to leave the account balance open to be modified by anyone at all.

So how do we achieve privacy in Lua? Other programmers probably have different methods, but what works for me is simply putting an underscore ("_") in front of each variable in the class. That reminds me that I'm not supposed to access those variables directly; and it also makes it easy to search my code to see if "._" appears anywhere.

This tutorial should give you enough to get started with OOP in Lua if you've never tried it before. In my next tutorial I'll extend this class with several subclasses to illustrate how inheritance works. Stay tuned!

For reference, here's the complete source code so far:

Number = {}
Number.__index = Number

function Number:new(i)
	local o = {_i = i}
	setmetatable(o, self)
	return o
end

function Number:value()
	return self._i
end

function Number:increment()
	self._i = self._i + 1
end

function Number:decrement()
	self._i = self._i - 1
end

function Number:test(title)
	print(title)
	print(self:value())
	self:increment(); print(self:value())
	self:increment(); print(self:value())
	self:decrement(); print(self:value())
	print()
end

local num
num = Number:new(1)
num:test("Number")

I hope you've enjoyed this tutorial. If you found it useful, please consider following me on Twitter and/or sharing it with someone. Thanks!

Sunday
Jan162011

Blue Balls, Part 2

In my previous tutorial I created a very simple program that places 10 balls randomly on the screen and moves each ball slowly to the left until reaching x=50.

The method I used was pretty simple, and very convenient for projectiles that don't get affected by anything during their journey to oblivion.

In this tutorial I'm going to illustrate a more complex solution that gives you ultimate control over each ball as it moves along the screen.

Exactly like the first tutorial, we start with the line of code that's going to create all the objects:


timer.performWithDelay(500, makeDude, 10)

Then we add the makeDude() function:

math.randomseed(os.time())

local function makeDude()
	local dude = display.newImageRect("blueball.png", 50, 50)
	dude.x, dude.y = math.random(200, 300), math.random(50, 350)
end

timer.performWithDelay(500, makeDude, 10)

Calling math.randomseed() just ensures that we get random results. It's not important to this tutorial, but I don't like to do randoms without it.

You can run this version and you'll see it creates ten static blue balls on the screen. Whoopie!

Now here's where we take a different fork in the road than the first tutorial. We're going to micromanage our balls. (Yeah, I went there.) The screen gets refreshed 30-60 times per second, and we're going to create a function that gets called each time before that happens. The idea is that we're going to modify each ball ourselves before each screen refresh.

You accomplish that by adding the following highlighted code:

math.randomseed(os.time())

local function makeDude() 
	local dude = display.newImageRect("blueball.png", 50, 50)
	dude.x, dude.y = math.random(200, 300), math.random(50, 350)
end

local function listener(event)
end

timer.performWithDelay(500, makeDude, 10)

Runtime:addEventListener("enterFrame", listener)

You can run this. It doesn't do anything different, but it does run.

As you can see in the last line, we added an event listener. When "enterFrame" happens (which is just before each screen refresh) our program will call the function we call "listener". You can call it anything you like.

The listener function itself is empty for now. That's where the fun will happen. In there, we want to loop through each ball we've created, and modify its position. So how do we access a reference to each ball object that was created by display.newImageRect()?

The answer is, there is no Corona facility (that I know of) that will give you that. Corona obviously has an internal list of objects, but I haven't seen any way to access it. That's fine. We'll roll our own. This is Lua, and we are kick-butt programmers.

Whenever we add a ball object, we're going to have to keep track of it in a table. Like this:

math.randomseed(os.time())

local dudes = {}

local function makeDude() 
	local dude = display.newImageRect("blueball.png", 50, 50)
	dude.x, dude.y = math.random(200, 300), math.random(50, 350)
	dudes[#dudes+1] = dude
end

local function listener(event)
end

timer.performWithDelay(500, makeDude, 10)

Runtime:addEventListener("enterFrame", listener)

Now let's create some action inside the listener() function:

math.randomseed(os.time())

local dudes = {}

local function makeDude() 
	local dude = display.newImageRect("blueball.png", 50, 50)
	dude.x, dude.y = math.random(200, 300), math.random(50, 350)
	dudes[#dudes+1] = dude
end

local function listener(event)
	for i, dude in pairs(dudes) do
		if dude.x < 50 then
			dude:removeSelf()
			dudes[i] = nil
		else
			dude.x = dude.x - 1
		end
	end
end

timer.performWithDelay(500, makeDude, 10)

Runtime:addEventListener("enterFrame", listener)

Now that we have a table of dudes, we loop through each one. Notice that even though the keys are numeric, I'm using pairs() instead of ipairs() in the loop. That's because we'll be deleting items in the middle of the table sequence, and ipairs will stop at the first "hole" in the sequence. The pairs() function treats the keys as generic data and doesn't care or know about holes or sequences.

Inside the loop, if the ball has passed x=50, it's deleted. Corona display objects can simply be told to remove themselves and they'll do it cleanly. How polite: "May I kill myself now, sir? Thank you." We also make sure to set the "dudes" table reference for that object to nil, using "dudes[i]", not "dude". Since "dude" is just a local var, setting it to nil doesn't do anything useful.

In the else part of the loop, we simply subtract 1 from the x position. (Ideally we'd be subtracting a value that's based on the ball's velocity times the amount of real time elapsed since the previous screen refresh, but this is just an example, so I'm keeping it simple).

If you run it now, you should see it works exactly like the previous tutorial.

So that explains how to do it. Now let's talk just a moment about why you'd want to do it this way, as opposed to the more concise method from the previous tutorial. Three reasons: control, control, and control. Just like with your '68 Mustang, when you pop the hood, get your hands dirty and tune your own engine, you have the ultimate in control.

Let's make the balls animate so they look a little more like soap bubbles by giving them a random y coordinate on each screen refresh:

math.randomseed(os.time())

local dudes = {}

local function makeDude() 
	local dude = display.newImageRect("blueball.png", 50, 50)
	dude.x, dude.y = math.random(200, 300), math.random(50, 350)
	dudes[#dudes+1] = dude
end

local function listener(event)
	for i, dude in pairs(dudes) do
		if dude.x < 50 then
			dude:removeSelf()
			dudes[i] = nil
		else
			dude.x = dude.x - 1
			dude.y = dude.y + math.random(3) - 2
		end
	end
end

timer.performWithDelay(500, makeDude, 10)

Runtime:addEventListener("enterFrame", listener)

The highlighted line shows that we're either adding -1, 0, or 1 to the y value each time. This gives the balls a nice little "jiggly" effect that makes 'em look at little bit like soap bubbles. (If you haven't run the code, you'll just have to trust me.)

For kicks and giggles, we can also add a test after x < 50 to randomly kill balls:

math.randomseed(os.time())

local dudes = {}

local function makeDude() 
	local dude = display.newImageRect("blueball.png", 50, 50)
	dude.x, dude.y = math.random(200, 300), math.random(50, 350)
	dudes[#dudes+1] = dude
end

local function listener(event)
	for i, dude in pairs(dudes) do
		if dude.x < 50 or math.random(200) == 1 then
			dude:removeSelf()
			dudes[i] = nil
		else
			dude.x = dude.x - 1
			dude.y = dude.y + math.random(3) - 2
		end
	end
end

timer.performWithDelay(500, makeDude, 10)

Runtime:addEventListener("enterFrame", listener)

As you can see, there are tons of possibilities here. Checking for collisions or proximity, changing direction, whatever you like. Once you micromanage your objects in the "enterFrame" event, there's really no limit to what you can do. You just have to remember that Lua is an interpreted language, so you always need to be vigilant about how well your code is performing.

I hope you've enjoyed this tutorial. If you found it useful, please consider following me on Twitter and/or sharing it with someone. Thanks!

Here is the zip file.

Sunday
Jan162011

Blue Balls, Part 1

In this tutorial I'm going to create a very simple program that places 10 balls randomly on the screen and moves each ball slowly to the left until it reaches x=50, at which point we'll kill it.

There are two ways to approach this, and which one you use depends on what you need to do in your program.

The first method is nice because it's pretty short. It works well for objects like projectiles which won't be affected during the course of their movement.

The second method is longer, but it allows for more flexibility in your code. I'll cover that in Part 2.

OK first we start with the line of code that's going to create all the objects:


timer.performWithDelay(500, makeDude, 10)

This says: "every 500 milliseconds, call a function called makeDude, and do that 10 times."

Easy enough, right? Now we need to create the makeDude function. Obviously you can call this function anything you like, but whatever you call it, it has to be defined before timer.performWithDelay(). So here's what you do:

math.randomseed(os.time())

local function makeDude()
	local dude = display.newImageRect("blueball.png", 50, 50)
	dude.x, dude.y = math.random(200, 300), math.random(50, 350)
end

timer.performWithDelay(500, makeDude, 10)

You can run this now. It simply puts 10 balls on the screen. They don't do anything, though. So let's make 'em do something:

math.randomseed(os.time())

local function makeDude()
	local dude = display.newImageRect("blueball.png", 50, 50)
	dude.x, dude.y = math.random(200, 300), math.random(50, 350)
	transition.to(dude, {time=(dude.x-50)*30, x=50})
end

timer.performWithDelay(500, makeDude, 10)

Using the power of transition.to(), I make each ball move from where it is to x=50. I could have picked a constant time value, but that would give each ball a different velocity since each ball starts at a different x value. So I calculated the time based on the distance from x=50, and multiplied by 30, which is a fudge factor I determined by running it a few times until the speed looked right.

Now all the balls move to x=50 and stay there. If these were (very slow!) projectiles, we'd want to kill them when they reach their target, so let's do that:

math.randomseed(os.time())

local function makeDude()
	local dude = display.newImageRect("blueball.png", 50, 50)
	dude.x, dude.y = math.random(200, 300), math.random(50, 350)
	local function killMe()
		dude:removeSelf()
	end
	transition.to(dude, {time=(dude.x-50)*30, x=50, onComplete=killMe})
end

timer.performWithDelay(500, makeDude, 10)

I added "onComplete" to transition.to(), along with the function killMe() that actually does the killing.

Note that the function is declared locally within makeDude(), so it doesn't need any parameters. At the point the function is instantiated, it knows who "dude" is.

What this essentially means is that each dude (ball) gets its own killMe() function.

In order to prevent the need for this, there's an undocumented feature built into "onComplete" -- it passes a reference to the object! (Thanks to jmp909 in the Corona forums for that.)

Now we can create a single function that all objects will call to kill themselves:

math.randomseed(os.time())

local function killDude(dude)
	dude:removeSelf()
end

local function makeDude()
	local dude = display.newImageRect("blueball.png", 50, 50)
	dude.x, dude.y = math.random(200, 300), math.random(50, 350)
	transition.to(dude, {time=(dude.x-50)*30, x=50, onComplete=killDude})
end

timer.performWithDelay(500, makeDude, 10)

And there you have it! Run it and you'll see it works like a charm.

I hope you've enjoyed this tutorial. If you found it useful, please consider following me on Twitter and/or sharing it with someone. Thanks!

Download zip file

Saturday
Jan152011

Editor? What editor?

I was new to the Mac when I started programming iPhone apps. I started my first apps in XCode using Objective-C. For that, I found XCode was fine. But then I discovered Corona, and I felt a new editor might be a good idea.

I tried a bunch of different editors, but finally settled on TextMate. It offered most of the functionality I was looking for at a pretty decent price. Also, what really sealed the deal for me was The Corona Bundle for TextMate by Ludicrous Software. It's very nice, and has saved me lots of time in the development cycle, especially since I tend to type two characters, compile & run, type two more, compile & run, etc.

Saturday
Jan152011

Welcome to Planet Lua!

Hi, my name is Mike and I'd like to welcome you to Planet Lua, a website dedicated to providing useful information about the Lua programming language, and in particular, about using Ansca Mobile's Corona to create games and other apps for the iPhone, iPad, and other mobile devices.

While my focus will largely be on Corona, I do intend to try to keep this site as general as possible, with tutorials and other information that can be used by any Lua programmer, whether they're creating an addon for World of Warcraft, embedding Lua in a nuclear missile's guidance software, or, of course, writing games in Corona.

Sorry there's no actual content here at the moment, but that will change soon. Stay tuned.