Lua Classes and Packages in Corona

Lua has some diverse usage, from AI, robotics, 3D shaders, game extensions, or writing games. As such there are a variety of ways to do OOP in Corona. Specifically:

  • Classes
  • Packages
  • Access Modifiers/Namespaces/Encapsulation
  • Inheritance
  • Polymorphism
  • “dealing” with scope

Below, I’ll show you how I do classes and packages in Corona SDK. I cover how to extend core Corona classes, what you can/can’t do, and how to implement classes, packages, & inheritance in Lua. I also briefly cover some of the OOP alternatives and compare/contrast why I chose t3h closure way.

I do not cover, nor use metatables. If you can live without prototype in JavaScript, you’ll do just fine using only closures in Lua. I’ll give you some links at the bottom for using metatables to accomplish OOP in Lua.

Extending DisplayObjects

Corona has a few different types of DisplayObjects. The ones that matter for the sake of this article are:

  • Group
  • SpriteSheet
  • Text
  • Vector
  • Native Text
  • MovieClip

The general use case is you want to create an image or composite group of images such as a screen, and add behavior to it. As you start doing things like this for weeks on end:

local player = display.newImage("player.png")
player:setReferencePoint(display.TopLeftReferencePoint)
player:addEventListener("touch", onTouch)
player.startX = 100
player.startY = 100

… your main.lua file starts to get unwieldy large in length. Additionally, some things will work differently depending on scope, which to me, makes the code less readable over time. Breaking out your code into classes, separate Lua files, is the first step to managing lots of code. There are 3 steps to make this happen.

  1. Make a new lua file, optionally in a package
  2. Put your code in it
  3. Encapsulate it

Classes, _G, and Packages

Lua has a function called require. It’s basically like JavaScript’s include via script tag, Python’s import, Ruby’s require, and ActionScript 1’s #include. It finds the file path you pass it, runs all the code in it, and ensures this is only done 1 time.

The code for the most part is treated like other Lua code. If you defined globals, they’ll put on _G just like code in your main.lua. All globals in Lua go on _G. If you’re a Flasher and remember creating classes back in AS1/AS2, this should be immediately familiar to you a la _global. Because of this, you need to treat your code in that file as encapsulated to ONLY put 1 thing on _G: your class.

You do this defining your class as a Lua table, and returning it. The require function is notoriously used by people to capture the value require imports, and use it. You don’t need to do this in the majority of cases and I never do. Here’s an example:

Player = {}
return Player

I declared a global variable here. If we save it out as “Player.lua” and import it in our main.lua, it’ll be put in _G, like so:

print("Player: ", Player) -- Player:	nil
require "Player"
print("Player: ", Player) -- Player:	table: 0x19b64e0
print("_G.Player: ", _G.Player) -- Player:	table: 0x19b64e0
print("_G['Player']: ", _G["Player"]) -- Player:	table: 0x19b64e0

If we wanted to use the new packages feature that came in build 619, we could:

print("Player: ", Player) -- Player:	nil
require "com.jessewarden.game.Player"
print("Player: ", Player) -- Player:	table: 0x20af230
print("_G.Player: ", _G.Player) -- Player:	table: 0x20af230
print("_G['Player']: ", _G["Player"]) -- Player:	table: 0x20af230

Notice, however, that the package name is completely discarded; all that’s actually stored on _G is your class’ name, so beware of name collisions (such as Player being defined in a completely different package).

The “new” Constructor Convention

The diversity of Lua’s user base makes conventions hard to come by. One that is pretty standard in the Corona community, however, is new. Sort of. Thus, its become the adopted constructor of most custom classes done by the community. It looks like this:

Player = {}
function Player:new()
end
return Player

Now, let’s actually create something, in this case

Player = {}
function Player:new()
	local player = display.newImage("player.png")
	return player
end
return Player

Now, you can get as many Player sprites as you want. Here’s 3:

require "com.jessewarden.game.Player"
local player1 = Player:new()
local player2 = Player:new()
local player3 = Player:new()
print("player1: ", player1)
print("player2: ", player2)
print("player3: ", player3)

And the console output:

player1: 	table: 0x361f510
player2: 	table: 0x361f6b0
player3: 	table: 0x361f850

Those numbers next to table are RAM addresses of where your object is stored. As you can see it’s a unique address, thus you have 3 unique instances of your Player class.

Now let’s modify our constructor to make it easier to position the players initially with less code to write:

Player = {}
function Player:new(startX, startY)
	local player = display.newImage("player.png")
	player:setReferencePoint(display.TopLeftReferencePoint)
	player.x = startX
	player.y = startY
	return player
end
return Player

And then use it:

require "com.jessewarden.game.Player"
local player1 = Player:new(40, 60)
local player2 = Player:new(player1.x + player1.width + 2, 40)
local player3 = Player:new(player2.x + player2.width + 2, 60)

Hot, flying in formation:

Class Composition

Classes within classes? Let’s add a health bar to your player sprites. First, we’ll create a health bar; 2 vectors with 1 method to move based on the number you pass in:

HealthBar = {}

function HealthBar:new()
	local group = display.newGroup()
	
	local redBar = display.newRect(0, 0, 35, 6)
	group:insert(redBar)
	redBar:setFillColor(255, 0, 0)
	
	local greenBar = display.newRect(0, 0, 35, 6)
	greenBar:setReferencePoint(display.TopLeftReferencePoint)
	group:insert(greenBar)
	greenBar:setFillColor(0, 255, 0)
	
	function group:setHealth(current, max)
		local percent = current / max
		local desiredWidth = 35 * percent
		greenBar.xScale = percent
		greenBar.x = redBar.x + greenBar.xReference
	end
	
	return group
end

return HealthBar

If you’re a Flash Developer, ignore the insanity in the setHealth function. So, let’s test her out:

require "com.jessewarden.game.HealthBar"
local bar = HealthBar:new()
bar.x = 100
bar.y = 100
bar:setHealth(7, 10)

Cool. However, our Player class is an image, not a group. Let’s re-factor it to be a group so we can put other things inside of the Player, like so:

Player = {}
function Player:new(startX, startY)
	local player = display.newGroup()
	
	local playerImage = display.newImage("player.png")
	playerImage:setReferencePoint(display.TopLeftReferencePoint)
	player:insert(playerImage)
	
	player.x = startX
	player.y = startY
	return player
end
return Player

Notice the great thing is by refactoring the class, none of our external API (ie Player:new()) changes. Now, let’s add the HealthBar in. First, the require statement at line 1, and Second, instantiating it and adding it to the Player group at line 11:

require "com.jessewarden.game.HealthBar"

Player = {}
function Player:new(startX, startY)
	local player = display.newGroup()
	
	local playerImage = display.newImage("player.png")
	playerImage:setReferencePoint(display.TopLeftReferencePoint)
	player:insert(playerImage)
	
	local healthBar = HealthBar:new()
	player:insert(healthBar)
	healthBar.y = playerImage.y + playerImage.height
	
	player.x = startX
	player.y = startY
	return player
end
return Player

Great, we have now have composited both our player.png as well as our HealthBar class both into our Player class. How does it look?

Great, full formation with full hit points.

Method Calls

Let’s pretend the 1st plane got damaged. Let’s give him 2 public variables to hold his hit points, add them to the constructor, and provide a method to change those values and immediately call it.

First, the modified constructor + our new variable:

function Player:new(startX, startY, hitPoints, totalHitPoints)
	local player = display.newGroup()
	player.hitPoints = hitPoints
	player.totalHitPoints = totalHitPoints

