How can I destroy all objects without using a global function?

Hi Guys

I have been trying to get back into OOP with some degree of success. I have used Jonathan Beebe’s tutorial on Modular Classes using metatables.

I am writing a business app and I have a button ‘class’, everything seems to work as it should. The issue I am now having is how do I go about removing all of the objects when certain conditions are met, given that each object doesn’t know about the other objects. I could use a global function to achieve this, but I am wondering if there is a better way?

Here is a simple example that works, but it is using a global function, is this the best way?

main.lua
[lua]
local buttons = require(“button”) – require button class
local allButtons = {} – table to hold all the buttons
for t = 1, 3 do --loops to create buttons
for i = 1, 3 do
allButtons[#allButtons + 1] = buttons.new(i * 55, t * 55) --create button
allButtons[#allButtons]:start() – add the listener
end
end

function destroyButtons() – global function to delete all objects - bad Idea?
for i = #allButtons, 1, -1 do
allButtons[#allButtons]:kill()
allButtons[#allButtons] = nil
end
allButtons = nil

end
[/lua]

button.lua
[lua]
local button = {}
local button_mt = { __index = button }

local function myRectListener (event, self) – button listener
self.counter = self.counter + 1
self.myRectangle.strokeWidth = self.myRectangle.strokeWidth + 1 – do something with the counter
print(self.counter)
if self.counter == 4 then
print (“Destroy all buttons”) – how do I do this? Call a global funciton?
destroyButtons()
end
end

function button.new(xloc,yloc)
local button ={}
button.myRectangle = display.newRect(xloc, yloc, 50, 50)
button.myRectangle.strokeWidth = 1
button.myRectangle:setFillColor(140, 140, 140)
button.myRectangle:setStrokeColor(180, 180, 180)
button.counter = 0 – counter to store how many times the button has been pressed
return setmetatable( button, button_mt )
end

function button:start()
self.myRectangle:addEventListener(“tap”,function(event) myRectListener(event,self) end) – add a lister to button
end

function button:kill()
self.myRectangle:removeEventListener(“tap”,function(event) myRectListener(event,self) end) – remove the listener
self.myRectangle:removeSelf()
self.myRectangle = nil
end

return button
[/lua]

Thanks!

Craig [import]uid: 184705 topic_id: 36309 reply_id: 336309[/import]

Hi Craig,
This doesn’t answer your specific question, but have you read and worked through the recent Lua-OOP methodology by Omid Ahourai? I know that he put a considerable amount of work into writing up and detailing this, but I haven’t tested it myself. Anyway, here’s the link:

http://www.ardentkid.com/blog/from-zero-to-oo-ardentkids-guide-to-object-oriented-lua-with-corona-sdk

Best regards,
Brent [import]uid: 200026 topic_id: 36309 reply_id: 144286[/import]

Thanks Brent, I had looked at this tutorial earlier in my travels but it was too confusing… However I decided to follow your advice and look at it again… I have found it much easier to consume since having ago at Jonathan Beebe’s tutorial, the concepts don’t seem so foreign to me…

Thanks again…

PS Can anyone tell me what “…” means in line 10 of enemy.lua [import]uid: 184705 topic_id: 36309 reply_id: 144389[/import]

hi Craig,

to answer your question, using a single function to remove your buttons is fine. as a good rule of thumb, objects should be created and destroyed at the same “context level” in an app. you have done this in your code example by creating and destroying the buttons at the same level, ie, in main.lua at the App level, so this is good.

however, to promote better programming practices, this function shouldn’t be global. currently destroyButtons() is called from within a button object thus must be global. that breaks OO because you have given your buttons an “awareness” of their parent or surroundings which they shouldn’t have. in OO, the button class should only know about its world and nothing else.

so what we need to do is just improve upon how the destroyButtons() is called.
enter events and listeners

probably the biggest benefit of events is to “decouple” different parts of an application. meaning we can still have communication, but if no one is listening then it doesn’t matter because nothing is going to break.
in your case we can create a custom event to achieve the same effect as the global method call, improve encapsulation within the app, and increase the re-usability of your button objects. win, win, win !

note that only Corona objects can be used to broadcast events. so the challenge in setting this up with your button class is that the Corona object (the newRect) isn’t at the top level of the button object, but rather located at the property ‘myRectangle’. so you have to take this into consideration.
here is your code re-worked. i have slightly moved around some of the code and added comments.
[lua]
– main.lua

local buttons = require(“button”) – require button class

local allButtons = {} – table to hold all the buttons

– this function isn’t a bad idea, but don’t think of it as global
– in fact, let’s make it local to force correct programming

– we need to pre-declare this because of removeEventListener()

local destroyButtons

destroyButtons = function()
print( “got button limit event, destroy all buttons” )

for i = #allButtons, 1, -1 do
local button = allButtons[#allButtons]

– must use the Corona object !!
button.myRectangle:removeEventListener( ‘button_limit_event’, destroyButtons )
button:kill()

allButtons[#allButtons] = nil
end
allButtons = nil

end

– this function to explicitly “show” that there is balance in the app.
– ie, create and destroy now have equal counterparts at this level

local createButtons = function()

for t = 1, 3 do --loops to create buttons
for i = 1, 3 do

local button = buttons.new(i * 55, t * 55) --create button
allButtons[#allButtons + 1] = button

– initialize, add the button listener
button:init()

– listen for button limit, must be on Corona object !
button.myRectangle:addEventListener( ‘button_limit_event’, destroyButtons )

end
end

end

createButtons()

[/lua]

[lua]
– button.lua
local button = {}
local button_mt = { __index = button }

local function myRectListener (event, self) – button listener
self.counter = self.counter + 1
self.myRectangle.strokeWidth = self.myRectangle.strokeWidth + 1 – do something with the counter
print(self.counter)

– difference here is that we’re creating a custom event
– to send out when our limit is reached and NOT calling
– the destroyButton() function directly.

if self.counter == 4 then
print (“Our limit has been reached, tell someone”)

– create our custom event
local e = {
name=“button_limit_event”
}
self.myRectangle:dispatchEvent( e )
end
end

function button.new(xloc,yloc)
local button ={}
button.myRectangle = display.newRect(xloc, yloc, 50, 50)
button.myRectangle.strokeWidth = 1
button.myRectangle:setFillColor(140, 140, 140)
button.myRectangle:setStrokeColor(180, 180, 180)
button.counter = 0 – counter to store how many times the button has been pressed
return setmetatable( button, button_mt )
end

function button:init()
self.myRectangle:addEventListener(“tap”,function(event) myRectListener(event,self) end) – add a lister to button
end

function button:kill()
self.myRectangle:removeEventListener(“tap”,function(event) myRectListener(event,self) end) – remove the listener
self.myRectangle:removeSelf()
self.myRectangle = nil
end

return button

[/lua]

note that i didn’t call the custom event “destroy_buttons”, because in a way that also implies that the button class knows what is supposed to happen when it sends the event. the issue is that some other app could re-use this button class but want to interpret the action of the event differently, ie, NOT delete the buttons. so, events should only state the facts of what happened.

a concrete example of this is the “tap” event on a Corona object. the event isn’t called “play_sound” because that’s just an action that the higher-level app will perform once it receives the event. the base event is the button saying “hey, someone just tapped me”. that’s it. playing a sound is one action on the event. another app could change the color of the background, or get JSON data from a server.
this is a small detail, but i think helpful when architecting software, especially if you are others are supposed to re-use parts of the code.
cheers,
dmc

ps, the removeEventListener() in your button class won’t actually work. you should read this thread to find out why.

https://developer.coronalabs.com/forum/2013/01/26/advanced-oop-removeeventlistener-question
[import]uid: 74908 topic_id: 36309 reply_id: 144452[/import]

the three dots in the method call represents a variable number of arguments to a function. the dots at line 10 are related to the dots at line 7. ie, new(…) is accepting a variable number of args, and instance:initialize(…) is just passing them along, whatever they may be.

[lua]

– accept a variable number of arguments, using ‘…’

function myMethod( … )

– pass our incoming args to another method
– we don’t know what they are or how many
anotherMethod( … )

end

[/lua]
there is more information here:

http://www.lua.org/pil/5.2.html
cheers,
dmc [import]uid: 74908 topic_id: 36309 reply_id: 144455[/import]

WOW… Thank you so much… I have just read about custom events in Ardentkids blog, and was wondering if these might be used… It is awesome that you took the time to redo my code, I am really ‘starting’ to get an idea of how it works in Corona/Lua…

Do these custom eventlisteners have much overhead? Is it OK to due them for everything, or should they be saved for bigger tasks?

Thanks heaps… Really appreciate it!

Craig [import]uid: 184705 topic_id: 36309 reply_id: 144498[/import]

Just adding to my previous comment, what I meant was is it OK to broadcast small detail? For example in Ardentkids blog he has an example where the mouse broadcasts that he has appeared, would it be OK to extend this so that he broadcasts his X and Y positions so that the cat knows where he is? Not making a game myself, I am just trying to sort things out in my head… Thanks… [import]uid: 184705 topic_id: 36309 reply_id: 144554[/import]

Hi Craig,

i’m glad that you found everything useful. :slight_smile:

for your question, using events is the way event-driven applications communicate, and Corona SDK is an event-driven framework. as such, you should think about using events whenever you can.

consider the Runtime event “enterFrame”. if you “subscribe” to this event then your callback is going to get called on every new frame which could be 20-60 times per second !

also you can pass around as much information as you like, you just need to put it inside of the custom event structure:

local custom\_event = {  
 name="button\_limit\_event", -- having 'name' is the most important part  
 message = "this is the big message", -- strings  
 reference = self, -- other objects  
 color = { 255, 168, 158 } -- tables  
}  

cheers,
dmc [import]uid: 74908 topic_id: 36309 reply_id: 144579[/import]

Hi Craig,
This doesn’t answer your specific question, but have you read and worked through the recent Lua-OOP methodology by Omid Ahourai? I know that he put a considerable amount of work into writing up and detailing this, but I haven’t tested it myself. Anyway, here’s the link:

http://www.ardentkid.com/blog/from-zero-to-oo-ardentkids-guide-to-object-oriented-lua-with-corona-sdk

Best regards,
Brent [import]uid: 200026 topic_id: 36309 reply_id: 144286[/import]

Thanks Brent, I had looked at this tutorial earlier in my travels but it was too confusing… However I decided to follow your advice and look at it again… I have found it much easier to consume since having ago at Jonathan Beebe’s tutorial, the concepts don’t seem so foreign to me…

Thanks again…

PS Can anyone tell me what “…” means in line 10 of enemy.lua [import]uid: 184705 topic_id: 36309 reply_id: 144389[/import]

hi Craig,

to answer your question, using a single function to remove your buttons is fine. as a good rule of thumb, objects should be created and destroyed at the same “context level” in an app. you have done this in your code example by creating and destroying the buttons at the same level, ie, in main.lua at the App level, so this is good.

however, to promote better programming practices, this function shouldn’t be global. currently destroyButtons() is called from within a button object thus must be global. that breaks OO because you have given your buttons an “awareness” of their parent or surroundings which they shouldn’t have. in OO, the button class should only know about its world and nothing else.

so what we need to do is just improve upon how the destroyButtons() is called.
enter events and listeners

probably the biggest benefit of events is to “decouple” different parts of an application. meaning we can still have communication, but if no one is listening then it doesn’t matter because nothing is going to break.
in your case we can create a custom event to achieve the same effect as the global method call, improve encapsulation within the app, and increase the re-usability of your button objects. win, win, win !

note that only Corona objects can be used to broadcast events. so the challenge in setting this up with your button class is that the Corona object (the newRect) isn’t at the top level of the button object, but rather located at the property ‘myRectangle’. so you have to take this into consideration.
here is your code re-worked. i have slightly moved around some of the code and added comments.
[lua]
– main.lua

local buttons = require(“button”) – require button class

local allButtons = {} – table to hold all the buttons

– this function isn’t a bad idea, but don’t think of it as global
– in fact, let’s make it local to force correct programming

– we need to pre-declare this because of removeEventListener()

local destroyButtons

destroyButtons = function()
print( “got button limit event, destroy all buttons” )

for i = #allButtons, 1, -1 do
local button = allButtons[#allButtons]

– must use the Corona object !!
button.myRectangle:removeEventListener( ‘button_limit_event’, destroyButtons )
button:kill()

allButtons[#allButtons] = nil
end
allButtons = nil

end

– this function to explicitly “show” that there is balance in the app.
– ie, create and destroy now have equal counterparts at this level

local createButtons = function()

for t = 1, 3 do --loops to create buttons
for i = 1, 3 do

local button = buttons.new(i * 55, t * 55) --create button
allButtons[#allButtons + 1] = button

– initialize, add the button listener
button:init()

– listen for button limit, must be on Corona object !
button.myRectangle:addEventListener( ‘button_limit_event’, destroyButtons )

end
end

end

createButtons()

[/lua]

[lua]
– button.lua
local button = {}
local button_mt = { __index = button }

local function myRectListener (event, self) – button listener
self.counter = self.counter + 1
self.myRectangle.strokeWidth = self.myRectangle.strokeWidth + 1 – do something with the counter
print(self.counter)

– difference here is that we’re creating a custom event
– to send out when our limit is reached and NOT calling
– the destroyButton() function directly.

if self.counter == 4 then
print (“Our limit has been reached, tell someone”)

– create our custom event
local e = {
name=“button_limit_event”
}
self.myRectangle:dispatchEvent( e )
end
end

function button.new(xloc,yloc)
local button ={}
button.myRectangle = display.newRect(xloc, yloc, 50, 50)
button.myRectangle.strokeWidth = 1
button.myRectangle:setFillColor(140, 140, 140)
button.myRectangle:setStrokeColor(180, 180, 180)
button.counter = 0 – counter to store how many times the button has been pressed
return setmetatable( button, button_mt )
end

function button:init()
self.myRectangle:addEventListener(“tap”,function(event) myRectListener(event,self) end) – add a lister to button
end

function button:kill()
self.myRectangle:removeEventListener(“tap”,function(event) myRectListener(event,self) end) – remove the listener
self.myRectangle:removeSelf()
self.myRectangle = nil
end

return button

[/lua]

note that i didn’t call the custom event “destroy_buttons”, because in a way that also implies that the button class knows what is supposed to happen when it sends the event. the issue is that some other app could re-use this button class but want to interpret the action of the event differently, ie, NOT delete the buttons. so, events should only state the facts of what happened.

a concrete example of this is the “tap” event on a Corona object. the event isn’t called “play_sound” because that’s just an action that the higher-level app will perform once it receives the event. the base event is the button saying “hey, someone just tapped me”. that’s it. playing a sound is one action on the event. another app could change the color of the background, or get JSON data from a server.
this is a small detail, but i think helpful when architecting software, especially if you are others are supposed to re-use parts of the code.
cheers,
dmc

ps, the removeEventListener() in your button class won’t actually work. you should read this thread to find out why.

https://developer.coronalabs.com/forum/2013/01/26/advanced-oop-removeeventlistener-question
[import]uid: 74908 topic_id: 36309 reply_id: 144452[/import]

the three dots in the method call represents a variable number of arguments to a function. the dots at line 10 are related to the dots at line 7. ie, new(…) is accepting a variable number of args, and instance:initialize(…) is just passing them along, whatever they may be.

[lua]

– accept a variable number of arguments, using ‘…’

function myMethod( … )

– pass our incoming args to another method
– we don’t know what they are or how many
anotherMethod( … )

end

[/lua]
there is more information here:

http://www.lua.org/pil/5.2.html
cheers,
dmc [import]uid: 74908 topic_id: 36309 reply_id: 144455[/import]

WOW… Thank you so much… I have just read about custom events in Ardentkids blog, and was wondering if these might be used… It is awesome that you took the time to redo my code, I am really ‘starting’ to get an idea of how it works in Corona/Lua…

Do these custom eventlisteners have much overhead? Is it OK to due them for everything, or should they be saved for bigger tasks?

Thanks heaps… Really appreciate it!

Craig [import]uid: 184705 topic_id: 36309 reply_id: 144498[/import]

Just adding to my previous comment, what I meant was is it OK to broadcast small detail? For example in Ardentkids blog he has an example where the mouse broadcasts that he has appeared, would it be OK to extend this so that he broadcasts his X and Y positions so that the cat knows where he is? Not making a game myself, I am just trying to sort things out in my head… Thanks… [import]uid: 184705 topic_id: 36309 reply_id: 144554[/import]

Hi Craig,

i’m glad that you found everything useful. :slight_smile:

for your question, using events is the way event-driven applications communicate, and Corona SDK is an event-driven framework. as such, you should think about using events whenever you can.

consider the Runtime event “enterFrame”. if you “subscribe” to this event then your callback is going to get called on every new frame which could be 20-60 times per second !

also you can pass around as much information as you like, you just need to put it inside of the custom event structure:

local custom\_event = {  
 name="button\_limit\_event", -- having 'name' is the most important part  
 message = "this is the big message", -- strings  
 reference = self, -- other objects  
 color = { 255, 168, 158 } -- tables  
}  

cheers,
dmc [import]uid: 74908 topic_id: 36309 reply_id: 144579[/import]

Hi Craig,
This doesn’t answer your specific question, but have you read and worked through the recent Lua-OOP methodology by Omid Ahourai? I know that he put a considerable amount of work into writing up and detailing this, but I haven’t tested it myself. Anyway, here’s the link:

http://www.ardentkid.com/blog/from-zero-to-oo-ardentkids-guide-to-object-oriented-lua-with-corona-sdk

Best regards,
Brent [import]uid: 200026 topic_id: 36309 reply_id: 144286[/import]

Thanks Brent, I had looked at this tutorial earlier in my travels but it was too confusing… However I decided to follow your advice and look at it again… I have found it much easier to consume since having ago at Jonathan Beebe’s tutorial, the concepts don’t seem so foreign to me…

Thanks again…

PS Can anyone tell me what “…” means in line 10 of enemy.lua [import]uid: 184705 topic_id: 36309 reply_id: 144389[/import]

hi Craig,

to answer your question, using a single function to remove your buttons is fine. as a good rule of thumb, objects should be created and destroyed at the same “context level” in an app. you have done this in your code example by creating and destroying the buttons at the same level, ie, in main.lua at the App level, so this is good.

however, to promote better programming practices, this function shouldn’t be global. currently destroyButtons() is called from within a button object thus must be global. that breaks OO because you have given your buttons an “awareness” of their parent or surroundings which they shouldn’t have. in OO, the button class should only know about its world and nothing else.

so what we need to do is just improve upon how the destroyButtons() is called.
enter events and listeners

probably the biggest benefit of events is to “decouple” different parts of an application. meaning we can still have communication, but if no one is listening then it doesn’t matter because nothing is going to break.
in your case we can create a custom event to achieve the same effect as the global method call, improve encapsulation within the app, and increase the re-usability of your button objects. win, win, win !

note that only Corona objects can be used to broadcast events. so the challenge in setting this up with your button class is that the Corona object (the newRect) isn’t at the top level of the button object, but rather located at the property ‘myRectangle’. so you have to take this into consideration.
here is your code re-worked. i have slightly moved around some of the code and added comments.
[lua]
– main.lua

local buttons = require(“button”) – require button class

local allButtons = {} – table to hold all the buttons

– this function isn’t a bad idea, but don’t think of it as global
– in fact, let’s make it local to force correct programming

– we need to pre-declare this because of removeEventListener()

local destroyButtons

destroyButtons = function()
print( “got button limit event, destroy all buttons” )

for i = #allButtons, 1, -1 do
local button = allButtons[#allButtons]

– must use the Corona object !!
button.myRectangle:removeEventListener( ‘button_limit_event’, destroyButtons )
button:kill()

allButtons[#allButtons] = nil
end
allButtons = nil

end

– this function to explicitly “show” that there is balance in the app.
– ie, create and destroy now have equal counterparts at this level

local createButtons = function()

for t = 1, 3 do --loops to create buttons
for i = 1, 3 do

local button = buttons.new(i * 55, t * 55) --create button
allButtons[#allButtons + 1] = button

– initialize, add the button listener
button:init()

– listen for button limit, must be on Corona object !
button.myRectangle:addEventListener( ‘button_limit_event’, destroyButtons )

end
end

end

createButtons()

[/lua]

[lua]
– button.lua
local button = {}
local button_mt = { __index = button }

local function myRectListener (event, self) – button listener
self.counter = self.counter + 1
self.myRectangle.strokeWidth = self.myRectangle.strokeWidth + 1 – do something with the counter
print(self.counter)

– difference here is that we’re creating a custom event
– to send out when our limit is reached and NOT calling
– the destroyButton() function directly.

if self.counter == 4 then
print (“Our limit has been reached, tell someone”)

– create our custom event
local e = {
name=“button_limit_event”
}
self.myRectangle:dispatchEvent( e )
end
end

function button.new(xloc,yloc)
local button ={}
button.myRectangle = display.newRect(xloc, yloc, 50, 50)
button.myRectangle.strokeWidth = 1
button.myRectangle:setFillColor(140, 140, 140)
button.myRectangle:setStrokeColor(180, 180, 180)
button.counter = 0 – counter to store how many times the button has been pressed
return setmetatable( button, button_mt )
end

function button:init()
self.myRectangle:addEventListener(“tap”,function(event) myRectListener(event,self) end) – add a lister to button
end

function button:kill()
self.myRectangle:removeEventListener(“tap”,function(event) myRectListener(event,self) end) – remove the listener
self.myRectangle:removeSelf()
self.myRectangle = nil
end

return button

[/lua]

note that i didn’t call the custom event “destroy_buttons”, because in a way that also implies that the button class knows what is supposed to happen when it sends the event. the issue is that some other app could re-use this button class but want to interpret the action of the event differently, ie, NOT delete the buttons. so, events should only state the facts of what happened.

a concrete example of this is the “tap” event on a Corona object. the event isn’t called “play_sound” because that’s just an action that the higher-level app will perform once it receives the event. the base event is the button saying “hey, someone just tapped me”. that’s it. playing a sound is one action on the event. another app could change the color of the background, or get JSON data from a server.
this is a small detail, but i think helpful when architecting software, especially if you are others are supposed to re-use parts of the code.
cheers,
dmc

ps, the removeEventListener() in your button class won’t actually work. you should read this thread to find out why.

https://developer.coronalabs.com/forum/2013/01/26/advanced-oop-removeeventlistener-question
[import]uid: 74908 topic_id: 36309 reply_id: 144452[/import]

the three dots in the method call represents a variable number of arguments to a function. the dots at line 10 are related to the dots at line 7. ie, new(…) is accepting a variable number of args, and instance:initialize(…) is just passing them along, whatever they may be.

[lua]

– accept a variable number of arguments, using ‘…’

function myMethod( … )

– pass our incoming args to another method
– we don’t know what they are or how many
anotherMethod( … )

end

[/lua]
there is more information here:

http://www.lua.org/pil/5.2.html
cheers,
dmc [import]uid: 74908 topic_id: 36309 reply_id: 144455[/import]

WOW… Thank you so much… I have just read about custom events in Ardentkids blog, and was wondering if these might be used… It is awesome that you took the time to redo my code, I am really ‘starting’ to get an idea of how it works in Corona/Lua…

Do these custom eventlisteners have much overhead? Is it OK to due them for everything, or should they be saved for bigger tasks?

Thanks heaps… Really appreciate it!

Craig [import]uid: 184705 topic_id: 36309 reply_id: 144498[/import]