Ways to implement OOP in Lua

I’m loving learning Lua / Corona SDK but being a 20+ year C/C++ vet I’m really anal about clean, structured, well organised (and readable) code and I’ve noticed a strong tendency for Lua code to start to get really messy once it get’s above a certain size.

To this end I wanted to try and find the best way to implement OOP principals in Lua and have a workaround for the fact that Lua doesn’t natively support classes.

The Lua “bible” Programming in Lua 2nd Ed suggest two ways - the first uses metatables as a way to organise code and have inheritance (but isn’t very strong on encapsulation), and whilst I can see how it works I’m still having problems getting my head round the whole metatable thing.

The other way suggested was to use a single “function” to represent an entire class and encapsulate all of the class fields and methods as local entities to the “function” and then have that “function” return a table which is basically the public interface to the class.

eg.

 function newClass( initial values )  
 local self = { a=0, b=1 } Note you always have to reference self when accessing the table  
 although you COULD also store many tables or many locals  
  
 -- You can also code "constructor" initial setup code here!  
  
 local method\_a = function(a) self.b = a   
 end  
  
 local function method\_b(c) return self.a + c Note - you wouldn't have to reference the functions via self as they're  
 all local - but you would probably need to be aware of forward references  
 end  
  
 -- Here's the magic...  
 -- or add some more "constructor" code here!  
  
 return {  
 method\_a = method\_a, method\_b = method\_b name  
 }  
 end  
  

This would then be accessed like so

  
 local a = newClass( initial values, table or nill etc )  
  
 a.method\_a(x)  
 y = a.method\_b(c)  
  

IMHO this is a much cleaner approach but I’d like to invite anyone with any greater knowledge of Lua to comment and potentially suggest improvements.

Whilst this nicely provides the encapsulation and the “newClass” can be stored in a module and “required” it’s a bit weak when it comes to inheritance - however I don’t see that as a limitation as I’m leaning more to a component based system for my framework rather than a hierarchical one (ie Game objects contain “instances” of the functionality they need rather than inheriting it via a convoluted hierarchy).

To demonstrate this further I’ve knocked up a quick particle system which might be of interest to some.

Put this code in main.lua

display.setStatusBar( display.HiddenStatusBar )  
  
-- The particle system class  
-- Contains two classes - the individual particle and the particle system (controller)  
local particleSystem = require "particle"   
  
local particleCtrl,gameGroup,background  
  
-- --------------------------  
  
local function touched(event)  
  
 if event.phase == "began" then particleCtrl.spawn(50,150,event.x,event.y) end  
 if event.phase == "moved" then particleCtrl.spawn(2,20,event.x,event.y) end  
 -- if event.phase == "ended" then particleCtrl.reset() end  
  
end  
  
-- --------------------------  
  
local function loop(event)  
 particleCtrl.update()  
end  
  
-- --------------------------  
  
background = display.newRect(0,0,display.contentWidth,display.contentHeight)  
background:setFillColor(64,0,64)  
  
gameGroup = display.newGroup()  
  
-- Create the controller object  
particleCtrl = particleSystem.newParticleController(500)  
  
gameGroup:insert(particleCtrl.group())  
  
Runtime:addEventListener( "enterFrame", loop )   
Runtime:addEventListener( "touch", touched )  
  
-- End of file...  

and this code in particle.lua

module(..., package.seeall)  
  
--[[  
 OOP in LUA - Game objects - is this the best way to implement them.  
  
--]]  
  
local mRand = math.random  
local mCeil = math.ceil  
local mSin = math.sin  
local mCos = math.cos  
local mRad = math.rad  
  
