Problems counting collisions and displaying data

I am trying to create a simulation of balls dropping down a peg board, like this example:

ttps://www.youtube.com/watch?v=AUSKTk9ENzg
 
I have managed to create the parts of the code necessary to spawn in the pegs and the balls, and the balls successfully bounce down the peg board and are removed when they collide with a counting grid at the bottom.
 
However, in order to extract useful data from this simulation I need to count the number of balls that hit each interval on the grid. I have tried store this data by creating an empty table and setting the collision detection function to count collisions in each interval, but it doesn’t seem to work at the moment.
 
Ultimately, I would like to display this information in a bar chart, so any advice on implementing that would also be appreciated.
 
Relevant parts of the code below, I hope it’s vaguely clear what I’m trying to achieve… Thanks for any help.
 

local screenW, screenH, halfW = display.contentWidth, display.contentHeight, display.contentWidth\*0.5 -- Spacing parameter spacing = 45 -- Create pins based on separation parameter for i = 1,25 do pins = 0 for j = 1,25 do pins =pins+1 if pins \< (i) then if i\>2 then local pin = display.newCircle((halfW-(spacing/2)\*(i)+((j)\*spacing)), (i)\*(spacing/2)+118,spacing\*2/40) physics.addBody( pin, "static", { friction=0.0}) end end end end -- Setting position of counting intervals for i = 1,25 do countxpos[i] = (halfW-(spacing/2)\*(25)+((i)\*spacing))+spacing/2 end local countvals = { [1] = 0, [2] = 0, [3] = 0, [4] = 0, [5] = 0, [6] = 0, [7] = 0, [8] = 0, [9] = 0, [10] = 0, [11] = 0, [12] = 0, [13] = 0, [14] = 0, [15] = 0, [16] = 0, [17] = 0, [18] = 0, [19] = 0, [20] = 0, [21] = 0, [22] = 0, [23] = 0, } -- Function to spawn balls function newBall(x,y,radius) spacing = 45 radius = spacing\*4/20 local ball = display.newCircle(math.random(501,529),150,radius) ball.class = "ball" physics.addBody(ball,"dynamic",{radius=radius,density=5.0, friction=0.0, bounce=0.45 }) ball.gravityScale = 4 -- function to handle the collision on the ball and counting function ball:collision(e) if (e.other.class == "catcher") then for j=1,23 do if ball.x \> countxpos[j] and ball.x \< countxpos[j+1] then countvals[j]= countvals[j]+1 end end timer.performWithDelay(1, function() -- remove the ball display.remove(ball) end, 1) end -- always return true because we have handled the collision return true end -- attach a collision listener to the ball ball:addEventListener("collision",ball) return ball end -- function makeGraph() for i= 1,23 do local graph = display.newRect( countxpos[1], 25\*(spacing/2)+70, spacing, 20\*countvals[i] ) graph:setFillColor( 0.5 ) end end function timer1() newBall() end physics.start() local spawnTable = {} local graphTable = {} for i=1,1 do spawnTable[i] = timer.performWithDelay(1500,timer1,10) graphTable[i] = makeGraph() end for k, v in pairs( countvals ) do print(k, v) end

I’d really appreciate some help on this, have I posted in the wrong section? I know it’s probably a very basic problem…

There really isn’t a question in the original post, so that might stop people from commenting. You state there is a problem, but don’t really go into what the problem is, just what you’re trying to accomplish, which is of course useful, but we don’t know where you are in your debug process. Are you having a problem populating the table with data? Are you getting any data returned at all? Do you need help on calculating the collisions when they happen? Are you getting any collision data back at all? 

Looking at your code, I’d say that, to start, you will want to add a collision phase to the listener, which might ensure you get better feedback. Maybe something like this:

 local ball = display.newCircle(math.random(501,529),150,radius) ball.class = "ball" ball.hasHit = false physics.addBody(ball,"dynamic",{radius=radius,density=5.0, friction=0.0, bounce=0.45 }) ball.gravityScale = 4 -- function to handle the collision on the ball and counting function ball:collision(e) if event.phase == "began" and not ball.hasHit then ball.hasHit = true -- collision code

Thanks for the feedback. 

I fixed that problem, but have found another. The original problem was due to printing the contents of the table (counting bins) directly after the first ball was spawned. I have fixed this by adding a timer to the the function, which now also plots a histogram. However, the timer method is crude, is there any way of initiating the printing/plotting function after the last ball has been removed?

