Tutorial: Modular Classes in Corona

Posted by

Dog class in Corona SDKWith the previously published tutorial on External Modules raking in a whopping 54 comments (and counting!), I decided it’s time to write a follow-up that goes a little more in-depth with Lua’s new recommended approach to external modules, and also cover another handy concept that I know you’ll find extremely useful.

One of the best things you can do for your app, especially if you foresee it growing to a significant size by the time you’re finished, is to use what’s known as “classes” to create and manage the separate objects in your app. In this tutorial, I’m going to show you how you can use what you learned in the previous tutorial to declare classes—so you can get the best of programming efficiency and project organization all at the same time.

What is a Class?

If you want, you can bloat your brain by reading all about it on Wikipedia, but that’s not really necessary. By the end of this tutorial, you’ll have a basic understanding of what classes are, and more importantly, how you might use them to your advantage.

Now before I continue, let me clarify that Lua itself does not have a class system built-in, and what I’m going to walk you through is a very simple implementation, but it’s easy and effective—that’s what really matters, right?

So all technicalities aside, let us begin…

Man’s Best Friend

In the example I’m going to walk you through, we are going to be creating a “dog” class and will be working mainly with dog.lua, which will have all things related to our “dog” object. Once we’re finished with that, main.lua is where we’ll be putting our brand-new class to use.

Let’s start off with dog.lua—and notice how it’s structured in a similar manner to the modules we created in the previous tutorial:

Next I’ll attempt to break the module up … one bite at a time. :-)

First we create the local table (which is returned at the end of the module) and a metatable which is essential to getting classes to work.

The next section is where our private functions are defined. These functions (or function in this case) are local and can only be accessed within that same module, since it’s not part of the dog table we return at the end of the module.

After that is our public functions. These can all be accessed outside of the module, but are still local because they are all included within our dog table, which is local. For now, let’s focus on one function in particular: dog.new()

Object Construction

The dog.new() function is what’s known as a constructor function, and is essential to this implementation of classes in Lua. The constructor should be the first thing you call when attempting to make a new instance of a dog object from an outside module.

The first thing we do in this constructor function is create a table with properties that will be unique to a new instance of dog (name and age), and then set the metatable to that of our dog table (or “class” as we’ll refer to it from now on). When the function returns, you get a brand new instance of our dog class, which is unique, but has inherited all the methods and properties of our dog class, which includes the rollOver(), sit(), and bark() methods.

Here’s the meat and potatoes of how it works:

When you set the metatable in the constructor function—dog.new()—and return it, you’re effectively creating a new “instance” of our dog object that’s completely unique, but inherits all the methods and properties of our “dog” class. The result is a completely unique object. If you skip the constructor and just return the dog table as is, you won’t get a unique instance of “dog” whenever you call new(), you’ll simply get a new reference to it which is obviously not what we’re trying to accomplish.

main.lua

Now, create a blank file, save it as main.lua and follow along.

At the top of the file, we’re going to require-in our dog.lua module (and make it local too!):

    local dog = require( "dog" )

Now we can start creating dogs! Let’s make two (and yes, those are real dog names!):

    local dog1 = dog.new( "Yai-Ya", 4 )
    local dog2 = dog.new( "Toki", 1 )

So now we have two completely separate instances of our dog class. Add the following lines to main.lua and run it in the Corona Simulator (with the Terminal so you can see print statements):

    dog1:printAge()
    dog2:printAge()

And this is what you should see in the Terminal:

    Yai-Ya is 28 in dog years.
    Toki is 7 in dog years.

Do you see what just happened there? We have two separate dog objects (easily seen by the fact that they have different names, and different ages), yet they both inherited the same methods! In this case, we saw the printAge() method in action, but we can do more:

    dog1:rollOver()
    dog2:sit()

…Which produces the following in the Terminal:

    Yai-Ya rolled over.
    Toki sits down in place.

Take a moment to look at dog.lua again and study how everything is working. The basic point is that we have two separate dog objects, they have methods (rollOver, sit, bark, etc.) as well as properties (name, age) and we only had to do everything just once in our dog.lua module/class.

By now, you should start to see how powerful this is… and I’m guessing the wheels are starting to turn in your head as far as how you might implement this concept into your own app—at least I hope so. :-)

Wrapping Things Up…

While you may never need to create dogs in the same manner as we just did, I encourage you to take this simplistic example and see how you can apply the same logic to your own app or game. What if you had a separate module for every type of character, enemy, game mode, or item in your game? Wouldn’t it be nice to just open up “goomba.lua” and add/edit what’s needed instead of opening up a huge file and wasting all your energy just sorting out and remembering how you did things? You can even extend your classes and make sub-classes that build off of one another—the possibilities are endless.

In addition to all that, programming in this manner simply works wonders for updating your app, adding functionality, and fixing bugs. Another benefit to programming in such a modular way, which might not be readily apparent, is that it’ll help make it easier for a new team member to jump in and get up to speed with how things work with your code if that need ever arises.

Now it’s one thing to go through a tutorial, and another to take the concept and use it in a real-world scenario. As with all other things, practice makes perfect, so setting aside some time to go through some exercises to explore the concept of modular classes in Corona will greatly benefit you in the long run.

Here’s one to get you started:

