OOP + Runtime Question

Nope, no physics involved - all ol’fashioned programatic animations. [import]uid: 33275 topic_id: 33208 reply_id: 131946[/import]

The reason behind the second line not working was because of this:

local dropTheOrbs = orbGroup[i]

The i from the for loop was always referring to 1-8 - so the second line of Orbs which would be 9-16, wouldn’t be affected.

I have now changed this to:

local function createLine()  
 --dropTheOrbs()  
 for i = #orbGroup+1,#orbGroup+8 do  
 orbGroup[#orbGroup+1] = orbs.new(xPos, yPos)  
 local dropTheOrbs = orbGroup[i]  
 Runtime:addEventListener("enterFrame", dropTheOrbs)  
 xPos = xPos + 40  
 if xPos \> 320 then  
 xPos = 20  
 end  
  
 end  
 --Runtime:addEventListener("enterFrame", dropOrbs)  
end  

Which is working - but again this doesn’t exactly seem very neat in an OOP kinda way. [import]uid: 33275 topic_id: 33208 reply_id: 131947[/import]

I would approach this differently:

local function dropOrbs()  
 for i = 1, #orbGroup do  
 orfbGroup[i]:drop()  
 end  
end   
  
local function createLine()  
 for i = #orbGroup+1,#orbGroup+8 do  
 orbGroup[#orbGroup+1] = orbs.new(xPos, yPos)  
 xPos = xPos + 40  
 if xPos \> 320 then  
 xPos = 20  
 end  
   
 end  
end  
  
createLine()  
Runtime:addEventListener("enterFrame", dropOrbs)  
  

or something like that. If I’m reading this correctly you have an array of orb objects (orbGroup), you spawn a new 8 each time. You’re code to drop a single orb is in orb:drop().

You only want to create one Runtime listener. Your code was creating multiple runtime listners (which of course you can do, but you have to also remove them when your object goes away). enterFrame wants a function, not an object and you were trying to pass the object in, not a method to run each frame. [import]uid: 19626 topic_id: 33208 reply_id: 131950[/import]

You can also play around with Closures:

function wrapParam(func, param)  
 return function(...)  
 return func(param, ...)  
 end  
end  

This little function allows you to generate a new function from an existing one that has 1 parameter already assigned.

so then when constructing an orb you do:

--Assuming you have a variable 'orb' that is of type Orb  
Runtime:addEventListener("enterFrame", wrapParam(orb.drop, orb))  

Which will call drop with the signature ‘orb.drop(self, event)’ or ‘orb:drop(event)’ (these are the same) since wrapParam supplies the self parameter, and so event moves down into the second parameter [import]uid: 134101 topic_id: 33208 reply_id: 131954[/import]

Wow Ntero - you’ve just completely blown my mind, in a good way :slight_smile: I’m going to have to go away and have a good read about closures, always just assumed these were just anonymous funcitons but clearly not.

Robmiracle - from your suggestion it seems as though I would have to take the drop ‘behaviour’ out of the Orb class, which probably is a good idea. [import]uid: 33275 topic_id: 33208 reply_id: 131955[/import]

Hey Ntero/Rob - if either of you guys have a spare 5 minutes, would love to tap into your expertise.

So I’ve got the Orbs dropping with a Runtime; however I need to build in collision detection to see if there’s any orbs already below.

I currently have Orb.lua and Deck.lua (represents the new line of Orbs, i.e it calls the spawn function from Orb.lua and sets up the runtime listener to make them drop).

The problem is building in the collision - this obviously can’t go in Orb.lua as there is no knowledge of other Orb instances in this class. So I’ve tried to build it in Deck.lua, which has the array of Orbs.

What I planned to do was call the collision function in the drop method contained in Orb.lua - i.e when that particular Orb instance is dropping check for others in the Orb array below it.

The problem I’m facing is one of circular dependency - which with my poor OOP knowledge seems to be a bad/impossible thing to cure.

Deck.lua requires Orb.lua to spawn the Orbs

and

Orb.lua requires Deck.lua to call the collision method.

Essentially this is the code:

Orb.lua

function Orb:drop()  
 if self.move == true then  
 --print("i: "..i)  
 --print("orbs[i].y: "..orbs[i].y)  
 print("LESS THEN")   
 print("self.id: "..self.id)  
 --calculatePos(orbs[i])  
  
 deck.collide(self)  
 if self.move == true and self.image.y \< 400 then  
  
 self.image:translate(0,self.weight)  
 else  
 print("DONT MOVE")  
 self.move = false  
 Runtime:removeEventListener("enterFrame", self)  
 --dumpOrbs()  
 end  
 end  
end  

Deck.lua

function Deck.addOrbs()  
 for i = #orbGroup+1,#orbGroup+8 do  
 local orb = orbs.new(xPos, yPos)  
 orbGroup[#orbGroup+1] = orb  
 local dropTheOrbs = orbGroup[i]  
 Runtime:addEventListener("enterFrame", wrapParam(orb.drop, orb))  
 --Runtime:addEventListener("enterFrame", dropTheOrbs)  
 xPos = xPos + 40  
 if xPos \> 320 then  
 xPos = 20  
 end  
 end  
  
end  
  
function Deck.collide(orb)  
 print("orb.id\*: "..orb.id)  
 local orb = orb  
 for i=#orbGroup,1,-1 do  
 if orbGroup[i].image.x == orb.image.x and orbGroup[i] ~= orb then  
 if orb.move == true then  
  
 if (orb.image.y \> (orbs[i].image.y - orbGroup[i].image.contentHeight)) then  
 print("COLLISION")  
 orb.move = false  
 else  
 orb.move = true  
 end  
 end  
 else  
 print("NOT THE ORB YOU'RE LOOKING FOR")  
 end  
 end  
end  

The problem is this obviously causes a Runtime error because Orb needs Deck and Deck needs Orb. I realise that I’ve structured this poorly, but I’m struggling to think of how to do this properly.

Many thanks guys… [import]uid: 33275 topic_id: 33208 reply_id: 132061[/import]

The difference between a closure and an anonymous function has to do with the concept of upvalues. The idea being that the returned function has access to the variable ‘param’ from an entirely different scope relative to where it’s called from. Some languages allow you to maintain the reference scope from where it was declared, and not from where it’s being called from, C# and Lua being the two I’m most familiar with. That and metamethods are probably my two favourite Lua features that help make it stand above most other interpreted languages.

There is one other Closure helper function I use a lot:

function generateCommand(func, ...)  
 local temp = {n=select('#', ...), ...}  
 return function()  
 return func(unpack(temp, 1, temp.n))  
 end  
end  

This one allows you to attach as many parameters as you want to be invoked later. i.e. generateCommand(print, “This”, “is”, “a”, “Test”) will give you a parameterless function that when invoked will call print(“This”, “is”, “a”, “Test”). The downside to this one is that it won’t accept any parameters later, meaning that to use this instead would mean you’d lose the event from a Corona Event. temp is a single upvalue that stores all parameters in a table (and n, being the number of parameters stored, otherwise a nil in the middle of the parameters would break it), and is accessible to the returned function (and only the returned function, effectively) regardless of where it is invoked.

edit: in the previous wrapParam example, the fact that you can access func and param inside of function(…) is the unique part, since that function, and stack memory are long gone by the time you call the function. Those two variables are accessible to the function variable for it’s entire lifetime. If you want to get very tricky, we do a few instances in our own code with hand offs, where a chain of functions pass their upvalues (usually callbacks at the end of the chain) down the chain of function calls, which means that 5 anonymous function calls later you can still access variables given to you in the first function call, without actually having to store them anywhere (each new function declaration can access the upvalue if it’s within the scope the function was declared in). It helps us write very little code for sometimes very complex sequential actions. [import]uid: 134101 topic_id: 33208 reply_id: 131965[/import]

You’re not really dropping the move/drop code from the object, you’re just maintaining a list of objects and iterating over it calling each object’s drop method. Still object oriented and the object is still in control of it’s actual movement.

If you wanted to do this without a runtime listener, you could simply have the drop method do a transition.to() instead of moving it a fixed amount. [import]uid: 19626 topic_id: 33208 reply_id: 131968[/import]

This may be a time you have to break out of the OOP mindset. Why not just let deck.lua do all the work. It knows your list of orbs, so just loop over the list and see if any of them are touching…
[import]uid: 19626 topic_id: 33208 reply_id: 132074[/import]

Cheers Rob - I think that’s where I’m struggling, my determination to keep everything as strictly OOP as possible seems to be causing more problems then it’s solving :slight_smile: [import]uid: 33275 topic_id: 33208 reply_id: 132077[/import]

Hey Ntero - I’m using your wrapParam example - even though I’m not 100% on it yet, it works nicely. The only problem I’m facing is that I want to remove that event listener when the Orbs have hit the bottom, and struggling to remove this.

Do you have any ideas how I could achieve this? [import]uid: 33275 topic_id: 33208 reply_id: 132089[/import]

Yeah, that’s one downside to wrapParam, is that it can make it a little tricker to track function references.

You can always attach it to orb:

--in orbs.new()  
orb.dropCmd = wrapParam(orb.drop, orb)  
Runtime:addEventListener("enterFrame", orb.dropCmd)  
  
--remove enterFrame for the orb from Deck.lua  
  
--in Drop()  
Runtime:removeEventListener("enterFrame", self.dropCmd)  
  

Also, if eventListeners are the only people calling drop() you can even replace the function with the wrapParam version (though this adds a sort of hidden requirement to some functions which can be bad)

With the first question, similar to what rob is saying for the original point, you could have Deck.lua have the enterFrame listener, and have it A) iterate over all Orbs to move them, then B) check each orb against the other ones for collisions after all movement is completed.

This solves 2 issues, 1) Your dependency issues, the Deck can take full control of managing the orbs, and so the Orbs no longer need to know about the deck and B) You can avoid checking collisions against orbs that have yet to move, which avoids what should not have been a collision, and helps reduce processing, also you can guarantee if you checked A colliding with B, you won’t check B colliding with A on the same frame (a waste since you already know the result)