The next problem I’m facing is that the collision appears to be being registered in the wrong place, but only some of the time, and sometimes collisions aren’t registered at all.

My assumption was that this might be because I’m labelling all of my balls as the same object, but this behaviour occurs when only spawning one ball. So now I’m assuming that I’ve misunderstood something fundamental, because I’ve checked over everything I can think over many (many) times.

Current code (sorry for the long post, including a lot in case I’ve missed something outside of the collision functions)

countxpos = {} countxpos[1] = spacing/2-5 for i = 2,24 do --countxpos[i] = (halfW-(spacing/2)\*(25)+((i)\*spacing))+spacing/2 countxpos[i] = countxpos[i-1]+spacing wall = display.newRect(countxpos[i]-spacing/2,25\*(spacing/2)+170,4,75) wall:setFillColor(0) physics.addBody(wall,"static") end --wall = display.newRect(halfW,screenH-2,screenW,4) wall:setFillColor(0) physics.addBody(wall,"static") for i =-11,11 do textLabel = display.newText(i, countxpos[i+12], 25\*(spacing/2)+170, native.systemFont, 22 ) textLabel:setFillColor( 0 ) end countvals={} for i=-11,11 do countvals[i]=0 end local countText = {} function newBall(x,y,radius) spacing = 45 radius = spacing\*47/200 -- radomising initial position of ball within safe limits local ball = display.newCircle(1,1,radius) ball.class = "ball" ball.y = 110 ball:setFillColor(0) if math.random(0,1)\<0.5 then ball.x = math.random(500,511) else ball.x = math.random(518,529) end physics.addBody(ball,"dynamic",{density=5.0, friction=0.0, bounce=0.45 }) ball.gravityScale = 10 function ball:collision(e) if (e.other.class == "catcher") then if e.phase == "began" and not ball.hasHit then ball.hasHit = true print(ball.x) for j=1,24 do if ball.x \> countxpos[j]+1 and ball.x \< countxpos[j+1]-1 then countvals[j-11]= countvals[j-11]+1 print(j) print(countxpos[j]) print(countxpos[j+1]) end end timer.performWithDelay(1, function() -- remove the ball display.remove(ball) end, 1) end end -- always return true because we have handled the collision return true end -- attach a collision listener to the ball ball:addEventListener("collision",ball) return ball end -- Printing and plotting functions function makeGraph() for i= -11,11 do print(countvals[i]) --display.remove(countText) local graph = display.newRect( countxpos[i+12], 25\*(spacing/2)+118, spacing, spacing\*countvals[i]\*20/balls ) graph:setFillColor( 0.3, 0.7,0.5 ) graph.anchorY = 25\*(spacing/2)+118 graph.class = "graph" countText[i] = printCount(i) end end function printCount(i) text = display.newText( countvals[i], countxpos[i+12], 25\*(spacing/2)+145, native.systemFont, 18 ) text:setFillColor( 1, 0, 0 ) text.class = "printText" return text end function timer1() newBall() end physics.start() local spawnTable = {} local graphTable = {} for i=1,1 do balls = 20 ball = timer.performWithDelay(1000,timer1,balls) graphTable[i] = timer.performWithDelay(10000+1000\*balls,makeGraph,1) end

I’m having trouble running your code as your syntax and functions are a bit ramshackle. Could you create a single main.lua that runs the simulation you are describing? That way, I can evaluate and try to provide some additional assistance.

Yeah, I appreciate that my coding is probably terrible. This is my first time with Corona, so I’m still getting to grips with function structures and, well, everything.

I really appreciate you taking the time to look at this.

The config I have just sets width = 768 and height = 1024, and I’m working in landscape.

