Classes and Inheritance?

Hi All,

I’m trying to get a little more clever with my non-player character (NPC) object creation. Currently I have unique code for each of my different NPC types. This works but takes up a lot of “space” and also means if I change a common system I change it in multiple locations (not the end of the world but not as slick as I think I ought to be). So I’m looking to figure out how to derive other NPC types off of a base NPC.

I have Programming in Lua (2nd Edition) and broadly follow 16.1 Classes, 16.2 Inheritance and 16.3 Multiple Inheritance. I’m just having a hard time putting it into practice. The challenge for me is that the examples from the book seem fairly different from what I’m attempting. Can anyone share any tips on how to approach making a base NPC class and then extending that into other NPC types?

Basically I have my normal critter that can spawn and move. I’d like to make a variant that’s larger, slower and more resilient and another one that is similar in size and speed but has some additional movement considerations.

My project also has weapons that the player can pick up. I currently only have one but imagine this system of class and inheritance will be very helpful when I work on more of those.

Thanks in advance for any help anyone can offer :slight_smile: [import]uid: 105707 topic_id: 35562 reply_id: 335562[/import]

hi EHO,

i have authored a library module which helps when doing OO in Lua/Corona. inside the library itself you’ll find many examples of OO programming (both using my library and straight Lua OO). in addition to the examples i have written a lot of support documentation on my website.

https://developer.coronalabs.com/code/dmc-corona-library

i would say the best, simple example for your situation is located in examples/dmc\_objects/DMC-MultiShapes. specifically look at shapes.lua. another example DMC-ufo also in dmc\_objects/ might be useful.

one thing that i’d particularly mention about my library is that it also guides the coding regarding setup/destruction to be more organized and thus less prone to memory issues.

lastly, i also re-wrote a game by Johathan Beebe into an OO architecture. you might find it helpful.

https://developer.coronalabs.com/code/ghost-vs-monsters-oop

cheers,
dmc [import]uid: 74908 topic_id: 35562 reply_id: 141361[/import]

Hi dmc,

I appreciate your input and the generosity in sharing your library.

I’ve been going through this information and it has been helpful. It’s almost “too much” in the sense that it’s stepping me from not really “getting” how to implement OOP concepts to a full blown framework. I’ve definitely learned from exploring it and imagine that I’ve only touched the tip of the iceberg and will gain lots more from it as I continue to come back to it.

What is your website? I’d love to see the documentation you’ve mentioned. I did a google search but may have found an old site?

As far as my efforts go, my first step is to make a base “class” and then see if I can extend that into other sub classes. I’ve been working at setting the __index with the setmetatable() function and have some luck with that. Here’s an example of what I ginned up:

[lua]local critter = {}

local function createBaseNPC( … )
function critter:functionTest()
print(“critter:functionTest() from createBaseNPC()”)
end
return critter
end

local critter2 = {}

local function createSecondNPC( … )
return critter2
end

local baseCritter = createBaseNPC()
local testCritter = createSecondNPC()

setmetatable(testCritter, {__index =baseCritter})
testCritter:functionTest() --this outputs the print statement from createBaseNPC()[/lua]
I guess I’m on the right track with this (since it works!) but I had hopped I could set the metatable with the functions:
[lua]setmetatable(createSecondNPC, {__index =createBaseNPC})[/lua]
Since everything in lua seems to be a table I thought this would work with the functions but it didn’t.

Ahhh… OK, as I’m writting this I’ve solved the issue… didn’t include function “()” on the ends of those!

This does work (setting it infront of the baseCritter and testCritter construction):
[lua]setmetatable(createSecondNPC(), {__index =createBaseNPC()})[/lua]

Excellent… (in my best “Mr. Burns” voice…) :slight_smile:
As a note for anyone reading this thread, the following is the above test case setting the metatable with the functions themselves:
[lua]local critter = {}

local function createBaseNPC( … )
function critter:functionTest()
print(“critter:functionTest() from createBaseNPC()”)
end
return critter
end
local critter2 = {}

local function createSecondNPC( … )
return critter2
end
setmetatable(createSecondNPC(), {__index =createBaseNPC()})