Second, our new method. It sets both the internal values like an accessor/setter would in more stricter languages, as well as updating the internal HealthBar:

	function player:setHitpoints(hitPoints, totalHitPoints)
		self.hitPoints = hitPoints
		self.totalHitPoints = totalHitPoints
		healthBar:setHealth(hitPoints, totalHitPoints)
	end
	
	player:setHitpoints(hitPoints, totalHitPoints)

Now that the code is in place, let’s modify how we create the planes by passing in their new hit point values:

local player1 = Player:new(40, 60, 8, 10)
local player2 = Player:new(player1.x + player1.width + 2, 40, 10, 10)
local player3 = Player:new(player2.x + player2.width + 2, 60, 10, 10)

And how does it now look:

Events

Most events in Corona are either from the global Runtime, or locally handled. Let’s show locally handled first. Let’s say if you click on a plane, it gains a hit point back. We’ll first add our event handler function:

	function player:touch(event)
		print("touch")
		if event.phase == "began" then
			if self.hitPoints < self.totalHitPoints then
				self:setHitpoints(self.hitPoints + 1, self.totalHitPoints)
			end
			return true
		end
	end

If the user touches on the plane, and it's hit points aren't at the max value, it'll increment it up by 1.

Now, let's listen for the actual touch event:

player:addEventListener("touch", player)

Voila!

View video on YouTube.com

Keep in mind, the same applies to timers (timer), transitions, (onComplete), and collisions (preCollision, collision, postCollision).

Singletons

Singletons and statics are pretty easy in Lua because you just make a table and use that. Statics are similar to a JavaScript way in which you add the variable to Object vs. Object.prototype. I briefly touched on a static class in my achievements tutorial.

The use case for a Singleton is when you want to ensure only 1 instance of a class. In Lua's case, we'll just ensure you're always using the same table. Traditionally, Singletons are actually creating instances and not utilizing static variables. Since scope in Lua is really loose via closures, I prefer the static one. Most would call it a static class. Whatever; it ensures only 1 is ever created, and there is no need to have an instance variable over a static one here.

The use case here is for the gaming network. Currently in Corona, you can only use 1 game network at a time, whether OpenFeint or Papaya Mobile. We'll create a wrapper class for both scores and achievements since those work on both Android and iOS for both services. We want 1 single class to handle this regardless of which network we've chosen, and regardless of what OS we're running on.

Let's create our GameNetwork class as a GameNetwork.lua file in the "com/jessewarden/game" folder structure:

require "gameNetwork"
GameNetwork = {}
return GameNetwork

Notice the lack of a new constructor. Now, let's set the mode variable to a default value, and give her an initialize function so we can run it at our leisure:

function GameNetwork:init(desiredMode)
	self.mode = desiredMode
	if desiredMode == "papaya" then
		gameNetwork.init("papaya", "SecretCodez")
	elseif desiredMode == "openfeint" then
		gameNetwork.init("openfeint", 
					"SecretCodez", 
					"SecretCodez", 
					"Game Title", 
					"SecretCodez")
	else
		print("Unknown mode.")
		return false
	end
end

Cool, let's initialize it before we create our planes:

require "com.jessewarden.game.Player"
require "com.jessewarden.game.GameNetwork"
GameNetwork:init("papaya")
local player1 = Player:new(40, 60, 8, 10)
local player2 = Player:new(player1.x + player1.width + 2, 40, 10, 10)
local player3 = Player:new(player2.x + player2.width + 2, 60, 10, 10)

There she is on line 3. Now, let's add a score variable and a method for saving high scores. We'll use this score variable later to show the user what their score is:

function GameNetwork:addToScore(value, scoreDisplayText)
	self.score = self.score + value
	if scoreDisplayText == nil then
		scoreDisplayText = ""
	end
	if self.mode == "papaya" then
		gameNetwork.request("setHighScore", {leaderboardID = "Level1", score = self.score})
	elseif self.mode == "openfeint" then
		gameNetwork.request("setHighScore", { leaderboardID = "Level1", score = self.score, displayText=scoreDisplayText})
	end
	self:dispatchEvent({name="onScoreChanged", target=self})
