Object Oriented Modules

Hi,

There used to be a tutorial here on the forums that showed how to create object-oriented modules, however I cannot seem to find it anymore.

Anyhow, in following the tutorial I created this for my game:

-- -- main.lua -- local d = require("d") local bot = require("bot") local bot1 = bot.new(100,200) -- Create bot in main.lua file for gameplay -- Would like for the touch listener to already be applied to the bot when created here

-- -- bot.lua -- local bot = {} local bot\_mt = { \_\_index = bot } local d = require("d") ------------------------------------------------- function bot.new(x, y) -- constructor print("bot.new") local o = display.newRect(x,y,32,32) o:setFillColor(0) camera:add(o,1) return setmetatable(o, bot\_mt) end function bot:remove() print("bot:remove") end -- Touch listener for the bot function bot:touch() print("bot:touch") end ------------------------------------------------- return bot

However, I cannot seem to get the touch listener to get to work for the bot object in the module so it applies to all created instances in the main.lua file.

If my file setup is outdated, or there is something I am completely missing, any advice would be greatly appreciated.

Thank you

  1. Lua is not really an object oriented language, but it can be coerced to act sort-of-OOP

  2. OOP, IMHO is not useful in many Corona game types because you end up having to manage both a OOP-ish table object and a display object.   

  3. Unless all you know is OOP, or if you are making an app (not a game), or if you’re making some kind of simulation game or some other game that handles lots of data, state-machines, and other ‘app like’ tasks.  Adding ‘learning to program Lua in a OOP style’ on top of learning Corona may be a lot of extra and unnecessary challenge.

  4. Corona display objects cannot be (easily) coerced to behave as OOP objects.

  5. If I have not dissuaded you from your OOPyness, I suggest using 30log as the basis of your OOP adventures in Lua.  It is my favorite, very stable, easy to use, and well documented:

In short, don’t roll your own.  Use a foundation and learn from it.

Try this

local bot = {} local bot\_mt = { \_\_index = bot } function bot.new(x, y) -- constructor local aNewBot = {} aNewBot.image = display.newRect(x,y,32,32) aNewBot.image:addEventListener("touch", botOnTouch ) return setmetatable(aNewBot, bot\_mt) end function botOnTouch(event) --event.target will be the bot you touched end return bot

@Ed, there is nothing wrong with applying OOP methodology in Lua as long as it is not too strictly applied as that can lead to really convoluted code. Lua is great at being flexible - especially with closure programming and we are lucky to be able to cherry pick closure programming v OOP v TDD v TDD v etc.

One thing I hate about OOP is constructs like this  Game.Interface.Player.CurrentInstance().x when player.x works just as well.

Now, because I love to debate…

Re point 2 and 4, that is not necessarily true, my implementation (previous post) only returns a reference to the object.  The object itself is responsible for it’s own graphic and logic and that is simply good programming practise.

For example, in my game I have a class of car.  The game engine creates many hundreds of instances of car and that is the game engines involvement done with cars (until it has to destroy them).  The game engine is not concerned about cars, where they are and what they are doing and nor should it. The car has enough basic AI to control and manage itself without being told what to do externally.

Encapsulation is key to OOP, is a great coding methodology and helps compartmentalise code. 

Just MHO.

@sgs (may I use the shorthand?),

I am not against OOP.  I’m only against increasing the number of things a new person must learn and understand.  I see enough new folks coming in and tripping over basics and fundamentals.  To me, OOP-ish Lua is really an advanced topic that adds little that can’t also be had from good module and object design.

Opinions and all that. :slight_smile:

I know your example is simple, and as such is not showing the true strengths of OOP, but I think this module compares better as it returns a direct reference to the object.

local bot = {} local onTouch function bot.new( group, x, y ) group = group or display.currentStage local obj = display.newRect( group, x, y, 32, 32 ) obj.touch = onTouch obj:addEventListener( "touch" ) return obj end onTouch = function( self, event ) -- ... touch code here end return bot

Again, opinions and all that.

Note: As soon as you have multiple types of bots where child bots inherit features from parents and override features or add new ones, then OOP is probably the way to go.  

I’m afraid at this point I may be hijacking the thread so I’ll stop.  

@Ed… yes SGS (or Adrian) is fine…

The OP was discussing metatables so a certain level of technical knowledge was therefore implied.

