I took a break from working on another side project to devote my Sunday and part of my Monday to playing more with Corona. Specifically, I wanted to:
- Learn how to do better OOP in Lua
- Learn how to extend the base Corona GUI classes like Group
- Learn about collision detection via their Box2D implementation
Mutha-grabbin success.
I set out to re-build 1942. It’s the perfect Corona game in that it has a lot collisions, sprites, and simple bitmap animations. I want to talk about what I learned in my 2 days. If you’re not familiar with Lua and know some ActionScript, hit my Lua for ActionScript Programmers Crash Course and check out a more simple example first. Here’s a quick sample video. You can download/follow the code on Github.
OOP in Lua
No one agrees on the best way to do OOP in Lua, specifically for Corona development. The official Lua docs give at leaste 2 different ways, and only 1 shows inheritance. This isn’t to say you can’t do inheritance with the first example, but this is a recurring theme I’m seeing: a blasé approach to OOP in Lua. Whether this is because the use of polymorphism and inheritance is seen as “over kill” for a lightweight scripting language, or is too old skool for a functional language, has seriously made it challenging for me to feel secure in what I’m doing.
ActionScript had a pretty standard path with only a few disagreements un-related to the actual result.
- in Flash 5 & 6 with ActionScript 1, we used Object.prototype. While prototypes were mutable, most purists didn’t edit them at runtime. These were basically classes and is how some classes are implemented in JavaScript. Calling a prototype extension a “class” offends some people, but it works. You get sub and super classes with inheritance and polymorphism.
- in Flash 7 & 8 with ActionScript 2, we had classes that looked like Java. While it compiled to AS1 prototypes, the advent of proper packages helped us scale larger class structures.
- in Flash 9 & 10 with ActionScript 3, we have classes where the prototype is immutable, and we now have enforced access rights via private and protected.
While the Rebel Alliance had their own implementation of YourClass.prototype, and no one to this day agrees on how you create a Singleton, at least it’s pretty easy to tell if you’re doing something “the AS3 way” or not.
Contrast this with Lua specifically in a Corona context. So far, the 3 approaches I’ve found are:
- Use metatables (from the main docs). Notice no easy way to call super in the inheritance example.
- Another example which also has a fake prototype (also whack sub-classes compared to what I’m used too). If you liked AS1, you’ll totally understand their example… until you to try to sub-class it.
- Use one of the 3rd party libraries like middleclass (thanks for the suggestion, Jim!)
You can read all the examples of just one particular site if you’re so inclined to do a deep dive.
Me? I just want re-usable objects that are DRY. It seems Lua is all about the re-usable and flexible objects, but how you implement DRY has no consensus.
For now, I’m using factory functions with no inheritance. This seems the most straightforward. When all else fails, you can use the Decorator pattern to add behavior you need without inheritance, and just “remembering” the interface via convention. I don’t know if any of you Flash Devs remember Flash MX’s DataProvider, or even the DataSelector; it would overwrite the Array.prototype and your Class’ prototype so you could implement the Observer pattern with a simple Array. Basically the same thing here; add the methods you want on a class at runtime since everything’s dynamic.
A word of warning for you Flash devs: you’re going to start to find all the old problems un-solved. I say “un-solved” and I’m sure there is some Lua veteran going “what problem…?” in his/her head. Specifically all the scope issues we had in AS1/AS2 using Activation Objects, and Delegate. Functional afficiandos’ think it’s neat… until they have a deadline (which they don’t).
Factory Functions & Closures
Factory functions in Corona build things for you. Factory functions as I implement them in Corona are similar to attachMovie back in the AS1/AS2 days in Flash. Instead of doing “new MovieClip()”, you’d go attachMovie(“your gui thing”, YourClassToMergeItWith). In this case, we’re creating the GUI thing, then decorating it with functions and public variables we need. It’s quick, it’s encapsulated, and calling the function gives you the object as a return value if you need it.
Closures are basically functions within a function. People go all Lady Gaga over them and I’m not sure why. They are convenient when quickly prototyping, yes, but once you do a DRY run over your code, they tend to disappear… *ahem*. Anyway, Lua has a lot of scope in common with JavaScript and ActionScript 1 in regards to nested objects and functions. I’m still learning the finer rules of the difference between functions functionName() and functionName = function() (huge difference in AS1 that only academics care about), but bottom line, you can define additional class like behavior on existing objects at runtime. The closures, or “functions within a function” are that additional added functionality.
Here’s an example of how I create an explosion animation when an enemy plane is shot in my simple PlaneShooter game:
function createEnemyDeath(targetX, targetY)
local si = sprite.newSprite(enemyDeathSet)
mainGroup:insert(si)
si.name = "enemyDeathSetYo"
si:prepare()
function onEnd(event)
if(event.phase == "loop") then
event.sprite:removeSelf()
end
end
si:addEventListener("sprite", onEnd)
si:play()
si.x = targetX
si.y = targetY
return si
end
Notice we’re taking a Sprite sheet animation, and handling loop event (called “sprite”) inline. This, as opposed to burdening whoever is calling the createEnemyDeath function. We also do some non-factory function stuff like assume mainGroup is already created and ready to have DisplayObjects added to it.
Ok, that’s a simple use of a closure, but let’s show how you add behavior to existing Corona object classes via closures with intact scoping (yes yes… more on scoping in a bit). There’s a lot of code in this next snippet; just scan it, I’ll go over the relevant pieces below.
local function createPlayer()
local img = display.newImage("plane.png")
mainGroup:insert(img)
img.speed = PLAYER_MOVE_SPEED -- pixels per second
img.name = "Player"
img.maxHitPoints = 3
img.hitPoints = 3
physics.addBody( img, { density = 1.0, friction = 0.3, bounce = 0.2,
bodyType = "kinematic",
isBullet = true, isSensor = true, isFixedRotation = true,
filter = { categoryBits = 1, maskBits = 12 }
} )
function img:move(x, y)
self.x = x
self.y = y
end
function img:onBulletHit(event)
self.hitPoints = self.hitPoints - 1
setHealth(self.hitPoints / self.maxHitPoints)
if(self.hitPoints <= 0) then
self.isVisible = false
audio.play(playerDeathSound, {loops=0})
createPlayerDeath(self.x, self.y)
stopPlayerInteraction()
endGame()
else
audio.play(playerHitSound, {loops=0})
end
end
function img:tick(millisecondsPassed)
if(self.x == planeXTarget) then
return
else
local deltaX = self.x - planeXTarget
local deltaY = self.y - planeYTarget
local dist = math.sqrt((deltaX * deltaX) + (deltaY * deltaY))
local moveX = self.speed * (deltaX / dist)
local moveY = self.speed * (deltaY / dist)
if (self.speed >= dist) then
self.x = planeXTarget
self.y = planeYTarget
else
self.x = self.x - moveX
self.y = self.y - moveY
end
end
end
return img
end
This function does a lot. It takes an image and adds TONS of behavior to it. Notice how I take the image that Corona builds for me from a simple PNG, and immediately add a speed variable to it. This does 2 things. The first is it allows me to later access this variable from internal “Player” functions via self (like ActionScript’s “this”). Secondly, I basically create a “PlayerClass” that has a public speed variable. Since everything in Lua is basically a Table, and a Table is basically an ActionScript Object, it’s dynamic, so we can add whatever we want to it at runtime.
Ok, so that’s properties. Now onto methods. I add 3 here: move, onBulletHit, and tick. Each is unique. Notice the format of move.
function img:move(x, y)
self.x = x
self.y = y
end
I take an instance, img, and add a method to it called move via the colon (lol!). This is a simple utility method, but shows how you can add simple utility methods inline. If things start to get duplicated, you can use the Decorator pattern mentioned above to dynamically add them later in a single space. Notice too that he has the use of “self” inside of the function. You can have faith that if he’s called correctly (more on that correctness later), he’ll have the self pointing to the img instance.
Let’s take a look now at “tick”, an impl method, also known as “an implementation method of an interface”. The interface here is “Jesse Warden’s convention of having all game loop participatory objects implementing a tick method which he’ll assume is there later and if not via an error in the console, then he’ll fix it”. People who like dynamic languages to make a living are strange.
function img:tick(millisecondsPassed)
if(self.x == planeXTarget) then
return
else
local deltaX = self.x - planeXTarget
local deltaY = self.y - planeYTarget
local dist = math.sqrt((deltaX * deltaX) + (deltaY * deltaY))
local moveX = self.speed * (deltaX / dist)
local moveY = self.speed * (deltaY / dist)
if (self.speed >= dist) then
self.x = planeXTarget
self.y = planeYTarget
else
self.x = self.x - moveX
self.y = self.y - moveY
end
end
end
Also this game loop method doesn’t make use of the time, we’ll ignore that for now. Just focus on a few important details. First, yet another function I’ve added dynamically to player sprite to emulate a class that has methods. Second, it has self, just like the others so it knows about itself and is independent from other instances without the need of global variables. Third, it makes use of local variables, just like var variableName used inside of ActionScript functions. Third, I still have access to all the basica Corona functionality, in this case the math.sqrt function. Fourth, because of self access, it too can access instance variables as well.
Normal stuff, right? Ok, now the 3rd, an event listener like ActionScript ones (has the “on” prefix, and the 1 and only parameter as event).
function img:onBulletHit(event)
self.hitPoints = self.hitPoints - 1
setHealth(self.hitPoints / self.maxHitPoints)
if(self.hitPoints <= 0) then
self.isVisible = false
audio.play(playerDeathSound, {loops=0})
createPlayerDeath(self.x, self.y)
stopPlayerInteraction()
endGame()
else
audio.play(playerHitSound, {loops=0})
end
end
The only thing to notice here is that I'm calling global functions. I didn't use "local" in front of setHealth, createPlayerDeath, stopPlayerInteraction, and endGame so they are global functions; meaning anyone can call them (like $global in PHP).
Again, standard stuff, right? So how do we ensure this "self" variable works and doesn't break...?
A Quick Intro to Lua Scope
You can read more about it at Stackoverflow (scroll to the bottom where jhocking answers it). Basically, the rules of accessing Tables (Objects for you ActionScript devs) goes like this:
When reading and writing properties, use a dot.
When calling functions, use a colon.
Why this is a feature of Lua, I'll never know, but ActionScript 1 and JavaScript have the same "feature" (read: problem if you're a Flash/Flex dev).
If use a dot, you have to manually pass in scope. If you use a colon, then when you use self inside the function, it points to the object you're calling the function on. Usually; again, it depends on 1 of the copious ways one can define functions. If you do it like I do above, you'll be fine.
Callback Weirdness
There are some callbacks where more advanced functional programmers will want to put a closure as their event listener handler. This is because some of Corona's events require a pointer (no, I don't know why). Let's take Box2D's collision, for example (Box2D, while a completely separate physics engine, treats Corona objects like any normal object). Here's the collision code for an enemy bullet:
function onHit(self, event)
if(event.other.name == "Player") then
event.other:onBulletHit()
self:destroy()
end
end
img.collision = onHit
img:addEventListener("collision", img)
Notice the collision pointer to the function. Also notice I define it as onHit vs. img:onHit... and self is the 1st param. Confused yet? Just know that those edge cases WILL give you self as a 1st param. If not, you can wrap it in a closure like so:
obj:addEventListener("touch", function(event) self:onCustomHandler(obj, event) end)
Collision Filters
Box2D, and thus Corona, comes with this nice feature for collision detection. Instead of having a ton of if/then or switch logic in your collision handlers, you can instead apply filters so that only certain objects trigger collision events when they collide with certain objects. In my game, the player's airplane can collide with enemy planes and enemy bullets, but not with other player airplanes, nor with his own bullets. The enemies cannot collide with each other, nor with their own bullets.
The problem is it makes my head hurt. Apparently others as well. So this cool guy named Brent Sorrentino made a great explanation + chart.
It helps you identify who can collide with what, and you end up with 2 numbers to use in your code, called category bits and mask bits. OT OT OT
Here's the Google Spreadsheet I made to determine what bit filters I needed for my game:
From that, I can implement the filters for my enemy bullets (just look for the filter option):
physics.addBody( img, { density = 1.0, friction = 0.3, bounce = 0.2,
bodyType = "kinematic",
isBullet = true, isSensor = true, isFixedRotation = true,
filter = { categoryBits = 8, maskBits = 1 }
} )
Now my collision events are like "Dude, I hit players only, so... simple if then statement, yo!".
Conclusions
I still haven't found the Branden Hall, Dave Yang, nor Colin Moock of Lua. All the blogs I read are humble opinions from smart people. While nice, we need leadership with someone to put their foot down on how OOP works in Lua for Corona. At least we don't have a module problem like JavaScript.
Maybe they exist and my 5 day Corona/Lua excursion just hasn't found them yet.
Many of you have asked what my draw is to Corona. Mainly the built-in Physics engine with collisions, other built-in gaming API's that'd require a bit of work in Flash/Flex, ...and Lua is a nice change of pace. I give her crap, but it's fun.
I encourage you to take a visual scan at the left side of the API page, I'm sure you'll find something you'd like to play with.
Nice post with some good explanations! I just wanted to comment about your leadership statement in your conclusion. I’m not sure we will get one definitive OOP approach, since I doubt anyone would presume so much as to say which way is right for everyone.
For exmaple, if I want multiple inheritance, I use the class code from the lua site directly. I find the simplicity of the approach is very nice. But I’d understand if someone didn’t want to do the additional lookup (via the metatable entry), and opted instead for a decorator pattern and a reliance on convention.
See what I mean? You offer an intelligent point, humbly, and don’t make the claim it’s right for everyone. *sigh* To be fair, ActionScript was only used for Flash.
Lua, however, is used in games, for game tooling like Unreal, Neverwinter Nights, World of Warcraft extensions, and robotics. So, I’m sure it’d be hard to have an implementation that would work across those vastly different demographics.
Finding out how to do proper OOP was the biggest issue for me when I started using Lua / Corona – I wrote an forum article on my initial experiments (http://developer.anscamobile.com/forum/2011/04/21/ways-implement-oop-lua) which pretty much mirror your last attempt.
I did consider “middle class” however I eventually stumbled on Ricardo Rauber’s loader class (http://rauberlabs.blogspot.com/2011/05/loader-class.html) which (IMHO) is a very clean and structured way to add OOP functionality and something I would heartily recommend.
Using Ricardo’s loader the preferred way seems to be to “promote” the Corona displayObject to a fully fledged class, however you can (and I’ve just done it on a current project) create an object instance out of any empty table. It provides a nice clean way to encapsulate all your data, provide class and instance variables, private and public data and functions – basically everything you need.
Jon…
Ps. Nice tip about the categories with Box2D – I’ll look into using that in the future!
It may be that Lua usage is just too varied to have a one size fits all OO approach and Corona is too new to have even reached the stage where it gets to the level of a couple of well argued positions with advocates etc to choose from. In Flash it didn’t really start til Flash 5 as I recall with ACK/Flem and the arguments that kicked off which took on steam in FlashMX. So it may just take time to develop.
Excited to see another Corona/Lua post – there aren’t enough out there yet! I agree with you that one of the best reasons to try Corona out is simply because it’s fun. If you already know AS3, or game programming in general, it’s only a matter of minutes/hours to crank out a game.
+1 for Ricardo Rauber’s loader class. It really does make the transition from AS3 to lua/corona a breeze.
There is not much documentation, but the example he provides has pretty much everything you need in it. To be honest, its so simple, not much is really required in the way of an API etc…
Elliot.