end

Notice on line 6 it detects which mode we're in, and calls the appropriate gaming network. Also notice that the signature is slightly different with OpenFeint in that you can show some text with the score. Finally, we dispatch a custom event for those who wish to know when the score value changes.

Now, let's wire it up that every time you heal your plane, you get 2 points. We'll go inside our Player class and add a call to the GameNetwork class' addToScore method. Don't forget to add require "com.jessewarden.game.GametNetwork" at the top:

function player:touch(event)
	print("touch")
	if event.phase == "began" then
		if self.hitPoints < self.totalHitPoints then
			self:setHitpoints(self.hitPoints + 1, self.totalHitPoints)
			GameNetwork:addToScore(2, "Healing")
		end
		return true
	end
end

Inheritance

Now I'll show you how you can extend another class. Let's give our Player class a move function. While small, it reduces all x and y setting from 2 lines of code to 1. In a game where you're constantly moving things, this is a low hanging fruit of reducing the amount of code you have to write. First we'll create our all your base class:

BaseGroup = {}

function BaseGroup:new()
	local group = display.newGroup()
	
	function group:move(x, y)
		self.x = x
		self.y = y
	end
	
	return group
end

return BaseGroup

Now, to extend it in our Player class, we first have to import it up top as require "com.jessewarden.game.BaseGroup", and then modify our constructor from this:

local player = display.newGroup()

to this:

local player = BaseGroup:new()

That's it, you can continue as you were.

Method Overriding

If we want to ensure our player is centered based on his new size because of the health bar, we can override the base class' move method. You store the old method, and call the new:

player.superMove = player.move
function player:move(x, y)
	self:superMove(x, y)
	-- compensate for new height
	local targetY = y
	targetY = targetY - healthBar.height
	self.y = targetY
end

Statics

One use for statics I've consistently been using is for Sprite Sheets. Since a Sprite Sheet is instantiated once, and instances are created from it, it's a perfect use case for static variables (or class level variables). Here's an example that sets up the Sprite Sheets one time, and only one time.

Player = {}

function Player:new()
	if(Player.spriteSheet == nil) then
		Player.spriteSheet = sprite.newSpriteSheet("player.png", 22, 17)
		Player.spriteSet = sprite.newSpriteSet(Player.spriteSheet, 1, 2)
		sprite.add(Player.spriteSet, "planeFly", 1, 2, 50, 0)
	end
	
	local img = sprite.newSprite(Player.spriteSet)
	img:prepare("planeFly")
	img:play()
	return img
end

return Player

I could put outside of the class's constructor function to let it run when the class is imported and not have to do an if then each time the class is new'd, but I'd prefer errors happen when running code vs. require; a lot easier to debug.

Dynamically Instantiating Classes

All globals are stored on _G. Classes, even in packages, become simple tables on _G. Last in wins, although, if you use the require statement, it'll ensure it's only run once if you require it twice. Although packages are supposed to solve class name collisions, in this case they don't, so just ensure your classes have unique names.

In the case of Robotlegs, an MVC framework I ported parts of to Corona, you have the need to instantiate a class by a String name. Since _G is just a normal Lua table, and classes themselves are just tables that have a new method by convention, it's straightforward to get our Player class and dynamically use it:

require "com.jessewarden.game.Player"
local className = "Player"
local class = _G[className]
print("class: ", class)
local obj = class:new(40, 60, 8, 10)
print("obj: ", obj)

Reflection

Sometimes you're not sure what you're dealing with. You'll print out an object and it'll say "table" with some RAM address. Not helpful. Enter a quick reflection function to see what properties your table has and its values.

for key,value in pairs(obj) do
    print(key, ":", value)
end

Notice you can put whatever you want in pairs. Let's put our Player instance:

_proxy			:	userdata: 0x2937954
_tableListeners		:	table: 0x2939060
setHitpoints		:	function: 0x2938aa0
hitPoints		:	8
_class			:	table: 0x28188c0
touch			:	function: 0x2939040
totalHitPoints		:	10

... or even _G:

for key,value in pairs(_G) do
    print(key, ":", value)
end

which gives... well, a really long and interesting output.

If you're smart (or drunk), you'll notice no name or package information is retained with your class. Annoying. For the name, some people will utilize a name property, usually as a String. The timer in Corona SDK actually calls itself "timer" for example. This is typically done on an instance basis. I've been using a classType convention with my instances, a sort of homage to createClassObject in ActionScript 2. That way, whenever I want to know what type I'm dealing with I can print it out since Lua's type function only works with Lua primitives.

Other OOP Options

I stumbled upon this Stack Overflow post which corroborated some of what I read on the Corona forums regarding Lua OOP solutions more to my taste. I went down the metatable route, but I was still learning Lua and found it... well, weird. You have to create all these tables and wire them up just to... create a table. Granted, you could abstract it into a base or utility class, but seriously?

Additionally, the Corona DisplayObjects and other classes are core C classes with magic on top. As such, you can't affect them with metatables like other Lua tables. Thus the standard fare of wrapping them to extend their functionality doesn't work.

Then I found middleclass. After trying to wrap my head around making prototypes in Lua for the mere sake of just getting simple inheritance working (BLAH BLAH BLAH META TABLE REFERS TO ANOTHER TABLE BLAH BLAH META OMG WHAT TABLE ARE WE TALKING ABOUT HERE!!!?!?!?!), middleclass was a nice breath of fresh air.

Over time, though, I had a hell of a time getting it to work extending the native DisplayObjects in Corona. Additionally, I actually didn't end up doing that much inheritance; I'd usually just add existing behavior to built in Corona DisplayObject classes. Now that I actually know what I'm doing 8 months later, I should take a look at it again, it's just what I have above works without the need of metatables.

Finally, Mr. Lindquist has a great metatable video tutorial.

Scope

I left out a discussion on scope in this article on purpose. Lua's scope is confusing and has a lot of rules to remember, more than JavaScript. If you follow the 2 simple rules, everything above should work just fine:

Use a . when you want to access a variable/property.

Use : when you want to call a function/method.

Everything else is doing something functional or lower level in Lua and is irrelevant in my opinion to doing classes in Lua. A discussion on scope on Lua would take a whole article to do it justice. I've tried to ensure no scope problems happen by using the techniques above. Where scope rules rear their ugly head is when you start passing in functions instead of tables to event handlers:

timer.performWithDelay(1000, onTimer)

Your function, onTimer, isn't scoped to anything; if you print out self inside of itself, it'll be nil. If it's a closure, you can access other objects, but then you start doing things like local self = player. At that point, that's why God invented 9mm's for eating.

The same goes for custom callback handlers, which are also functions. Instead of defining a function player:timer method on your class, you instead go function player:onTimer, and then in the listener go:

local theTimer = timer.performWidthDelay(1000, player.onTimer)

Same problem; your function is actually scoped to the timer; if you trace out self, it'll refer to theTimer. However, if you make your method a variable instead of scoped to yourself by using a dot like function player.onTimer, and then do the same code above, self will refer to your actual class object like a static.

If you're of the functional mind, you can definitely go down the route of advanced closures, and use local's to get your scope back on track. For the rest of us who want to get things done and don't want to go the Delegate.create route, just stick to the above and you'll be fine.

Encapsulation and Accessors

You'll notice all methods and variables are public. Additionally, there are no access rules, like getters/setters, since I'm not using any of Lua's special sauce functions like setmetatable or __index. Additionally, while each Lua file only creates 1 global variable, you can access all functions on that object. If you want to emulate private, you can put all your methods as local, put on a metatable object, and use that.

So far the Ruby & Python guys seem fine with convention & documentation over access, so... I'm not stressing it.