----------------------------------------------------------------------------------------- -- -- main.lua -- ----------------------------------------------------------------------------------------- -- hide the status bar display.setStatusBar( display.HiddenStatusBar ) -- include the Corona "composer" module local composer = require "composer" -- include Corona's "physics" library local physics = require "physics" physics.start(); physics.pause() local screenW, screenH, halfW = display.contentWidth, display.contentHeight, display.contentWidth\*0.5 -- create a grey rectangle as the backdrop local background = display.newRect( 0, 0, screenW, screenH ) background.anchorX = 0 background.anchorY = 0 background:setFillColor( .99 ) -- Spacing parameter spacing = 45 -- Create pins based on separation parameter for i = 1,25 do pins = 0 for j = 1,25 do pins =pins+1 if pins \< (i) then if i\>2 then local pin = display.newCircle((halfW-(spacing/2)\*(i)+((j)\*spacing)), (i)\*(spacing/2)+118,spacing\*9/200) --pin.x,pin.y = (halfW-(spacing/2)\*(i)+((j)\*spacing)), (i)\*(spacing/2)+118 physics.addBody( pin, "static", { friction=0.0}) pin:setFillColor(0.7) pin.class = "pin" end end end end -- create counting grid and add physics (with custom shape) local catcher = display.newRect(0,0,screenW,75) catcher.anchorX = 0 catcher.anchorY = 1 catcher.x, catcher.y = 0, display.contentHeight catcher:setFillColor( 3,2,0.75) catcher.class = "catcher" physics.addBody(catcher,"static") catcher.isSensor = true countxpos = {} countxpos[1] = spacing/2-5 for i = 2,24 do --countxpos[i] = (halfW-(spacing/2)\*(25)+((i)\*spacing))+spacing/2 countxpos[i] = countxpos[i-1]+spacing wall = display.newRect(countxpos[i]-spacing/2,25\*(spacing/2)+170,4,75) wall:setFillColor(0) physics.addBody(wall,"static") end --wall = display.newRect(halfW,screenH-2,screenW,4) wall:setFillColor(0) physics.addBody(wall,"static") for i =-11,11 do textLabel = display.newText(i, countxpos[i+12], 25\*(spacing/2)+170, native.systemFont, 22 ) textLabel:setFillColor( 0 ) end countvals={} for i=-11,11 do countvals[i]=0 end local countText = {} function newBall(x,y,radius) spacing = 45 radius = spacing\*47/200 -- radomising initial position of ball within safe limits local ball = display.newCircle(1,1,radius) ball.class = "ball" ball.y = 110 ball:setFillColor(0) if math.random(0,1)\<0.5 then ball.x = math.random(500,511) else ball.x = math.random(518,529) end physics.addBody(ball,"dynamic",{density=5.0, friction=0.0, bounce=0.45 }) ball.gravityScale = 10 function ball:collision(e) if (e.other.class == "catcher") then if e.phase == "began" and not ball.hasHit then ball.hasHit = true print(ball.x) for j=1,24 do if ball.x \> countxpos[j]+1 and ball.x \< countxpos[j+1]-1 then countvals[j-11]= countvals[j-11]+1 print(j) print(countxpos[j]) print(countxpos[j+1]) end end timer.performWithDelay(1, function() -- remove the ball display.remove(ball) end, 1) end end -- always return true because we have handled the collision return true end -- attach a collision listener to the ball ball:addEventListener("collision",ball) return ball end -- Printing and plotting functions function makeGraph() for i= -11,11 do print(countvals[i]) --display.remove(countText) local graph = display.newRect( countxpos[i+12], 25\*(spacing/2)+118, spacing, spacing\*countvals[i]\*20/balls ) graph:setFillColor( 0.3, 0.7,0.5 ) graph.anchorY = 25\*(spacing/2)+118 graph.class = "graph" countText[i] = printCount(i) end end function printCount(i) text = display.newText( countvals[i], countxpos[i+12], 25\*(spacing/2)+145, native.systemFont, 18 ) text:setFillColor( 1, 0, 0 ) text.class = "printText" return text end function timer1() newBall() end physics.start() local spawnTable = {} local graphTable = {} for i=1,1 do balls = 20 ball = timer.performWithDelay(1000,timer1,balls) graphTable[i] = timer.performWithDelay(10000+1000\*balls,makeGraph,1) end

@manifolds have you made any progress on this? Let me know if you’re still encountering issues.

I’d really appreciate some help on this, have I posted in the wrong section? I know it’s probably a very basic problem…

There really isn’t a question in the original post, so that might stop people from commenting. You state there is a problem, but don’t really go into what the problem is, just what you’re trying to accomplish, which is of course useful, but we don’t know where you are in your debug process. Are you having a problem populating the table with data? Are you getting any data returned at all? Do you need help on calculating the collisions when they happen? Are you getting any collision data back at all? 