Add a new property to our dog class—color—and have the constructor function create a new display object (of a dog) and tint the image to the color that’s specified when a new instance of a dog is created in a separate module (e.g. main.lua).

Easy enough right? And speaking of applying color tints to display objects…

Corona SDK subscribers currently have access to this brand new feature via the Daily Builds, plus tons of other features that are not publicly available yet. Become a subscriber today to download the latest Daily Build and also take advantage of much faster build times.

Ready to get started?

Create amazing games and apps for iOS & Android

71 Comments

BobSeptember 29th, 2011 at 6:28 pm

Hot dog! You weren’t kidding when you said you’d have a follow-up

madmusic6September 29th, 2011 at 7:22 pm

Excellent!
Really helpful for us and a bonus explanation of a constructor.
@Jonathan (Ask Carlos for a raise!)
I have changed bebeegames to work like this but not sure if I’ve done it all correct. Have you updated bebeegames without seeall?

I am using director that has been changed to incorporate this, (I’ve also tried the other scene mangers around) but when I go back to my menu page, the memusage is always higher, (not constantly climbing, but adding the data form a module). I’ve tried using the unriquire function (spriteloc) but no effect. If a menu page memusage is say 200, then memusage goes up to say 350 after changing scenes, surely it should go back to 200 when changing back to menu page. (Although I haven’t been putting the { __index = dog } part in so maybe that will make a difference)? Anyone else had the same problem?
Many Thanks
madmusic6

Jonathan BeebeSeptember 29th, 2011 at 9:53 pm

@madmusic6: I haven’t personally had a chance to use director since finding out about module() being deprecated, so I can’t really comment as to why memusage is still going up. I’m not sure if using what’s in this tutorial would apply much to that though. All I could tell you right now is to make sure you’re properly unloading all objects, event listeners, and any variables that might get stuck in memory.

Regarding the beebegames class, it’s actually pretty outdated right now as I haven’t had a chance to touch that for quite a few months. At this point, I’d recommend using a different script as I’m not sure when that will be updated.

JonSeptember 30th, 2011 at 1:41 am

@JonBeebe

I totally “got” everything you said regarding the previous tutorial and in the spirit of that (removing the module declaration, making everything local etc etc), and I wrote a load of examples demonstrating the implementation and use of some common design patterns often used in games and how to properly (or at least IMHO properly ) implement all the major OOP concepts, singletons, factories and pools in pure Lua that I was planning on sharing in the near future – I’m currently using them to destruction in my tilemap engine that handles native “Tiled” output and they’re working great!

However the one thing about Lua that has me completely stumped is the whole “metatable” thing and why you even need the line

“local dog_mt = { __index = dog } – metatable”

I’ve never used it and I can’t see what difference that lines makes (and why the code wouldn’t work if it was removed), it’d be much appreciated if in the next “follow up” if you address the issues of meta tables and how and why they should be used (or at the very least explain the rationale behind the above line).

Cheers!

NickSeptember 30th, 2011 at 2:38 am

Thanks Jonathan.

You have a really good way of explaining and demonstrating concepts in an easy to understand way with practical examples.

The app I’m working on at the moment has grown (as I expect they all do) and so I’ll probaby try a modular framework for all my future apps to manage the app more efficiently and will use your article as the starting point.

Thanks again.

Nick

EmiSeptember 30th, 2011 at 2:40 am

What about having two different methods that are referencing to each other?

For example:

local function getSomething()
getSomethingMore()
end

local function getSomethingMore()
getSomething()
end

Will it work?

I’ve been having this problem with some other OOP approaches using Lua and the only way I found is using public methods instead of private ones.

Regards,
Emi

ThomasSeptember 30th, 2011 at 3:23 am

Is there a way to adapt this code to create kittens? :-) No really, here’s a serious question: what’s the best way to destroy the instantiated objects? Is just saying dog1=nil enough to completely remove all associated variables from memory or would an extra method like the aptly named CleanUpAfterDog() be necessary inside the class dog (like a reverse constructor method), that set the “internal” variables and functions to nil first?

ThomasSeptember 30th, 2011 at 3:25 am

@ Jon: I second that: I’m getting starting on metatables and metamethods myself and would love Jon Beebe to help out a bit!

madmusic6September 30th, 2011 at 4:35 am

@Jonathan
Many thanks, I will use your ideas of putting objects into a table (and listeners and timers etc), so I can (Destroy) them and pause etc. Now I know how to use constructor, I can build my own class to do everything I need. (learning from the master).
May I sugest you put a note/update notice on the bebeegames download page to say it is on hold, as newbies might start using it, as it was good.
Lets hope all 3rd party libs get updated, as I have learnt a lot and can do things myself now, but newbies, because of links and suggestions from many forums,will jump straight into outdated libs. For example, someone new to the site today will download the samples, and straight away they are given ui.lua which is using module(). (although ui.lua is not 3rd party).
+1 to metatable tutorial.

JonSeptember 30th, 2011 at 6:00 am

@Thomas – Re destroying kittens 8-)
In my experience, this is what I’ve found out.
Lua garbage collection is reference counted, so that for memory to be released (when the GC decides to kick in) all references must be removed.