Conclusions

Creating classes in Lua is simply making a table, giving it a new method, and returning that table. Inside the new method you add additional functions to that object you're creating using a colon to ensure it retains self scope inside the function. For callbacks, you just define the event name, such as timer, as a function/method inside your class, and it'll get called with scope intact just fine. For overriding, you just store the super method as a function reference, and call that in your new, same named function. Inheritance is just calling new, and decorating the same object more in your sub-class. While Corona does have packages, all package information is lost so ensure you don't have same named classes if you can help it. All classes become globals defined _G, the main global table in Lua.

I stole most of my ideas from Darren who helped me get over some large learning curves. Also, Jonathan Beebe gives an overview of modular classes that are similar to what I do but using metatables. He also wrote another interesting post about using modules, and why he and many others (myself included) do not utilize the module function. The lua.org online manual also has some neat examples of how metatables can be applied.

Using the above, you should be able to get all the OOP you need, shield you from Lua's scope rules, and help make your code more manageable as it grows through Composition. Further learning suggestions are the debug object, pcall, and assert. As you start creating more module methods, those last 2 functions will really help you write better, easier to debug and use classes. Good luck and hope this helps!

9 Replies to “Lua Classes and Packages in Corona”

  1. Display objects are regular Lua tables. It’s the metatable where the C code is hiding. I found that you can replace the metatable with your own as long as you keep a copy of the original metatable so that your custom one can pass things to it when needed.

    With Player:new(), I would recommend using Player.new() instead. It’s a static method, so the reference to self that the : adds isn’t really needed because you already have a reference to Player. Plus, it helps to differentiate static and instance methods by sight for someone who is quickly reading your code.

    1. Thanks Josh! I’ll ignore the 1st paragraph until I get a Charlie Gordon shot.

      To your 2nd paragraph, I agree too, it’s more consistent with the API they provide. I’ll give a whirl!

  2. Great and very helpful tutorial. Thanks.

    Can you post a link to the sourcefiles for this tutorial or are they on github?

    Thanks.
    David

  3. Thanks for the great write ups! Definitely making the transition into Corona a whole lot less painful. I checked out your code for the top down Tyrian esque game as well.

    I am working on a few general libraries that should hopefully make Corona development even less painful – a TouchManager for figure out how the playing is tapping, swiping, etc (multi touch as well) and also some ParticleEffect engine stuff. While I wish Flash was viable (and still might be…) on mobile I am glad people like you are moving into the Corona community.

  4. Thanks for the great article. I’ve tried the meta table thing but I found it to be much work for… creating a table … like you said. Giving your method a try … I’m a AS3 developer and I’m used to working with object oriented languages and this “Lua thing” is very confusing to me.
    Thanks again for explaining everything… it saved me a lot of time.
    Bookmarked and Stumbled!

  5. what goes where?
    i have a file lua for the class and the main.lua… can you distinct where i need to put every portion of code?

  6. @andrea main.lua always goes in your root folder.

    For classes, you have 2 options: to use packages or not. Packages are synonymous with folders. If you don’t provide/use a package, then the class goes in root (next to main.lua).

    If I make a class, Cow.lua, and save it in root (next to main.lua), then:

    Cow = {}
    function Cow:new()
        local cow = {}
        return cow
    end
    return Cow

    Then anyone, including main.lua and others, can simply go:

    require "Cow"
    local myCow = Cow:new()

    If, however, you wish to use packages, then it matters where the file Cow.lua goes. If I used com.jessewarden.animals, then I’d:
    1. make a folder called “com”
    2. inside of “com”, make a folder called “jessewarden”
    3. inside of “jessewarden” make a folder called “animals”
    4. inside of “animals” put my Cow.lua

    Then, whoever wants to use it goes:

    require "com.jessewarden.animals.Cow"
    local myCow = Cow:new()

    Make sense?

Comments are closed.