Looking at your code, I’d say that, to start, you will want to add a collision phase to the listener, which might ensure you get better feedback. Maybe something like this:

 local ball = display.newCircle(math.random(501,529),150,radius) ball.class = "ball" ball.hasHit = false physics.addBody(ball,"dynamic",{radius=radius,density=5.0, friction=0.0, bounce=0.45 }) ball.gravityScale = 4 -- function to handle the collision on the ball and counting function ball:collision(e) if event.phase == "began" and not ball.hasHit then ball.hasHit = true -- collision code

Thanks for the feedback. 

I fixed that problem, but have found another. The original problem was due to printing the contents of the table (counting bins) directly after the first ball was spawned. I have fixed this by adding a timer to the the function, which now also plots a histogram. However, the timer method is crude, is there any way of initiating the printing/plotting function after the last ball has been removed?

The next problem I’m facing is that the collision appears to be being registered in the wrong place, but only some of the time, and sometimes collisions aren’t registered at all.

My assumption was that this might be because I’m labelling all of my balls as the same object, but this behaviour occurs when only spawning one ball. So now I’m assuming that I’ve misunderstood something fundamental, because I’ve checked over everything I can think over many (many) times.

Current code (sorry for the long post, including a lot in case I’ve missed something outside of the collision functions)

countxpos = {} countxpos[1] = spacing/2-5 for i = 2,24 do --countxpos[i] = (halfW-(spacing/2)\*(25)+((i)\*spacing))+spacing/2 countxpos[i] = countxpos[i-1]+spacing wall = display.newRect(countxpos[i]-spacing/2,25\*(spacing/2)+170,4,75) wall:setFillColor(0) physics.addBody(wall,"static") end --wall = display.newRect(halfW,screenH-2,screenW,4) wall:setFillColor(0) physics.addBody(wall,"static") for i =-11,11 do textLabel = display.newText(i, countxpos[i+12], 25\*(spacing/2)+170, native.systemFont, 22 ) textLabel:setFillColor( 0 ) end countvals={} for i=-11,11 do countvals[i]=0 end local countText = {} function newBall(x,y,radius) spacing = 45 radius = spacing\*47/200 -- radomising initial position of ball within safe limits local ball = display.newCircle(1,1,radius) ball.class = "ball" ball.y = 110 ball:setFillColor(0) if math.random(0,1)\<0.5 then ball.x = math.random(500,511) else ball.x = math.random(518,529) end physics.addBody(ball,"dynamic",{density=5.0, friction=0.0, bounce=0.45 }) ball.gravityScale = 10 function ball:collision(e) if (e.other.class == "catcher") then if e.phase == "began" and not ball.hasHit then ball.hasHit = true print(ball.x) for j=1,24 do if ball.x \> countxpos[j]+1 and ball.x \< countxpos[j+1]-1 then countvals[j-11]= countvals[j-11]+1 print(j) print(countxpos[j]) print(countxpos[j+1]) end end timer.performWithDelay(1, function() -- remove the ball display.remove(ball) end, 1) end end -- always return true because we have handled the collision return true end -- attach a collision listener to the ball ball:addEventListener("collision",ball) return ball end -- Printing and plotting functions function makeGraph() for i= -11,11 do print(countvals[i]) --display.remove(countText) local graph = display.newRect( countxpos[i+12], 25\*(spacing/2)+118, spacing, spacing\*countvals[i]\*20/balls ) graph:setFillColor( 0.3, 0.7,0.5 ) graph.anchorY = 25\*(spacing/2)+118 graph.class = "graph" countText[i] = printCount(i) end end function printCount(i) text = display.newText( countvals[i], countxpos[i+12], 25\*(spacing/2)+145, native.systemFont, 18 ) text:setFillColor( 1, 0, 0 ) text.class = "printText" return text end function timer1() newBall() end physics.start() local spawnTable = {} local graphTable = {} for i=1,1 do balls = 20 ball = timer.performWithDelay(1000,timer1,balls) graphTable[i] = timer.performWithDelay(10000+1000\*balls,makeGraph,1) end

I’m having trouble running your code as your syntax and functions are a bit ramshackle. Could you create a single main.lua that runs the simulation you are describing? That way, I can evaluate and try to provide some additional assistance.

Yeah, I appreciate that my coding is probably terrible. This is my first time with Corona, so I’m still getting to grips with function structures and, well, everything.

I really appreciate you taking the time to look at this.

The config I have just sets width = 768 and height = 1024, and I’m working in landscape.