In the above example setting dog1 = nil will remove the reference to the local table (newDog) that was created in the dog.new constructor (the original reference was lost automatically when newDog went out of scope at the end of dog.new())

If you did something like dog3 = dog1 and then set dog1 = nil then a reference will still exist and the memory wont be released.

When I create a complex class (ie one that may contain references to other objects or tables) I always create a :destroy() method (or function) that is responsible for clearing out any references that the object contains, I always call this before I want to “release” the object and then follow it by setting the object variable to nil.

ie.

tom = objectType.new()

….

tom:destroy()
tom = nil

At this point any and all references to “tom” should have been removed and “tom”‘s memory etc will be released the next time the GC runs (which you can force using the collectgarbage() function), obviously if I do something like

fred = tom

and then forget about it – tom’s memory will still be allocated until the value of fred changes.

Hope this helps.

JohnSeptember 30th, 2011 at 6:17 am

@ Jon and Thomas

Regarding the metatables thing, when dog.new returns a new dog, the dog table contains only two entries, “name” and “age”. If you ask for dog1:rollOver() [which is syntactic sugar for dog1.rollOver(dog1) btw], lua would terminate saying that no such entry “rollOver” exists. However, the metatable shenanigans overrides this behaviour: if a table entry does not exist, lua will look for the entry in “dog”. So when asked for dog1.rollOver(), lua actually accesses dog.rollOver() which does exist.

Another method to achieve the same thing would be to just add entries rollOver() etc to newDog when it is created. But I think this would cause problems later if you use inheritance.

I don’t personally believe in OO programming, however. I don’t use any of this sort of thing myself as I don’t think it actually makes things simpler or more elegant. There’s no real evidence that the OO revolution has made programming easier, more reusable, easier to collaborate on, or less buggy the traditional programming. Just an opinion!

IngemarSeptember 30th, 2011 at 6:32 am

I’ve found this site to be quite helpful when familiarizing myself with Lua metatables.
http://j.mp/qK9qDR

kawikaSeptember 30th, 2011 at 6:53 am

There are a few good youtube metatable videos by John Lundquist that are very helpful. Here is an initial link: http://youtu.be/CYxMfVy5W00

JonSeptember 30th, 2011 at 8:33 am

@John,
Ahhhh (*lightbulb moment*) – I see, normally I would have just added the :rollOver(), :sit() and other methods directly to the newDog { } table that’s returned and hence they’d have been present.

I’ve often wondered if this would cause excessive code bloat in my scripts basically adding in all of these extra functions all the time, does it mean I end up with multiple copies of each function??? – I can see the point now (although in truth does either method make much difference?)

As for inheritance – I would normally implement that by creating an extended class that actually contained an “instance” of the parent, so parent’s methods / variables etc would be safely tucked away within the parent not in the main structure.

As for the OOP debate – I was for a long time a die hard C programmer not giving in to the whole OOP / class thing – although without actually realising it, I’d been using OOP concepts for years but via well written, structured and thought out C 8-)

Having used stricter languages where OOP is forced (like HaXe and Java) I can definitely see the benefits from an organisational point of view – but I think it’s far too easy to write “bad” code and not appreciate the subtleties and pitfalls of things like automatic constructors / destructors etc and can lead to lazy programming (assuming the compiler will fix all your bugs for you, that and not understanding what the compiler is doing anyway – hence my initial questions regarding the whole metatable thing).

The trick with OOP is knowing when to use it and when not – to my mind, it’s just another tool in the box, sometimes you need a screwdriver, other times a hammer (especially when dealing with management) 8-)

@everyone else.
Thanks for the metatable links – I’ll have to spend a little more time reading up on the subject.

JohnSeptember 30th, 2011 at 11:40 am

@Jon

regarding your comments about destroying variables, when you said

tom = objectType.new()

….

tom:destroy()
tom = nil

I think display objects work in a very similar way

rect=display.newRect(10,10,10,10)
:
rect:removeSelf()
rect=nil

HOWEVER, the difference is that the Corona system itself maintains a reference to the object so just setting rect=nil will not cause the GC to remove rect. This can be clearly seen since the object rect=nil does not cause the object to disappear and it still participates in physics collisions (if set as a physics object).

Indeed, if a collision event listener has been set up, rect will be passed to it even after it has been nilled out, showing that is still exists in memory. So

1) display objects are magical (not like user created tables)
2) both nilling and removeSelf() are also needed to get rid of them

Dave HaynesSeptember 30th, 2011 at 12:14 pm

Is there a way to create your own public events here? I’ve not been able to figure out the best way to do that yet. For example, how would you add a barked event that you can listen to from main?

PiotrTSeptember 30th, 2011 at 12:42 pm

If I understand coretly … Can I crate my ” hero” class put some for example animation for what the hero can do and then require it in my levels? For example when hero touch the wall do:
Hero:isdead() or hero:isBigger() do I understand the concept coretly?

Nicholas GSeptember 30th, 2011 at 1:41 pm

Digesting the information, but I got a question

Could you post the projects on github or something? When I copy and past into my text editor (notepad++ on windows) it posts as a single line going across the editor, and it’s VERY annoying lol.

I suppose I could try it on my mac, but i’m at “work”

ng

Nicholas GSeptember 30th, 2011 at 1:43 pm