-- --------------------------------------------------------------------------------------------------------  
-- --------------------------------------------------------------------------------------------------------  
-- The actual particle class  
function newParticle(group)  
  
 -- -------------------------------  
 -- Object data  
  
 local self = { x=0,y=0,a=0,xinc=0,yinc=0,aStep=0,life=0,image=nil,active=false }  
  
 -- Create (Construct) the particle as a small square (but hide it until it's needed)  
  
 self.image = display.newRect(0,0,4,4)  
 self.image:setReferencePoint(display.CenterReferencePoint)  
  
 self.image.x = self.x  
 self.image.y = self.y  
 self.image.isVisible = false  
  
 group:insert(self.image)  
  
 -- -------------------------------  
 -- Object methods  
  
 local function init(life, xLoc,yLoc)  
 -- pick a random colour and direction, reset the position and make active  
  
 self.x = xLoc  
 self.y = yLoc  
  
 local r,g,b = mRand(192,255),mRand(192,255),mRand(192,255)   
 self.a = 1  
  
 self.image:setFillColor(r,g,b) -- random colour  
  
 self.image.x = self.x  
 self.image.y = self.y  
 self.image.isVisible = true  
 self.image.alpha = self.a  
  
 local angle = mRad(mRand(1,36) \* 10) -- pick a random angle of travel  
 local speed = 2 + (mRand(1,20) \* 0.1) -- and a random speed  
  
 self.xinc = speed \* mSin(angle)  
 self.yinc = speed \* mCos(angle)  
  
 self.life = life  
 self.aStep = 1 / life  
  
 self.active = true  
  
 end  
  
 -- -------------------  
  
 local function update()  
  
 if self.active == false then return end  
  
 self.yinc = self.yinc + 0.05  
  
 self.x = self.x + self.xinc  
 self.y = self.y + self.yinc  
  
 self.a = self.life \* self.aStep  
  
 self.image.x = self.x  
 self.image.y = self.y  
 self.image.alpha = self.a  
  
 self.life = self.life - 1  
  
 if self.life == 0 then   
 self.active = false  
 self.image.isVisible = false  
 end  
  
 end  
  
 -- -------------------  
  
 local function reset()  
  
 self.life = 0  
 self.active = false  
 self.image.isVisible = false  
  
 end  
  
 -- -------------------------------  
 -- The public interface  
  
 return {  
 init = init,  
 update = update,  
 reset = reset,  
 }  
end  
-- --------------------------------------------------------------------------------------------------------  
-- --------------------------------------------------------------------------------------------------------  
-- --------------------------------------------------------------------------------------------------------  
-- The particle controller class  
function newParticleController(maxParticles)  
  
 -- ------------------------------  
 -- Class storeage space  
  
 local group = display.newGroup()  
  
 local particles = { }  
 local numParticles = maxParticles  
 local last = 1  
  
 -- Create the required number of particle objects here  
  
 local p  
  
 for i=1,numParticles do  
  
 p = newParticle(group)  
 table.insert(particles,p)  
 end  
  
 -- ------------------------------  
 -- Class method definitions  
  
 -- -------------------  
 -- Trigger a set number of particles to be reset at the specified position  
 -- start from where we last left off  
  
 local function spawn(num, life, xLoc, yLoc)  
  
 local current = last  
  
 while (num \> 0) do  
  
 particles[current].init(life, xLoc,yLoc)  
 num = num - 1  
 current = current + 1  
 if current \> numParticles then current = 1 end  
 end  
  
 last = current  
  
 end  
  
 -- -------------------  
 -- Update all of the active particles  
  
 local function update()  
  
 for i=1,numParticles do  
 particles[i].update()  
 end  
  
 end  
  
 -- -------------------  
 -- Reset all the particles regardless  
  
 local function reset()  
  
 for i=1,numParticles do  
 particles[i].reset()  
 end  
  
 end  
  
 -- ------------------------------  
 -- Return the public interface  
  
 return {  
 group = function() return group end, -- simple "getter"  
  
 spawn = spawn,  
 update = update,  
 reset = reset,  
 }  
end  
-- End of file...  

Note on the implementation - rather than dynamically creating particles when the controller “spawns” them - the above code actually creates a “block” of particles at the start and then just recycles them by making each one visible and active as required.

Given my “limited” understanding of Lua - I’m assuming that including some kind of “destructor” function that performs internal cleanup should also be included as part of the interface, and after calling this function setting the “object” variable to nil should free up any claimed memory (or at least inform the GC that it’s available to be freed).

Any comments / feedback is much appreciated (FWIW - I’ll repost this to the code exchange at the point I get my full subscription going).

Jon…
techdojo@gmail.com
www.whitetreegames.com
[import]uid: 7901 topic_id: 9155 reply_id: 309155[/import]

This is a pretty clear description of how to implement this style of OOP in Lua. It’s certainly given me food for thought for my current game.

Thanks for posting this - hopefully more people will comment. [import]uid: 26769 topic_id: 9155 reply_id: 33431[/import]

While most people recommend meta-tables for doing OOP in Lua here’s the approach I use:
http://developer.anscamobile.com/code/object-oriented-sample-game-framework

It’s very similar to what you’re doing but with a couple crucial differences. First off, I directly add methods to display objects rather than putting the display objects as properties of my abstract code objects; doesn’t make a big difference but I think the code is a little simpler that way.

Second, I add methods as colon functions with a self parameter. I’m actually surprised you can use local functions the way you do because that violates the spirit of declaring them as local. I’ll just assume that does work because why post that much non-working code, but I consider it a bug that you can call local functions outside of the scope where they were declared.

ADDITION: oh wait I just noticed the returns at the end where you setup public declarations for the functions. That just seems redundant to me; if you want the functions to be public then don’t make them local to begin with. [import]uid: 12108 topic_id: 9155 reply_id: 33449[/import]

Thanks for the feedback - I’ve downloaded your source and I’ll have a look through it.

I did consider using display objects as a base class (which is how I used to use Haxe when I do Flash development) - although in this case I just wanted to start with a clean slate and have each element as a component of my game structure (plus I think it’s a little more portable that way)

I used “local” more in the C static or C++ private sense to keep the functions within the “namespace” of the class (the namespace being provided by the containing function and not having to prepend the name of the table to each function - I hate the way I have to do that in C++) I understand your point, but I just think of local as meaning something else, the whole thing seems to work using “closures”, “upvalues” and other Lua concepts I’m still digesting :sunglasses:

I think using the hidden self parameter with the colon just makes it more confusing to the Lua beginner - it confused me with some methods requiring it and others not :sunglasses:

The thing that still “kettles my swede” is how functions are “created” especially anonymous ones, when coming from a structured C background where everything is laid down in stone prior to the compilation - in the latest iteration of my text rendering code I “create” a custom callback function that get’s added to “hyperlinks” and is customised with the index of the link, afterwards when I re-read it, it did seem logical in a way and made the actual callback process quite clean and simple - I couldn’t imagine how I’d have coded it in C++, but considering Lua is written in C everything doable in Lua must be possible in C in some way :sunglasses:

I was going to mention the table that’s returned at the end and when I think of it as a public interface it makes more sense - I agree it does seem to be a little overkill but again it seems like the vTable used by C++ classes to implement virtual functions - but by not making the functions local surely you loose access to the local data within the original “namespace” function and defeat the objective of trying to do OOP properly in the first place.

Jon… [import]uid: 7901 topic_id: 9155 reply_id: 33490[/import]

That’s the great thing about this forum, I posted what I’d learnt and someone has run with it and I end up learning even more!

Great follow up LordMooch.

Jon…
[import]uid: 7901 topic_id: 9155 reply_id: 33963[/import]

I’ve been having a play around with Jon’s code as I wanted to incorporate SQLite into it. Well, not into Jon’s particle class, but into my own code. The stuff posted below is my attempt to do that. It works really well, although I have no idea if it’s good/bad Lua code.

All the code below is held inside my database.lua file. The first part of the code is some simple stuff that allows me to easily get the database path and a reference to the database itself. The third function will execute the SQL passed to it and is an easy way of allowing the execution of SQL code that doesn’t return records, e.g. inserts, updates, deletes. With these bits n bobs out of the way, performing SQL becomes quite trivial.

  
module(..., package.seeall)  
  
----------------------------------------------------------------------------------  
-- Database helper file  
--   
-- Database stuff  
--  
----------------------------------------------------------------------------------  
require "sqlite3"  
  
dbName = "data.sqlite3"  
----------------------------------------------------------------------------------  
--  
-- Stuff  
--  
----------------------------------------------------------------------------------  
  
-- Return the path and file name to the Database  
function getDatabasePath()  
  
 return system.pathForFile(dbName, system.DocumentsDirectory)  
  
end  
-- Return a database connection  
function getDatabase()  
  
 return sqlite3.open(getDatabasePath() )  
  
end  
-- Run SQL that returns no records  
function execSQLNoRecords(sql)  
  
 local db = getDatabase()  
  
 db:exec(sql)  
  
 db:close()  
  
end  
----------------------------------------------------------------------  

The next part of the code is the “OOP” stuff, as modelled on Jon’s code posted earlier. Essentially, what I’m doing is mapping one of my database tables onto a class. The class gives me the ability to load and save a record.

----------------------------------------------------------------------------------  
-- Classes  
----------------------------------------------------------------------------------  
function dbTeam\_Match(teamID)  
  
 -- These are the columns from the database table  
 -- Prefixed with db\_ so that we know they represent the table's columns  
 local self = {db\_teamID = 0, db\_fame = 0, db\_cash = 0}  
  
 -- Set the Primary Key based on the parameter passed in  
 -- Would be usual to do some checking that it's valid  
 self.db\_teamID = teamID  
  
 -- Open a record  
 local function openTeam\_Match()  
  
 local db = getDatabase()  
 local sql = "Select \* From Team\_Match Where TeamID = " .. self.db\_teamID  
  
 for row in db:nrows(sql) do  
 self.db\_teamID = row.TeamID  
 self.db\_fame = row.Fame  
 self.db\_cash = row.Cash  
 end  
  
 db:close()  
  
 end  
  
 -- Save object  
 local function save()  
  
 -- Create the Update statement for saving this object  
 local sql = "Update Team\_Match "  
 sql = sql .. "Set Fame = " .. self.db\_fame .. ", "  
 sql = sql .. "Cash = " .. self.db\_cash .. " "  
 sql = sql .. "Where TeamID = " .. self.db\_teamID  
  
 -- An update returns no records, so we use the helper function declared earlier  
 -- This is a great example of how I am running SQL code without needing  
 -- to reference a database or the database file or the documents directory etc  
 execSQLNoRecords(sql)  
  
 end  
  
 -- Properties  
  
 -- The idea of the properties is that they serve two purposes within the one function  
 -- 1. You can call this function with a nil parameter  
 -- and it will just return the current value  
 -- 2. You can call the function with a non-nil parameter  
 -- and it will update the object's value and then return that value  
 local function fame(newValue)  
  
 -- If not nil then update class value   
 if (newValue ~= nil) then  
  
 -- Here we ensure the value is within certain bounds  
 -- This is one of the best uses of properties  
 if (newValue \< 1) then  
 newValue = 1  
 end  
  
 if (newValue \> 100) then  
 newValue = 100  
 end  
  
 self.db\_fame = newValue  
 end  
  
 -- Return value  
 return self.db\_fame  
  
 end  
  
 local function cash(newValue)  
  
 -- If not nil then update class value   
 if (newValue ~= nil) then  
 self.db\_cash = newValue  
 end  
  
 -- Return value  
 return self.db\_cash  
  
 end  
 -- If we have a valid ID open the record  
 -- This allows us to automatically populate an object if a valid ID is passed in  
 if (self.db\_teamID \> 0) then  
 openTeam\_Match()  
 end  
  
 return {  
 openTeam\_Match = openTeam\_Match,  
 save = save,  
 fame = fame,  
 cash = cash  
 }  
  
end  
  
-- End of Classes  
----------------------------------------------------------------------------------  

Now some code to actually use the class.

local database = require("database")  
  
-- Let's assume we've got two teams; Home Team and Away Team  
local homeTeamID = 1 -- Primary Key value in the database  
local awayTeamId = 2 -- Primary Key value in the database  
  
-- Let's load the Home Team data into our object  
local homeTeam = database.dbTeam\_Match(homeTeamID)  
  
-- Now let's pretend the Home Team won the match and it's Fame has increased  
local homeTeamFame = homeTeam.fame() -- Call method with nil parameter so it just returns the value  
homeTeam.fame(homeTeamFame + 10) -- Call method with a value so it Updates the value in the object  
homeTeam.save() -- Save this object in the database  
  
-- Do the same for the Away Team, but decrease their Fame  
local awayTeam = database.dbTeam\_Match(awayTeamID)  
local awayTeamFame = awayTeam.fame()  
awayTeam.fame(awayTeamFame - 10)  
awayTeam.save()  

And that’s it. Works a treat, but would love to here what you lot have got to say. [import]uid: 26769 topic_id: 9155 reply_id: 33938[/import]

Jon, I noticed that you use a table called self, and that your comment says you could use a list of locals instead. Any reason for using self over a list of locals? [import]uid: 26769 topic_id: 9155 reply_id: 35948[/import]

No reason - I used self just as a table to hold all the fields to try and keep things a little cleaner. [import]uid: 7901 topic_id: 9155 reply_id: 35954[/import]

Hmm, can’t decide which I prefer now. On the one hand, I’m used to using something more like a list of locals. On the other hand, the use of self keeps it all kind of encapsulated and reminds me that they’re internal variables for the object. Well, that’s how I think of them.

:slight_smile: [import]uid: 26769 topic_id: 9155 reply_id: 35956[/import]

First of all thanks for the examples!

lordmooch is the code that has the classes comment:

  
----------------------------------------------------------------------------------  
-- Classes  
----------------------------------------------------------------------------------  

Is that in the same file that had this comment?:

-- Database helper file [import]uid: 12090 topic_id: 9155 reply_id: 42703[/import]

Yes, it is. I store all the data access code and the classes inside a database.lua file. [import]uid: 26769 topic_id: 9155 reply_id: 42706[/import]

Lord Mooch,
In the database code I have seen before it always had something like this:

--Handle the applicationExit event to close the db  
local function onSystemEvent( event )  
 if( event.type == "applicationExit" ) then   
 db:close()  
 end  
end  

I didn’t see that in your code. Is it because it is not needed (you are closing somewhere else) or that you didn’t post the entire database.lua?

If it is in you database.lua file could you please post it in its entirety?

Thanks for all the help! [import]uid: 12090 topic_id: 9155 reply_id: 42787[/import]

@ lordmooch

Since you have:

local database = require("database")  

do you think you will ever use:

[code]
– Return a database connection
function getDatabase()

return sqlite3.open(getDatabasePath() )

end
[/code] [import]uid: 12090 topic_id: 9155 reply_id: 42792[/import]

That code isn’t present because it wasn’t needed to illustrate the points I was making. I’m not sure how useful the code is given that my use of “db” is limited to a local definition within functions. But perhaps i need some way of closing “db” if a function is ever interrupted by the system. I’ll have to think about it!

As for posting my database.lua fully, well effectively I already have. The generic stuff at the start of the file is all you really need. The classes stuff is specific to each person’s implementation. [import]uid: 26769 topic_id: 9155 reply_id: 43068[/import]

AndrewB, I use that function all the time as it returns an open SQLite database reference. [import]uid: 26769 topic_id: 9155 reply_id: 43070[/import]

I understand that the function returns a sqlite db reference. What does this return?:

local database = require("database")  

does it return the same thing? [import]uid: 12090 topic_id: 9155 reply_id: 43128[/import]

No, that line returns a reference to database.lua itself. So you can access the components within database.lua in your code. [import]uid: 26769 topic_id: 9155 reply_id: 43153[/import]

@LordMooch

Thanks for your patience and help. [import]uid: 12090 topic_id: 9155 reply_id: 43257[/import]