function Deck.onFrame()  
 for i = 1, #orbGroup do  
 local orb = orbGroup[i]  
 if orb.move then --This if could be replaced with an orb:update() -like call so the orb can manage it's own state better  
 orb:drop()  
 end  
 end  
  
 for i = 1, #orbGroup do  
 for j = i + 1, #orbGroup do --Looping like this makes sure you compare each pairing once and only once  
 --Do Collision check of orbGroup[i] vs orbGroup[j] here and process accordingly  
 end  
 end  
end  
  
--Do this once for the lifetime of the deck object  
Runtime:addEventListener("enterFrame", Deck.onFrame)  

Also, you can be lazy and simply do the require in place to avoid the circular dependency, i.e. in drop() replace deck.collide( with require(“deck”).collide(, and the issue will go away. This has to do with how Lua/Require work, and the fact that a require loads a file once, but caches the returned results, so simply requiring outside the file scope, and putting it in a function called afterwards fixes circular dependencies. [import]uid: 134101 topic_id: 33208 reply_id: 132094[/import]

I went with the lazy option and required deck in drop() - but I’ll probably look at that later. I figure it’s probably best to get things working and then worry about the implementation afterwards, I can only stand pen-and-paper theory for so long :slight_smile:

Thanks for the tip about storing the function on the object - for some bizarre reason I completely overlooked that simple, yet elegant solution :slight_smile:

Cheers… [import]uid: 33275 topic_id: 33208 reply_id: 132097[/import]

Hey Ntero/Rob - if either of you guys have a spare 5 minutes, would love to tap into your expertise.

So I’ve got the Orbs dropping with a Runtime; however I need to build in collision detection to see if there’s any orbs already below.

I currently have Orb.lua and Deck.lua (represents the new line of Orbs, i.e it calls the spawn function from Orb.lua and sets up the runtime listener to make them drop).

The problem is building in the collision - this obviously can’t go in Orb.lua as there is no knowledge of other Orb instances in this class. So I’ve tried to build it in Deck.lua, which has the array of Orbs.

What I planned to do was call the collision function in the drop method contained in Orb.lua - i.e when that particular Orb instance is dropping check for others in the Orb array below it.

The problem I’m facing is one of circular dependency - which with my poor OOP knowledge seems to be a bad/impossible thing to cure.

Deck.lua requires Orb.lua to spawn the Orbs

and

Orb.lua requires Deck.lua to call the collision method.

Essentially this is the code:

Orb.lua

function Orb:drop()  
 if self.move == true then  
 --print("i: "..i)  
 --print("orbs[i].y: "..orbs[i].y)  
 print("LESS THEN")   
 print("self.id: "..self.id)  
 --calculatePos(orbs[i])  
  
 deck.collide(self)  
 if self.move == true and self.image.y \< 400 then  
  
 self.image:translate(0,self.weight)  
 else  
 print("DONT MOVE")  
 self.move = false  
 Runtime:removeEventListener("enterFrame", self)  
 --dumpOrbs()  
 end  
 end  
end  

Deck.lua

function Deck.addOrbs()  
 for i = #orbGroup+1,#orbGroup+8 do  
 local orb = orbs.new(xPos, yPos)  
 orbGroup[#orbGroup+1] = orb  
 local dropTheOrbs = orbGroup[i]  
 Runtime:addEventListener("enterFrame", wrapParam(orb.drop, orb))  
 --Runtime:addEventListener("enterFrame", dropTheOrbs)  
 xPos = xPos + 40  
 if xPos \> 320 then  
 xPos = 20  
 end  
 end  
  
end  
  
function Deck.collide(orb)  
 print("orb.id\*: "..orb.id)  
 local orb = orb  
 for i=#orbGroup,1,-1 do  
 if orbGroup[i].image.x == orb.image.x and orbGroup[i] ~= orb then  
 if orb.move == true then  
  
 if (orb.image.y \> (orbs[i].image.y - orbGroup[i].image.contentHeight)) then  
 print("COLLISION")  
 orb.move = false  
 else  
 orb.move = true  
 end  
 end  
 else  
 print("NOT THE ORB YOU'RE LOOKING FOR")  
 end  
 end  
end  

The problem is this obviously causes a Runtime error because Orb needs Deck and Deck needs Orb. I realise that I’ve structured this poorly, but I’m struggling to think of how to do this properly.

Many thanks guys… [import]uid: 33275 topic_id: 33208 reply_id: 132061[/import]

This may be a time you have to break out of the OOP mindset. Why not just let deck.lua do all the work. It knows your list of orbs, so just loop over the list and see if any of them are touching…
[import]uid: 19626 topic_id: 33208 reply_id: 132074[/import]

Cheers Rob - I think that’s where I’m struggling, my determination to keep everything as strictly OOP as possible seems to be causing more problems then it’s solving :slight_smile: [import]uid: 33275 topic_id: 33208 reply_id: 132077[/import]

Hey Ntero - I’m using your wrapParam example - even though I’m not 100% on it yet, it works nicely. The only problem I’m facing is that I want to remove that event listener when the Orbs have hit the bottom, and struggling to remove this.

Do you have any ideas how I could achieve this? [import]uid: 33275 topic_id: 33208 reply_id: 132089[/import]

Yeah, that’s one downside to wrapParam, is that it can make it a little tricker to track function references.

You can always attach it to orb:

--in orbs.new()  
orb.dropCmd = wrapParam(orb.drop, orb)  
Runtime:addEventListener("enterFrame", orb.dropCmd)  
  
--remove enterFrame for the orb from Deck.lua  
  
--in Drop()  
Runtime:removeEventListener("enterFrame", self.dropCmd)  
  

Also, if eventListeners are the only people calling drop() you can even replace the function with the wrapParam version (though this adds a sort of hidden requirement to some functions which can be bad)

With the first question, similar to what rob is saying for the original point, you could have Deck.lua have the enterFrame listener, and have it A) iterate over all Orbs to move them, then B) check each orb against the other ones for collisions after all movement is completed.

This solves 2 issues, 1) Your dependency issues, the Deck can take full control of managing the orbs, and so the Orbs no longer need to know about the deck and B) You can avoid checking collisions against orbs that have yet to move, which avoids what should not have been a collision, and helps reduce processing, also you can guarantee if you checked A colliding with B, you won’t check B colliding with A on the same frame (a waste since you already know the result)