Oopsy, disregard my last post. I viewed “raw” and now it’s cool :|
sorry!

Jonathan BeebeSeptember 30th, 2011 at 1:44 pm

@ng: On the code block (I assume you’re talking about the big one), click the ‘view raw’ link on the bottom right, that’ll give you a version that you should be able to copy and paste into your editor.

Nicholas GSeptember 30th, 2011 at 2:13 pm

Jon, you commented seconds after I posted but while you were posting I was posting so you posted after my post so now im posting to your post so your post is not necessary since I posted a correction to my previous post…….or something PHEW!

Thanks, ya I totally had a noob moment. Jonathan, I have to say your tutorials are damn flipping BRILLIANT! You need to do videos, write an “Official Corona SDK Development Book” I know others are writing books and they are good, but damn if you wrote a book and sold it for $50 I would scoop that son of a B up faster than butter drippin off a hot biscuit.

That’s right, tapped into my southern speak for a moment. I shall resume proper english from this point forward. I need to calm down on the expresso today.

ng

Jonathan BeebeSeptember 30th, 2011 at 2:33 pm

@ng: Thanks for the encouragement! I’m glad you find a lot of value in these tutorials :-)

madmusic6October 1st, 2011 at 5:53 pm

anyone: How come I cant get the count of a table after the functions have been put in?

I did this in dog.lua:

dog.tableCount = function()
    print ("Number in table dog = "..#dog)
    end

and in main I put:

dog.tableCount()

The printout says:
Number in table dog = 0
Shouldn’t the new function be showing up ,as it is placed into the dog table?

Jonathan BeebeOctober 1st, 2011 at 11:26 pm

@madmusic6: #table will only print the total number of numeric keys in the table, not associative keys.

Example:

    local luaTable = {}
    luaTable[1] = "first key"
    luaTable[2] = "second key"
    luaTable.id = "table name"
   
    print( #luaTable )    -- output: 2

To count the number of associative keys, use:

    local count = 0
    for k,v in pairs(luaTable) do
        count = count + 1
    end

See tables tutorial here:
http://blog.anscamobile.com/2011/06/understanding-lua-tables-in-corona-sdk/

Hope that helps!

madmusic6October 2nd, 2011 at 5:03 am

Many Thanks!

kawikaOctober 4th, 2011 at 9:17 am

@Jonathan Beebe Thanks so much for this insightful presentation. This is exactly what I want to build the best app possible. I am looking forward to more presentations!

AntheorOctober 5th, 2011 at 9:01 am

Thk you my Lord !

I have currently a class using module that I try to rewrite according to this great post.
My class returns a newGroup with no public functions :

module(...)
function newObject (a,b)
local object = display.newGroup()
-- spawn things and insert them "object"
return  object
end

Where should I specify display.newGroup() using your example ?
At the beginning :

local dog = display.newGroup()

in function dog.new :

local newDog = display.newGroup()
return setmetatable( newDog, dog_mt )

As you can see I don’t really understand what metatables do, but it’s ok if I just can use your dog class template :)

AntheorOctober 6th, 2011 at 2:50 am

And … (maybe with more success) what if I want my dog with a nice image.

I don’t understand how to define dog, where to put “=display.newImage…”

Could you help me to better understand the better modular class ?

Paul StevenOctober 6th, 2011 at 6:59 am

For those wanting to add an image, here is what I have done which seems to work. My code is to display an arrow graphic but the principle is the same for a dog

arrow.lua

local arrow= {}
local arrow_mt = { __index = arrow }    -- metatable


-------------------------------------------------
-- PUBLIC FUNCTIONS
-------------------------------------------------

function arrow.new( xPos, yPos )    -- constructor

    local newArrow = display.newImageRect("images/guidearrow.png", 128,128)
   
    newArrow.x = xPos
    newArrow.y = yPos
   
    return setmetatable( newArrow, arrow_mt )
end

return arrow

main.lua

local arrow = require("arrow")
local arrow1 = arrow.new( 250,250 )

Hope this helps and that I am doing this correctly:)

Paul StevenOctober 6th, 2011 at 7:08 am

Further to my last post, I need to display arrows from various other external “modules” so just tested this and seems to work if I add the require statement at the top of the other module. If I don’t add the require statement, it doesn’t work.

So my other module is called keyFrames.lua and is as follows:

local arrowClass = require("arrow")


function displayKeyFrame(passedKeyFrameID)

    if (passedKeyFrameID == "startGame") then

        director:changeScene("game")
       
    elseif (passedKeyFrameID == "0042") then
   
        playAudio("audio/LG1_0042.mp3", "0044", "play")
       
        local arrow1 = arrowClass.new( 250,350 )
       
    elseif (passedKeyFrameID == "0044") then
   
        playAudio("audio/LG1_0044.mp3", "0046", "play")

    end

end

Note I include keyFrames in main.lua as follows:

local director = require ("director")
require("keyFrames")

Hopefully this may be of use to someone:)

Paul StevenOctober 6th, 2011 at 7:37 am

Sorry to bombard but just tried to create a public function to rotate the arrow graphic as follows:

function arrow:setRotation(passedRotation)
   
    print ("rotate")
    self.rotation  = passedRotation
end

Which I call as follows from main.lua