----------------------------------------------------------------------------------------- -- -- main.lua -- ----------------------------------------------------------------------------------------- -- hide the status bar display.setStatusBar( display.HiddenStatusBar ) -- include the Corona "composer" module local composer = require "composer" -- include Corona's "physics" library local physics = require "physics" physics.start(); physics.pause() local screenW, screenH, halfW = display.contentWidth, display.contentHeight, display.contentWidth\*0.5 -- create a grey rectangle as the backdrop local background = display.newRect( 0, 0, screenW, screenH ) background.anchorX = 0 background.anchorY = 0 background:setFillColor( .99 ) -- Spacing parameter spacing = 45 -- Create pins based on separation parameter for i = 1,25 do pins = 0 for j = 1,25 do pins =pins+1 if pins \< (i) then if i\>2 then local pin = display.newCircle((halfW-(spacing/2)\*(i)+((j)\*spacing)), (i)\*(spacing/2)+118,spacing\*9/200) --pin.x,pin.y = (halfW-(spacing/2)\*(i)+((j)\*spacing)), (i)\*(spacing/2)+118 physics.addBody( pin, "static", { friction=0.0}) pin:setFillColor(0.7) pin.class = "pin" end end end end -- create counting grid and add physics (with custom shape) local catcher = display.newRect(0,0,screenW,75) catcher.anchorX = 0 catcher.anchorY = 1 catcher.x, catcher.y = 0, display.contentHeight catcher:setFillColor( 3,2,0.75) catcher.class = "catcher" physics.addBody(catcher,"static") catcher.isSensor = true countxpos = {} countxpos[1] = spacing/2-5 for i = 2,24 do --countxpos[i] = (halfW-(spacing/2)\*(25)+((i)\*spacing))+spacing/2 countxpos[i] = countxpos[i-1]+spacing wall = display.newRect(countxpos[i]-spacing/2,25\*(spacing/2)+170,4,75) wall:setFillColor(0) physics.addBody(wall,"static") end --wall = display.newRect(halfW,screenH-2,screenW,4) wall:setFillColor(0) physics.addBody(wall,"static") for i =-11,11 do textLabel = display.newText(i, countxpos[i+12], 25\*(spacing/2)+170, native.systemFont, 22 ) textLabel:setFillColor( 0 ) end countvals={} for i=-11,11 do countvals[i]=0 end local countText = {} function newBall(x,y,radius) spacing = 45 radius = spacing\*47/200 -- radomising initial position of ball within safe limits local ball = display.newCircle(1,1,radius) ball.class = "ball" ball.y = 110 ball:setFillColor(0) if math.random(0,1)\<0.5 then ball.x = math.random(500,511) else ball.x = math.random(518,529) end physics.addBody(ball,"dynamic",{density=5.0, friction=0.0, bounce=0.45 }) ball.gravityScale = 10 function ball:collision(e) if (e.other.class == "catcher") then if e.phase == "began" and not ball.hasHit then ball.hasHit = true print(ball.x) for j=1,24 do if ball.x \> countxpos[j]+1 and ball.x \< countxpos[j+1]-1 then countvals[j-11]= countvals[j-11]+1 print(j) print(countxpos[j]) print(countxpos[j+1]) end end timer.performWithDelay(1, function() -- remove the ball display.remove(ball) end, 1) end end -- always return true because we have handled the collision return true end -- attach a collision listener to the ball ball:addEventListener("collision",ball) return ball end -- Printing and plotting functions function makeGraph() for i= -11,11 do print(countvals[i]) --display.remove(countText) local graph = display.newRect( countxpos[i+12], 25\*(spacing/2)+118, spacing, spacing\*countvals[i]\*20/balls ) graph:setFillColor( 0.3, 0.7,0.5 ) graph.anchorY = 25\*(spacing/2)+118 graph.class = "graph" countText[i] = printCount(i) end end function printCount(i) text = display.newText( countvals[i], countxpos[i+12], 25\*(spacing/2)+145, native.systemFont, 18 ) text:setFillColor( 1, 0, 0 ) text.class = "printText" return text end function timer1() newBall() end physics.start() local spawnTable = {} local graphTable = {} for i=1,1 do balls = 20 ball = timer.performWithDelay(1000,timer1,balls) graphTable[i] = timer.performWithDelay(10000+1000\*balls,makeGraph,1) end

@manifolds have you made any progress on this? Let me know if you’re still encountering issues.