Your code example is single-instance whereas mine was multi-instance (I spent many nights struggling with this simple point - shared memory pointers, etc.).  So now I point all questions towards multi-instance even if a single instance is all that is required.

In true OOP you do not need (nor should you need) a reference to the underlying object(s) (in your example a newRect) you only need a reference to the class (in my example).  You should be able to call obj.moveNorth() and obj will move north.  Now obj may be a simple rect but it could also be a displayGroup with multiple assets and multiple animations. This is why we abstract the details as the calling code should never know the internal implementations of what is being called - this in OOP terms is called abstraction.

I refer to my game: I do not need a reference to the display group that contains my car assets only need a reference to the instance of a car.  Calling car:Stop() or car:Start() against a graphic object makes no sense. Calling car.stop() should trigger internal actions within the car class to cause the car to stop; this should include cancelling all transitions and timers.

And yes this is an advanced subject that most devs do not need to get involved with… I appreciate you are trying to simplify things but we are talking coding principles here :slight_smile:

Thanks, sincerely, for elevating the discussion.  I completely agree and would love to see higher-level coding discussions going on here in the community.

Cheers,

Ed

Great thread. More opinions on the matter?

Rob

Other opinions (than inheritance) include composition and duck typing. Cowboy Programming has a good article on the first of these: Evolve Your Hierarchy

opinion on what??

are you asking about the “metaphysical” aspects of this thread:  is OOP good or bad?  (neither, inherently, is what I would say)

or are we discussing the merits of imperative vs object-oriented, perhaps even vs functional, and perhaps even vs logical?  (far too esoteric for most readers of this forum is what i would suspect)

or the specific mechanisms for implementing OOP via Lua?  30log was mentioned, and uses a simple “prototype” -type system, where a parent “pattern object” (aka prototype, aka template, take your pick) is deep copied onto the child object.  this works well for Corona display objects (metamethod classes don’t - you can make it work, but it gets hairy)  30log’s only use of metamethods is to spoof a call to the class as the constructor (so you don’t need an explicit class.new() function)