local arrowClass = require("arrow")
local arrow1 = arrowClass.new( 250,250 )
arrow1:setRotation(45)

I am not getting any errors but sadly not getting my arrow to change rotation.

Any ideas what I am getting wrong here? I can see the function is being called from checking the terminal to see if the print statement did anything

Paul StevenOctober 6th, 2011 at 9:31 am

Sorted I think

local arrow= {}
local arrow_mt = { __index = arrow }    -- metatable


-------------------------------------------------
-- PUBLIC FUNCTIONS
-------------------------------------------------

function arrow.new( xPos, yPos )    -- constructor

    local newArrow = {
        imagery = display.newImageRect("images/guidearrow.png", 128,128)
   
    }
   
    newArrow.imagery.x = xPos
    newArrow.imagery.y = yPos

    return setmetatable( newArrow, arrow_mt )
   
end


function arrow:setRotation(passedRotation)
   
    self.imagery.rotation = passedRotation
   
end
saravananOctober 7th, 2011 at 3:51 am

how to close the app in corona sdk

VictorOctober 7th, 2011 at 4:12 am

First of all, thanks for sharing this good example Jonathan, good job.

I have a doubt regarding the heritage among classes. Let’s imagine we have a class called animal and the class dog provided by Jonathan. The class dog extend from the animal class in order to heritage their method and properties.

–Class animal

local animal= {}
local animal_mt = { __index = animal } — metatable

————————————————-
– PUBLIC FUNCTIONS
————————————————-

function animal.new( params ) — constructor

local newAnimal = {
legs = params.legs,
hair = params.hair or false,
vertebrate = params.vertebrated or true
}

return setmetatable( newAnimal, animal_mt )

end

————————————————————

function animal:numberOfLegs ()
print ( “The animal has :” .. self.legs )
end

return animal

How should dog class extends from animal class?. I would like to create an object dog which was able to call method “numberOfLegs”

Jonathan BeebeOctober 7th, 2011 at 10:22 am

@Victor: To make the dog class extend from that, in your dog.new() function, you’d set the table be an instance of animal, and also have whatever new properties that are specific to that dog. For example:

function dog.new()
    local d = require( "animal" ).new()
    d.name = "Rover"
   
    return setmetatable( d, dog_mt )
end
IsakOctober 8th, 2011 at 1:45 am

Thanks for sharing!

i wonder if i have an sprite with zwoptex how do i use external modules with it? i Have tried but can’t seems to get it to work?

Kind regards

AntheorOctober 8th, 2011 at 2:24 pm

Thx for all the answers and info :)

ETdoFreshOctober 11th, 2011 at 2:22 pm

I am 75% sure why this doesn’t work, but can someone help me understand why this does not work? Thanks!

dog.lue

local dog = {}
local dog_mt = { __index = dog }

function dog.new( x, y, width, height )
    local newDog = display.newRect( x, y, width, height )
    return setmetatable( newDog, dog_mt )
end

function dog:rollOver()
    print( self.x, self.y )
end

return dog

main.lua

local dog = require 'Dog'
local dog1 = dog.new(10,10,10,10)
dog1:rollOver() -- prints nil, nil?
Jonathan BeebeOctober 11th, 2011 at 3:05 pm

@ETdoFresh: If your module is dog.lua (all lowercase), then your require statement in main.lua should also be all lowercase (the ‘D’ is uppercase in your example, which would definitely throw an error).

ETdoFreshOctober 11th, 2011 at 8:18 pm

@Jonathan Beebe: Sorry, I caught that after I clicked Submit Comment, but I did not know how to edit my comments. Haha, not that I expected it at all, but I’m using Windows version of CoronaSDK, and it actually still compiles, I guess it’s that whole case insensitivity thing in Windows. Shame on me for requiring “Dog” and not “dog”! Anyway, the issue still remains. I can create dog1 from the dog module, but I can’t access display object variables or functions (like x, y, rotation, removeSelf()). I made a forum post so I won’t clutter your blog. Thanks so much for answering!

Forum: Inheriting a display object?

ETdoFreshOctober 11th, 2011 at 8:41 pm

Alright, I believe I found the answer in the last paragraph of this Corona SDK documentation. Basically it states that display objects’ metatables cannot be set. So the method in this blog will not easily be implemented if you are trying to extend a display object. So what workarounds can we think of to do Beebe’s assignment (create a dog gfx and tint it) at the end of his post?

Solution 1
Create dog as stated in the blog, and then add dog.gfx which holds your dog display object

Solution 2
Put all the functions for newDog inside the “new” function (functions in a function).

Can you think of other solutions? Thanks all!

IsakOctober 12th, 2011 at 1:04 am

Hey!

Don’t get this!

I have to tests:

main.lua

local dog = require (“em”)

local dog1 = dog.new ()

em.lua

local dog = {}
local dog_mt = { __index = dog } – metatable

————————————————-
– PUBLIC FUNCTIONS
————————————————-

function dog.new() – constructor

local newDog = {

}

return setmetatable( newDog, dog_mt )
end

return dog

and the other one :

main.lua

local dog = require (“ufo”)

local dog1 = ufo.new ()

ufo.lua

local ufo = {}
local ufo_mt = { __index = ufo }

function ufo.new()