local baseCritter = createBaseNPC()
local testCritter = createSecondNPC()
–setmetatable(testCritter, {__index =baseCritter})
testCritter:functionTest() --this outputs the print statement from createBaseNPC()[/lua] [import]uid: 105707 topic_id: 35562 reply_id: 141548[/import]

EHO,

you’re on the right track. though it seems like you’re mixing two different styles of OO architecture in Lua. another thing is that the setmetatable() for new objects (not the base class) usually goes inside of a constructor.
here is a simple example of one method of setting up OO.

this is a slight modification of examples/dmc\_autostore/DMC-autostore-advanced/ufo.lua. keep in mind, this is just for first-level objects, no chaining. i’ll try and find a simple example of chaining soon.

[lua]-- prep our new base class

local UFO = {}
local mt = {
__index = UFO
}
setmetatable( UFO, mt )

–== Class constructor ==–

function UFO:new( id, data )

local o = {}
local mt = {
__index = UFO
}
setmetatable( o, mt )

– set properties
o._id = id
o._data = data

return o
end

local ufo = UFO:new( ‘34324’, { x=5, y=10 } )[/lua]
btw, the documentation site is: docs.davidmccuskey.com

cheers,
dmc [import]uid: 74908 topic_id: 35562 reply_id: 141591[/import]

Hi dmc,

Your libraries were insightful on their own but your thorough documentation really makes them stand out. I appreciate the link to that information.

Thanks for the code sample, it’s enlightening. I can see how I was on a different path. I especially appreciate the insight regarding the setmetatable() use in the constructor.

If you do have time to share a chaining sample, that would be awesome since ultimately that’s what I’m working up to.

[import]uid: 105707 topic_id: 35562 reply_id: 141934[/import]

Hello EHO,

i edited my previous code sample, i had one to many calls to setmetable() in there, plus i am reusing the class metatable.

here is an example with inheritance:

[lua]
– Main.lua –
print("---------------------------------------------------")

–== Base Class ==–

local UFOBaseClass = {}
local UFOBaseClass_meta = {
__index = UFOBaseClass
}
– constructor –

function UFOBaseClass:new()

local o = {}
setmetatable( o, UFOBaseClass_meta )

– set properties
o._vel = nil – velocity

return o
end
– class method –

function UFOBaseClass:move()
print( “velocity=” … tostring( self._vel ) )
end

–== Fast Class ==–

local UFOFastClass = UFOBaseClass:new()
local UFOFastClass_meta = {
__index = UFOFastClass
}
– constructor –

function UFOFastClass:new( id, data )

local o = {}
setmetatable( o, UFOFastClass_meta )

– set properties
o._id = id
o._data = data
o._vel = 42

return o
end

–== Slow Class ==–

local UFOSlowClass = UFOBaseClass:new()
local UFOSlowClass_meta = {
__index = UFOSlowClass
}
– constructor –

function UFOSlowClass:new( id, data )

local o = {}
setmetatable( o, UFOSlowClass_meta )

– set properties
o._id = id
o._data = data
o._vel = 12

return o
end

–== Create Instances of Our Classes ==–

local ufo_f = UFOFastClass:new( “423432”, { x=1, y=40 } )
local ufo_s = UFOSlowClass:new( “1234”, { x=20, y=400 } )

print( UFOFastClass._id )
UFOFastClass:move()
print( “\n” )

print( ufo_f._id )
ufo_f:move()
print( “\n” )

print( ufo_s._id )
ufo_s:move()
[/lua]

output is:

---------------------------------------------------  
nil  
velocity=nil  
423432  
velocity=42  
1234  
velocity=12  

HTH.

cheers,
dmc [import]uid: 74908 topic_id: 35562 reply_id: 141987[/import]

hi EHO,

i have authored a library module which helps when doing OO in Lua/Corona. inside the library itself you’ll find many examples of OO programming (both using my library and straight Lua OO). in addition to the examples i have written a lot of support documentation on my website.

https://developer.coronalabs.com/code/dmc-corona-library

i would say the best, simple example for your situation is located in examples/dmc\_objects/DMC-MultiShapes. specifically look at shapes.lua. another example DMC-ufo also in dmc\_objects/ might be useful.

one thing that i’d particularly mention about my library is that it also guides the coding regarding setup/destruction to be more organized and thus less prone to memory issues.

lastly, i also re-wrote a game by Johathan Beebe into an OO architecture. you might find it helpful.