function Deck.onFrame()  
 for i = 1, #orbGroup do  
 local orb = orbGroup[i]  
 if orb.move then --This if could be replaced with an orb:update() -like call so the orb can manage it's own state better  
 orb:drop()  
 end  
 end  
  
 for i = 1, #orbGroup do  
 for j = i + 1, #orbGroup do --Looping like this makes sure you compare each pairing once and only once  
 --Do Collision check of orbGroup[i] vs orbGroup[j] here and process accordingly  
 end  
 end  
end  
  
--Do this once for the lifetime of the deck object  
Runtime:addEventListener("enterFrame", Deck.onFrame)  

Also, you can be lazy and simply do the require in place to avoid the circular dependency, i.e. in drop() replace deck.collide( with require(“deck”).collide(, and the issue will go away. This has to do with how Lua/Require work, and the fact that a require loads a file once, but caches the returned results, so simply requiring outside the file scope, and putting it in a function called afterwards fixes circular dependencies. [import]uid: 134101 topic_id: 33208 reply_id: 132094[/import]

I went with the lazy option and required deck in drop() - but I’ll probably look at that later. I figure it’s probably best to get things working and then worry about the implementation afterwards, I can only stand pen-and-paper theory for so long :slight_smile:

Thanks for the tip about storing the function on the object - for some bizarre reason I completely overlooked that simple, yet elegant solution :slight_smile:

Cheers… [import]uid: 33275 topic_id: 33208 reply_id: 132097[/import]