local newUfo = {

}

return setmetatable(newUfo, ufo_mt)

end

return ufo

what am i doing wrong the first one works the second complains that :

attempt to index global ‘ufo’ (a nil value)

please if someone can help me i would love it, i want to continue with my game now not struggling with external modules:S

Kind regards Isak

PiotrTOctober 12th, 2011 at 2:44 am

@Paul Steven
When your arrow has an image did you try to add this arrow1 in main.lua to the display group ?

JonLOctober 13th, 2011 at 5:45 pm

@ETdoFresh

It is possible to assign a metatable to Corona display objects by using a proxy table as detailed in

http://tekarak.blogspot.com/2010/08/displayobject-inheritance-in-corona.html

I posted this on your Community forum entry.

Jim-JacquesOctober 18th, 2011 at 4:37 pm

Missing some things ;-)

Inheritance
Polymorphism
Class methods (statics)
Copy constructor
Destructor

Just a joke. When I started with lua this summer, the first thing i did, was to emulate/simulate object oriented paradigms (I am a C++/Java developer) in lua. But all tries ended up in the awareness that lua isn’t a object oriented language. Lua is a functional language with closure power. That’s enough. And if a programmer has a healthy mind, he will
instinctively use modules and similar concepts to structure his code. Lua is not intended for large projects, hence no need for OO. Yipiee!

AntheorOctober 29th, 2011 at 1:45 am

Thx to Paul, I can add an image now:)

I’m back to this old thread to point the following :

I tried to

dog1:addEventListener("touch", dogTouch)

And I get an error, obviously.

So ? I have to add something like that :

----
function dog:sit()
    print( self.name .. " sits down in place." )
end
----
function dog:addEventListener("touch", dogTouch)
end
---

I don’t think so : my touch fonction is in main.lua and I want it to stay there …

Any Hints ?

AntheorOctober 29th, 2011 at 1:56 am

I realize I can edit my comment, sorry. I’ll try to create a new post in the forum

MichaelDecember 19th, 2011 at 11:54 am

Great post as usual Jonathan. My question is to do with event listeners and their corresponding function. For example, I have added a display object to my own version of your dog class. I now want to add an event listener to that object, which I have done. So far, so good. However, I now want to code my event listener so that it only process its code if the object isn’t paused. I want my class to have an isPaused property, but how do I reference that in the class definition itself so that my event listener’s code can access it?

MichaelDecember 19th, 2011 at 12:02 pm

Ok, as usual I seem to come up with a solution above 5 minutes after I post a question. I’m not sure this is a good solution, but I present it here for your perusal.

[code]
local square = {}
local square_mt = {__index = square}

--------------------------------------------------------------------------------
-- Public Functions
--------------------------------------------------------------------------------

function square.new(displayGroup, xPos, yPos)

local object = display.newRect(10, 10, 50, 50)
object.x = xPos
object.y = yPos
displayGroup:insert(object)

local newSquare = {
squareImage = object,
isPaused = false,
direction = 1,
speed = 1,
isTapped = false
}

return setmetatable(newSquare, square_mt)
end

--------------------------------------------------------------------------------

-- Initialise stuff that can't be done inside the New
function square:init()

local function onTap(event)

if self.isPaused == false then

if event.phase == "ended" then
print("Square tapped!")
end

end

end

self.squareImage:addEventListener("touch", onTap)

end

-- Pause the object
function square:pause()

self.isPaused = true

end

-- Unpause the object
function square:unpause()

self.isPaused = false

end

-- Update the object
function square:update()

if self.isPaused == false then

-- Do Updates

end

end

return square
[/code]

MichaelDecember 19th, 2011 at 12:06 pm

Woops – apologies for the bad formatting on that post.

The code above works in that my object’s onTap listener now “knows” if the object has been paused or not. The bit I don’t like is that after creating the object I need to called the init method to create event listener etc.

e.g.

local square = require("square")

local myGroup = display.newGroup()

local object = square.new(myGroup, 10, 10)

-- Now I have to also 'init' my new object
object:init()

Anyone got a better solution to this? To be honest, I’m struggling to see the benefit of coding classes this way as opposed to just using one function that contains all relevant methods/properties etc and returns them as a table.

MichaelDecember 19th, 2011 at 12:15 pm

Hmm. Thinking about it now, is it better to have properties such as isPaused be properties of the display object property of the class? That way, by using event.target, it is easy to pick up properties such as isPaused and whatever other properties are relevant to the display object.

BacteriaJanuary 8th, 2012 at 10:30 pm

how can i call a function specific to an instance let say:
dog 2:sit()
or
dog3:roll()

using a timer??

timer.performWithDelay(1000, dog2:sit, 1) doesn’t work nor
timer.performWithDelay(1000, sit, 1)