https://developer.coronalabs.com/code/ghost-vs-monsters-oop

cheers,
dmc [import]uid: 74908 topic_id: 35562 reply_id: 141361[/import]

Hi dmc,

This example is very helpful.

One confusion I had to get my head around is that I was hung up on the idea that the __index table is the table the object looks to when a field doesn’t exist in the current object table. Somehow I expected the __index value of each of the sub-classes to point directly at UFOBaseClass:new(). Since that is a function and it returns a table I see that that makes sense that you have to set the metatable to a table that is created out of UFOBaseClass:new(). So it’s making sense to me but I had to really force myself to look at this differently than what I was expecting.

It is a paradigm shift for me to start working this way so I guess this is just taking a little more time than I thought to really settle in. The concept seems so clear looking at your example but I’m taking longer to get going with it than I expected.

I’ve really been getting a lot out of your documentation.

Thanks for all your help, it’s made a real difference for me :slight_smile: [import]uid: 105707 topic_id: 35562 reply_id: 142217[/import]

PS - you used some really clever values for your inheritance example. I had to really think on why UFOFastClass:move() outputs “velocity=nil” and why the UFOFastClass object method ufo_f:move() outputs “velocity=42”. Makes sense now that I’ve figured it out but took me a minute of head scratching. Great example that really distinguishes some subtleties :slight_smile: [import]uid: 105707 topic_id: 35562 reply_id: 142222[/import]

Hi dmc,

I appreciate your input and the generosity in sharing your library.

I’ve been going through this information and it has been helpful. It’s almost “too much” in the sense that it’s stepping me from not really “getting” how to implement OOP concepts to a full blown framework. I’ve definitely learned from exploring it and imagine that I’ve only touched the tip of the iceberg and will gain lots more from it as I continue to come back to it.

What is your website? I’d love to see the documentation you’ve mentioned. I did a google search but may have found an old site?

As far as my efforts go, my first step is to make a base “class” and then see if I can extend that into other sub classes. I’ve been working at setting the __index with the setmetatable() function and have some luck with that. Here’s an example of what I ginned up:

[lua]local critter = {}

local function createBaseNPC( … )
function critter:functionTest()
print(“critter:functionTest() from createBaseNPC()”)
end
return critter
end

local critter2 = {}

local function createSecondNPC( … )
return critter2
end

local baseCritter = createBaseNPC()
local testCritter = createSecondNPC()

setmetatable(testCritter, {__index =baseCritter})
testCritter:functionTest() --this outputs the print statement from createBaseNPC()[/lua]
I guess I’m on the right track with this (since it works!) but I had hopped I could set the metatable with the functions:
[lua]setmetatable(createSecondNPC, {__index =createBaseNPC})[/lua]
Since everything in lua seems to be a table I thought this would work with the functions but it didn’t.

Ahhh… OK, as I’m writting this I’ve solved the issue… didn’t include function “()” on the ends of those!

This does work (setting it infront of the baseCritter and testCritter construction):
[lua]setmetatable(createSecondNPC(), {__index =createBaseNPC()})[/lua]

Excellent… (in my best “Mr. Burns” voice…) :slight_smile:
As a note for anyone reading this thread, the following is the above test case setting the metatable with the functions themselves:
[lua]local critter = {}

local function createBaseNPC( … )
function critter:functionTest()
print(“critter:functionTest() from createBaseNPC()”)
end
return critter
end
local critter2 = {}

local function createSecondNPC( … )
return critter2
end
setmetatable(createSecondNPC(), {__index =createBaseNPC()})

local baseCritter = createBaseNPC()
local testCritter = createSecondNPC()
–setmetatable(testCritter, {__index =baseCritter})
testCritter:functionTest() --this outputs the print statement from createBaseNPC()[/lua] [import]uid: 105707 topic_id: 35562 reply_id: 141548[/import]

EHO,

you’re on the right track. though it seems like you’re mixing two different styles of OO architecture in Lua. another thing is that the setmetatable() for new objects (not the base class) usually goes inside of a constructor.
here is a simple example of one method of setting up OO.

this is a slight modification of examples/dmc\_autostore/DMC-autostore-advanced/ufo.lua. keep in mind, this is just for first-level objects, no chaining. i’ll try and find a simple example of chaining soon.