the other options are “corona-display-objects own an object” or “object owns a corona-display-object” (this is Sphere Game Studios example, and is also probably my favorite arrangement) or they’re independent but linked (a list of corona-display-objects plus a list of objects and a list of links between them - almost a MVC controller setup, except that you (often) can’t fully decouple the view from its behavior, because of events and such or physics, etc.

-- lua-object "owns" display-object anObject = aClass.new(... anObject.view = display.newRect(... -- display-object "owns" lua-object aDisplay = display.newRect(... aDisplay.inst = aClass.new(... -- separate but linked aDisplay = display.newRect(... anObject = aClass.new(... controller = {} controller[aDisplay].model = anObject controller[anObject].view = aDisplay -- prototype-ish (30log style, greatly simplified, shallow) anObject = aClass.new(... aDisplay = display.newRect( for k,v in pairs(anObject) do aDisplay[k] = v end -- metatable chaining, ie implementing metamethod class directly over -- a Corona display object's proxy without wiping it out, is not shown, -- too complex for how much effort i feel like putting in here :D

for what it’s worth, i modularize “to the extreme” - my scene files aren’t any longer than the empty template (i don’t need any of the lines that alias sceneGroup and add only about half a dozen, and two of those lines are add/removeEventListener calls).  the entire “contents” of any scene is specified in a config file, classes are loaded dynamically, instances are created from those classes dynamically (many times they’re just singletons - you only need one pause button on your game scene, fe, but I modularize it anyway).  results in a lot of special-purposes classes, but each is very self-contained. (we’ve all seen the 10KLOC scene module with all the code needed to do 100 different things == yuk, imho)   apart from requiring those settings and the controller that reads them, the only “real” code added is “what’s my name in the config file” and “controller:construct(self, settings[self.name])”.  done, everything else is “automatic”.  (again, just fwiw, not suggesting anyone else go that nuts :D)

I’d be more than happy to give my opinion as well :slight_smile:

First of all, to the original poster, a lot depends on what you mean with OOP. If you’re talking hardcore strict OOP (with abstraction, encapsulation, inheritance and polymorphism), you might find that Lua does not offer these things out-of-the-box, or at least not in the way you want it to.

However, what I’ve found is that for most of my practical needs, you can get a lot of this functionality by using metatables, and attentive use of dot syntax and providing “handles” for functions you might want to use later. In my opinion, when it comes to game programming you basically just need good instancing (for spawning), and that can be done perfectly with modules and meta-tables, and behaves a lot like OOP.

In a strange way, the fact that I needed to “roll my own” OOP and really understand what I was doing, meant that I mastered Lua a lot more than if OOP was just there right from the start.

I did have trouble grasping some aspects though, so to the OP, if you’d like I’m willing to take you through this. Just let me know.

Cheers,

Thomas

Thank you all for the very informative posts!

Specifically regarding Ed’s answer, it appears as though I am unable to create colon based functions after the onTouch function. In trying to create a bot:reset function, it results in runtime errors in the console.

Below is referencing your above code then adding the following function:

function bot:reset(self, event) print("bot:reset") end

Thanks

Here is a Lua OOP tutorial and a Lua OOP explanation you might find useful:

Simple Lua OOP

https://theindiestone.com/forums/index.php?/topic/8023-simple-lua-oop-without-metatables/

Lua OOP

http://www.troubleshooters.com/codecorn/lua/luaoop.htm

In response to the original post, I have some code provided in one of the Wattage Tile Engine examples that is an OOP module and sets up a listener.  It is a control stick class and the code is listed on this page of the Wattage Tile Engine documentation (scroll down a bit)…

https://paulwatt526.github.io/wattageTileEngineDocs/regionManagerExample.html

You can see that it sets up a listener that applies only to an instance of that class.  I hope this example is helpful.

Now, on the topic of OOP, I will agree that OOP has a bit of a learning curve, but in my opinion it is worth using in many cases.  In my projects, I have adopted the approach outlined in the link that animonger provided, and although use of OOP is not required to use the Wattage Tile Engine, I have included an OOP primer in the documentation because the plugin exposes its underlying object system which may optionally be used by projects utilizing the plugin.

Here is the link to the OOP primer (this approach does NOT require the plugin)…

https://paulwatt526.github.io/wattageTileEngineDocs/luaOopPrimer.html

If anyone is interested, here is a link to the guide to using the Wattage Tile Engine’s optional underlying Object system.  Use of this object system enables serialization of complex object graphs which can be really helpful when state needs to be persisted.

https://paulwatt526.github.io/wattageTileEngineDocs/objectSystem/usageGuide.html

  1. Lua is not really an object oriented language, but it can be coerced to act sort-of-OOP

  2. OOP, IMHO is not useful in many Corona game types because you end up having to manage both a OOP-ish table object and a display object.   

  3. Unless all you know is OOP, or if you are making an app (not a game), or if you’re making some kind of simulation game or some other game that handles lots of data, state-machines, and other ‘app like’ tasks.  Adding ‘learning to program Lua in a OOP style’ on top of learning Corona may be a lot of extra and unnecessary challenge.

  4. Corona display objects cannot be (easily) coerced to behave as OOP objects.

  5. If I have not dissuaded you from your OOPyness, I suggest using 30log as the basis of your OOP adventures in Lua.  It is my favorite, very stable, easy to use, and well documented:

In short, don’t roll your own.  Use a foundation and learn from it.

Try this

local bot = {} local bot\_mt = { \_\_index = bot } function bot.new(x, y) -- constructor local aNewBot = {} aNewBot.image = display.newRect(x,y,32,32) aNewBot.image:addEventListener("touch", botOnTouch ) return setmetatable(aNewBot, bot\_mt) end function botOnTouch(event) --event.target will be the bot you touched end return bot

@Ed, there is nothing wrong with applying OOP methodology in Lua as long as it is not too strictly applied as that can lead to really convoluted code. Lua is great at being flexible - especially with closure programming and we are lucky to be able to cherry pick closure programming v OOP v TDD v TDD v etc.

One thing I hate about OOP is constructs like this  Game.Interface.Player.CurrentInstance().x when player.x works just as well.

Now, because I love to debate…

Re point 2 and 4, that is not necessarily true, my implementation (previous post) only returns a reference to the object.  The object itself is responsible for it’s own graphic and logic and that is simply good programming practise.

For example, in my game I have a class of car.  The game engine creates many hundreds of instances of car and that is the game engines involvement done with cars (until it has to destroy them).  The game engine is not concerned about cars, where they are and what they are doing and nor should it. The car has enough basic AI to control and manage itself without being told what to do externally.

Encapsulation is key to OOP, is a great coding methodology and helps compartmentalise code. 

Just MHO.