when it comes to timers or event listeners using OOP, it’s a big mess :( at least for me.

i’d truly appreciate any help.

JamesJanuary 9th, 2012 at 11:20 pm

Does anyone agree or disagree that the following is much simpler and will result in less and more maintainable code:

——– Object.lua ———-

– Constructor
function Object:new()
local newInstance = {}
setmetatable(newInstance, self)
self.__index = self

return newInstance
end

——– END Object.lua ———-

——– Dog.lua ——————-

require(“object”)

Dog = Object:new()

function Dog:init(name, age)
self.name = name or “Unnamed”
self.age = age or 2
return self
end

local function getDogYears( realYears ) – local; only visible in this module
return realYears * 7
end

function Dog:rollOver()
print( self.name .. ” rolled over.” )
end

– etc. the other methods are defined as per the original

——– END Dog.lua —————

First 3 lines of usage in main is slightly different, note the use of colon to call new:

require(“Dog”)

local dog1 = dog:new():init( “Yai-Ya”, 4 )
local dog2 = dog:new():init( “Toki”, 1 )

What’s the advantage?
1. You’re not repeating the same construction code all over the place
2. You can even subClass further (and make super calls) as below in the GreatDane example

———– GreatDane.lua ———-
require(“Dog”)

GreatDane = Dog:new()

– We’re happy with init from Dog so no new definition required
– Ditto other functions (dare I say methods?)

– But not bark, we need some more for a Great Dane
function GreatDane:bark()
Dog.bark(self) — super call, note use of period not colon and passing of self
print(“Very menacingly!!!”)
end

———- END GreatDane.lua ———–

The power and beauty of OO can be mostly achieved in Lua quite easily and elegantly.

JamesJanuary 9th, 2012 at 11:23 pm

BTW @Bacteria I think the answer you want is:

timer.performWithDelay(1000, function() dog2:sit() end)

BacteriaJanuary 10th, 2012 at 1:35 am

@James That was exactly what i was looking for, thanks a lot man.
btw your OO sample looks very organized, simple and neat. More importantly it does the job. Thanks again for your help, you saved my a**.

sXcJanuary 11th, 2012 at 6:29 pm

How can you add physics to an object using this method? I tried this, but it crashes my game:

local character = {}
local character_mt = { __index = character }

local physics = require( “physics” )

function character.new( xpos, ypos ) – constructor

local character = display.newImageRect( “character.png”, 40, 40 )

character.x = xpos
character.y = ypos
physics.addBody( character, “dynamic”, { density= 14, friction= 40, bounce = 0.0, radius=12} )

return setmetatable( character, character_mt )
end

return character

BacteriaJanuary 13th, 2012 at 4:44 am

@sXc you are not supposed to require physics inside the object class/module.

local physics = require( “physics” ) <——– this line shouldn't be inside the character class/module. It should go inside main.lua or level1.lua or where ever you gonna create an instance of that object. (in your case "character").

ChrisFebruary 8th, 2012 at 7:24 am

Hi all, frustrated newbie here…. I eagerly set out to try this new method, and immediately run into the following error output from the terminal:

Copyright (C) 2009-2011 A n s c a , I n c .
Version: 2.0.0
Build: 2011.704
The file sandbox for this project is located at the following folder:
(/Users/amandabackof/Library/Application Support/Corona Simulator/73-6B401E333B469B2F5D3563E3A8DA53CC)
Runtime error
…x0hb5k4j_7_1xry7m0000gn/T/TemporaryItems/73/main.lua:8: attempt to call method ‘printAge’ (a nil value)
stack traceback:
[C]: in function ‘printAge’
…x0hb5k4j_7_1xry7m0000gn/T/TemporaryItems/73/main.lua:8: in main chunk
Runtime error: …x0hb5k4j_7_1xry7m0000gn/T/TemporaryItems/73/main.lua:8: attempt to call method ‘printAge’ (a nil value)
stack traceback:
[C]: in function ‘printAge’
…x0hb5k4j_7_1xry7m0000gn/T/TemporaryItems/73/main.lua:8: in main chunk

Here’s the code from my dog.lua and main.lua files. I probably have a dot instead of a colon somewhere, etc., but I have gone through this 100 times and retyped it 3 times – a trained set of eyes to help me find my error would be most appreciated!!!

–dog.lua, example “dog” class for Modular Classes tutorial

local dog = { }
local dog_mt = { _index = dog } — metatable

—–Private Functions

local function getDogYears(realYears) — local, visible only within getDogYears()
return realYears * 7
end

–Public Functions

function dog.new(name, ageInYears) — constructor
local newDog = { name = name or “Unnamed”, age = ageInYears or 2 }
return setmetatable (newDog, dog_mt)
end
————-
function dog:rollOver()
print(self.name .. “rolled over.”)
end
————
function dog:sit()
print (self.name .. “sits down in place.”)
end
———–
function dog:bark()
print (self.name .. “says \”woof!\”")
end
———-
function dog:printAge()
print(self.name .. ” is ” .. getDogYears(self.age) .. “in dog years.”)
end
————-

return dog

**********

–main.lua for modular “dog” class example

local dog = require(“dog”)

local dog1 = dog.new(“Donji”, 11)
local dog2 = dog.new(“Molly”, 8)

dog1:printAge()
dog2:printAge()
******* end main.lua

Many thanks!!!

Chris (and Westies Donji and Molly, who eagerly await seeing their names on the terminal!)

ChrisFebruary 8th, 2012 at 10:21 am

Don’t know why the line local dog2 = dog.new(“Molly”, 8)

turned into a smiley face…. it has the digit eight there…

TravisFebruary 14th, 2012 at 4:16 pm

Can someone explain or point me to a resource to explain why this works “correctly”

timer.performWithDelay(2000, function() dog1:sit() end)

and this skips the delay

timer.performWithDelay(2000, dog1:sit())

Jonathan BeebeFebruary 14th, 2012 at 5:11 pm

@Travis: The reason why the second example doesn’t perform as expected is because the second argument to timer.performWithDelay expects a function. When you pass: function() dog1:sit(); end, you are indeed passing a function as the second argument.

However, when you pass dog1:sit(), you are passing whatever is returned by dog1:sit(). If nothing is returned, then you are passing ‘nil’ as the second argument to timer.performWithDelay.

TravisFebruary 14th, 2012 at 7:47 pm

@Jonathan
Wow, my first reply is by a legend! Unfortunately my mind is now blown.

I tried to think of it as the table dog1 has a key called sit… but that would still seem to be a valid function, so my mind broke…

Then I tried to create a key/value in the table of
sit1 = function() print(self.name .. “sits.”) end
but then it has problems with indexing “self”

Ahhh these rabbit holes keep getting deeper…

So we created a function…
function dog:sit()… blah, blah, blah

but it isn’t seen as a function because it really isn’t owned by dog1? And we are using the metatable to cheat… or

I hate the whole being a newb thing… Perhaps the best way to ask my question is…
“Whatcha talkin’ ’bout Willis?”

I don’t grok how dog1:sit() is not a function? Unless it has to do with the metatable cheat?

Thanks,
Travis

TravisFebruary 14th, 2012 at 9:18 pm

@Chris,
Hey Chris…
I found your error I believe…

It is in your dog.Lua file.

you have
local dog_mt = { _index = dog }

it should be
local dog_mt = { __index = dog }

In other words, you have a single underscore, while it should be a two underscores.

Thanks,
Travis

Jonathan BeebeFebruary 15th, 2012 at 12:53 am

@Travis: Here’s a good way to think of it…

Every function returns something, even if you don’t write ‘return’ at the end of the function. In those cases, a function returns nil (or false, one of the two).

With the timer.performWithDelay() function, the second argument expect either a function or a reference to a function. The following may seem valid:

  timer.performWithDelay( 1000, dog:sit() )

The reason is because dog:sit() is obviously a function, and the second argument requires a function to be passed—so it’s valid right? Actually, no…

With Lua, the presence of “()” means that the function will be called, which means you’re actually passing whatever the function returns instead. Therefore, if your function dog:sit() returns nil or false, then this is actually what you’re passing to timer.performWithDelay():

  timer.performWithDelay( 1000, nil )

Which explains why you’re not getting anything from your timer. The only time “()” doesn’t call a function is during function definition (in which case an ‘end’ is expected at some point), which is why the following is valid:

  timer.performWithDelay( 1000, function() dog:sit(); end )

Because there’s an ‘end’, the above is valid because you are indeed passing a function and not just calling one. In the above example, you are passing a function that ‘calls’ another function, which is exactly what you want.

The following is also valid:

local function callDogSit()
    dog:sit()
end

timer.performWithDelay( 1000, callDogSit )

In that example, callDogSit is a direct reference to the actual function, since we ommitted the “()”.

Anyway, I hope that explanation helped some. Let me know if there’s still areas you’re confused about and I’ll try my best to clear things up!

TravisFebruary 15th, 2012 at 8:25 am

Excellent explanation, thank you!

ChrisFebruary 15th, 2012 at 9:36 am

@Travis,

You are my hero! If only all my other problems were so simple.
Thank you!

chris

CollabDanFebruary 23rd, 2012 at 10:17 am

I’m still a little new to Corona and Lua and am trying to understand the purpose of the “return dog” in the dog.lua module. For the most part I get the rest of the example. I could not find any similar examples in any documentation for Lua.
Can you explain:
1. What exactly happens when dog is returned at the end of the module?
2. How is dog accessed/utilized by the invoking main.lua?
3. Is this return statement really needed?

The more detail the better. If you could point me to some references that would be great.

Thanks.

Dan

CollabDanFebruary 23rd, 2012 at 10:56 am

Please disregard (delete) my last post. I found the answer after I did some more digging.

Thanks.

Dan

Max WilliamsMarch 27th, 2012 at 6:43 am

Great explanation of (pseudo) classes in Lua, thanks! I’m going to go off and tidy up the corona sdk game i’ve been working on as a learning exercise. One question: when you define the dog class in Main, like

local dog = require( “dog” )

would it not be more in keeping with convention (with other languages) to capitalise the name, since it’s representing a class (or lua equivalent)? ie

local Dog = require(“dog”)
local dog1 = Dog.new

etc

?

rayman1900April 6th, 2012 at 8:29 am

@James

I’m trying to implement your “more maintainable code” but I got an error saying that it attempt to index global ‘Object’ (a nil value) when I try to use it in the main.lua…

Care to explain a solution to this, please ? Thank you

Jonathan BeebeApril 26th, 2012 at 9:08 am

For those interested, I’ve created a single-module class implementation for Corona/Lua that allows you to create classes that produce instances of display objects (as well as non-display objects). And has a couple other neat features.

More info here:
http://jonbeebe.net/2012/04/object-oriented-programming-in-lua.html

Leave a comment

Your comment