[lua]-- prep our new base class

local UFO = {}
local mt = {
__index = UFO
}
setmetatable( UFO, mt )

–== Class constructor ==–

function UFO:new( id, data )

local o = {}
local mt = {
__index = UFO
}
setmetatable( o, mt )

– set properties
o._id = id
o._data = data

return o
end

local ufo = UFO:new( ‘34324’, { x=5, y=10 } )[/lua]
btw, the documentation site is: docs.davidmccuskey.com

cheers,
dmc [import]uid: 74908 topic_id: 35562 reply_id: 141591[/import]

Hi dmc,

Your libraries were insightful on their own but your thorough documentation really makes them stand out. I appreciate the link to that information.

Thanks for the code sample, it’s enlightening. I can see how I was on a different path. I especially appreciate the insight regarding the setmetatable() use in the constructor.

If you do have time to share a chaining sample, that would be awesome since ultimately that’s what I’m working up to.

[import]uid: 105707 topic_id: 35562 reply_id: 141934[/import]

Hello EHO,

i edited my previous code sample, i had one to many calls to setmetable() in there, plus i am reusing the class metatable.

here is an example with inheritance:

[lua]
– Main.lua –
print("---------------------------------------------------")

–== Base Class ==–

local UFOBaseClass = {}
local UFOBaseClass_meta = {
__index = UFOBaseClass
}
– constructor –

function UFOBaseClass:new()

local o = {}
setmetatable( o, UFOBaseClass_meta )

– set properties
o._vel = nil – velocity

return o
end
– class method –

function UFOBaseClass:move()
print( “velocity=” … tostring( self._vel ) )
end

–== Fast Class ==–

local UFOFastClass = UFOBaseClass:new()
local UFOFastClass_meta = {
__index = UFOFastClass
}
– constructor –

function UFOFastClass:new( id, data )

local o = {}
setmetatable( o, UFOFastClass_meta )

– set properties
o._id = id
o._data = data
o._vel = 42

return o
end

–== Slow Class ==–

local UFOSlowClass = UFOBaseClass:new()
local UFOSlowClass_meta = {
__index = UFOSlowClass
}
– constructor –

function UFOSlowClass:new( id, data )

local o = {}
setmetatable( o, UFOSlowClass_meta )

– set properties
o._id = id
o._data = data
o._vel = 12

return o
end

–== Create Instances of Our Classes ==–

local ufo_f = UFOFastClass:new( “423432”, { x=1, y=40 } )
local ufo_s = UFOSlowClass:new( “1234”, { x=20, y=400 } )

print( UFOFastClass._id )
UFOFastClass:move()
print( “\n” )

print( ufo_f._id )
ufo_f:move()
print( “\n” )

print( ufo_s._id )
ufo_s:move()
[/lua]

output is:

---------------------------------------------------  
nil  
velocity=nil  
423432  
velocity=42  
1234  
velocity=12  

HTH.

cheers,
dmc [import]uid: 74908 topic_id: 35562 reply_id: 141987[/import]

Hi dmc,

This example is very helpful.

One confusion I had to get my head around is that I was hung up on the idea that the __index table is the table the object looks to when a field doesn’t exist in the current object table. Somehow I expected the __index value of each of the sub-classes to point directly at UFOBaseClass:new(). Since that is a function and it returns a table I see that that makes sense that you have to set the metatable to a table that is created out of UFOBaseClass:new(). So it’s making sense to me but I had to really force myself to look at this differently than what I was expecting.

It is a paradigm shift for me to start working this way so I guess this is just taking a little more time than I thought to really settle in. The concept seems so clear looking at your example but I’m taking longer to get going with it than I expected.

I’ve really been getting a lot out of your documentation.

Thanks for all your help, it’s made a real difference for me :slight_smile: [import]uid: 105707 topic_id: 35562 reply_id: 142217[/import]

PS - you used some really clever values for your inheritance example. I had to really think on why UFOFastClass:move() outputs “velocity=nil” and why the UFOFastClass object method ufo_f:move() outputs “velocity=42”. Makes sense now that I’ve figured it out but took me a minute of head scratching. Great example that really distinguishes some subtleties :slight_smile: [import]uid: 105707 topic_id: 35562 reply_id: 142222[/import]