I’ve noticed that some boucing balls pass through Edge objects, even when ball.bullet= true. Is this expected? Looks like a physics bug.
run this and wait after 30s some balls will go through the edges, i made a sound and a print to show the event.
--# EventMngr
-- ############## START of EVENT MANAGER ##################
-- @tnlogy & @JMV38 & @Briarfox
-- example of usage:
-- EventMngr:extend(evMngr) -- extend an existing table with event manager funcs
-- evMngr:on("touch",func) -- register func() to fire on "touch" event
-- evMngr:on("touch", obj.func, obj) -- register obj:func() to fire on "touch" event
-- evMngr:trigger("touch",10,50) -- fires func(10,50) and obj:func(10,50)
-- evMngr:off("touch", func) -- unregister func()
-- evMngr:off("touch", obj.func, obj) -- unregister obj:func()
-- evMngr:off("touch") -- unregister all "touch" listeners
-- evMngr:off(obj.func) -- unregister all listeners with obj.func
-- evMngr:off(obj) -- unregister events with obj listening
-- "all" captures all events and passes the event name as the first param:
-- evMngr:on("all", func)
EventMngr = {}
local fifo = true -- first in (to register) first out (to be triggered)
function EventMngr:on(eventName, fn, obj)
if not self.events then self.events = {} end -- init event table if does not exist
-- if not self.events[eventName] then self.events[eventName] = {} end -- init this event name
if not self.events[eventName] then self.events[eventName] = {} end -- init this event name
local new = true -- confirm it is a new request
for i,fa in ipairs(self.events[eventName]) do
if fa.func == fn and fa.obj == obj then new = false end
end
local p -- insertion point in the table
if new then
if fifo then p = 1 else p = #self.events[eventName] +1 ; fifo=true end
local listener = {func = fn, obj = obj }
table.insert(self.events[eventName], p, listener)
end
return self
end
function EventMngr:executeNextCallBeforeOthers()
fifo = false
end
function EventMngr:off(nameOrFnOrObj, fn, obj)
local name
local fn,obj = fn,obj -- manage the case when they are nil
local firstType = type(nameOrFnOrObj)
local request
if firstType == "string" or firstType == "number" then
name = nameOrFnOrObj
if name == "all" then request = "remove all events"
elseif fn == nil then request = "remove all instances of this event"
else request = "remove this event" end
elseif firstType == "function" then
fn = nameOrFnOrObj
request = "remove all events with this function"
else
obj = nameOrFnOrObj
request = "remove all events with this object"
end
if request == "remove all instances of this event" then
self.events[name] = nil
elseif request == "remove all events" then
self.events = {}
else
local evs = self.events -- go through all events ...
if name then evs = {evs[name]} end -- ... or through 1 event only
for eventName,fns in pairs(evs) do
local n = #fns
for i=0,n-1 do
local j = n-i -- go backward because of remove, ipairs not suitable
local f = fns[j]
local match
if request == "remove this event"
then match=(f.func==fn and f.obj==obj)
elseif request == "remove all events with this function"
then match=(f.func==fn)
elseif request == "remove all events with this object"
then match=(f.obj==obj)
end
if match then
table.remove(fns,j)
end
end
end
end
return self
end
function EventMngr:trigger(name, ...)
self.lastTrigger = name
local evs = (self.events and self.events[name]) or {}
for i = #evs,1,-1 do
local fa = evs[i]
local func,obj = fa.func, fa.obj
if obj then func(obj,...)
else func(...) end
end
--trigger all
local evs = (self.events and self.events["all"]) or {}
for i,fa in ipairs(evs) do
local func,obj = fa.func, fa.obj
if obj then func(obj,name,...)
else func(name,...) end
end
end
-- to transform a table into an event manager
function EventMngr:extend(target)
for k, v in pairs(self) do
if type(v) == "function" and v ~= EventMngr.extend
then target[k] = v
end
end
return target
end
-- ############## END of EVENT MANAGER ##################
--# World
World = class()
EventMngr:extend(World)
function World:init()
end
function World:draw()
self:trigger("draw")
end
function World:touched(touch)
self:trigger("touched", touch)
end
--# Edge
Edge = class()
EventMngr:extend(Edge)
function Edge:init(data)
if data.x then
local x = data.x
self.pos0 = vec2(x,0)
self.pos1 = vec2(x,HEIGHT)
elseif data.y then
local y = data.y
self.pos0 = vec2(0, y)
self.pos1 = vec2(WIDTH, y)
else
error("please define x or y")
end
self.body = physics.body(EDGE, self.pos0, self.pos1)
self.body.info = self
-- events
World:on("draw",self.draw,self)
World:executeNextCallBeforeOthers()
World:on("touched",self.touched,self)
end
function Edge:draw()
fill(223, 223, 223, 255)
line(self.pos0.x, self.pos0.y, self.pos1.x, self.pos1.y)
end
function Edge:touched(touch)
end
--# Ball
Ball = class()
function Ball:init(x,y,r)
self.x = x
self.y = y
self.r = r
self:updateColor()
self.body = physics.body(CIRCLE, self.r)
self.body.x = self.x
self.body.y = self.y
self.body.sleepingAllowed = false
self.body.gravityScale = 1
self.body.linearVelocity = vec2(400,200)
self.body.linearDamping = 0
self.body.angularDamping = 0
self.body.friction = 0
self.body.restitution = 1
self.body.info = self
self.body.bullet = true
-- events
World:on("draw",self.draw,self)
self.outside = false
end
function Ball:delete()
self.body:destroy()
World:off(self)
end
function Ball:draw()
local pos = self.body.position
self.x, self.y = pos.x, pos.y
fill(self.color)
ellipse(self.x,self.y,self.r*2)
if self.outside==false and (self.x<0 or self.y<0 or self.x>WIDTH or self.y>HEIGHT) then
self.outside=true
print("out!")
sound("Game Sounds One:Zapper 1")
end
end
local rand = math.random
function Ball:updateColor()
-- lets change color
local r,g = rand(255),rand(255)
local b = 255 - g
self.color = color(r,g,b)
end
local abs = math.abs
function Ball:touched(t)
if abs(self.x-t.x)<self.r and abs(self.x-t.x)<self.r then
if t.state == BEGAN then
end
end
end
--# MotherBall
MotherBall = class(Ball)
function MotherBall:init(x,y,r)
Ball.init(self, x,y,r)
self.body.gravityScale = 0
World:on("draw",self.drawText,self)
end
function MotherBall:drawText()
fill(0)
fontSize(30)
local t = World.events["delete"]
if t then text( tostring(#t), self.x,self.y) end
end
function MotherBall:babyBall()
local baby = Ball(self.x,self.y,self.r/3)
World:on("delete",baby.delete,baby)
end
function MotherBall:collide(c)
-- lets change color
self:updateColor()
-- if i touch a wall, make a baby ball
if c.bodyA.info:is_a(Edge) or c.bodyB.info:is_a(Edge) then self:babyBall() end
end
--# Main
-- eventsExample
-- Use this function to perform your initial setup
function setup()
e1 = Edge({x=0})
e2 = Edge({x=WIDTH})
e3 = Edge({y=0})
e4 = Edge({y=HEIGHT})
b = MotherBall(WIDTH/2, HEIGHT/2, 50)
parameter.action("delete",function() World:trigger("delete") collectgarbage() end )
end
-- This function gets called once every frame
function draw()
background(40, 40, 50)
strokeWidth(1.5)
noSmooth()
World:draw()
end
function touched(t)
World:touched(t)
end
function collide(c)
if c.state == BEGAN then
fA = c.bodyA.info.collide
if fA then fA(c.bodyA.info, c) end
fB = c.bodyB.info.collide
if fB then fB(c.bodyB.info, c) end
end
end
here is a new version: i’ve put a ghost red ball where it disappears. There doesnt seem to be a special location.
--# EventMngr
-- ############## START of EVENT MANAGER ##################
-- @tnlogy & @JMV38 & @Briarfox
-- example of usage:
-- EventMngr:extend(evMngr) -- extend an existing table with event manager funcs
-- evMngr:on("touch",func) -- register func() to fire on "touch" event
-- evMngr:on("touch", obj.func, obj) -- register obj:func() to fire on "touch" event
-- evMngr:trigger("touch",10,50) -- fires func(10,50) and obj:func(10,50)
-- evMngr:off("touch", func) -- unregister func()
-- evMngr:off("touch", obj.func, obj) -- unregister obj:func()
-- evMngr:off("touch") -- unregister all "touch" listeners
-- evMngr:off(obj.func) -- unregister all listeners with obj.func
-- evMngr:off(obj) -- unregister events with obj listening
-- "all" captures all events and passes the event name as the first param:
-- evMngr:on("all", func)
EventMngr = {}
local fifo = true -- first in (to register) first out (to be triggered)
function EventMngr:on(eventName, fn, obj)
if not self.events then self.events = {} end -- init event table if does not exist
-- if not self.events[eventName] then self.events[eventName] = {} end -- init this event name
if not self.events[eventName] then self.events[eventName] = {} end -- init this event name
local new = true -- confirm it is a new request
for i,fa in ipairs(self.events[eventName]) do
if fa.func == fn and fa.obj == obj then new = false end
end
local p -- insertion point in the table
if new then
if fifo then p = 1 else p = #self.events[eventName] +1 ; fifo=true end
local listener = {func = fn, obj = obj }
table.insert(self.events[eventName], p, listener)
end
return self
end
function EventMngr:executeNextCallBeforeOthers()
fifo = false
end
function EventMngr:off(nameOrFnOrObj, fn, obj)
local name
local fn,obj = fn,obj -- manage the case when they are nil
local firstType = type(nameOrFnOrObj)
local request
if firstType == "string" or firstType == "number" then
name = nameOrFnOrObj
if name == "all" then request = "remove all events"
elseif fn == nil then request = "remove all instances of this event"
else request = "remove this event" end
elseif firstType == "function" then
fn = nameOrFnOrObj
request = "remove all events with this function"
else
obj = nameOrFnOrObj
request = "remove all events with this object"
end
if request == "remove all instances of this event" then
self.events[name] = nil
elseif request == "remove all events" then
self.events = {}
else
local evs = self.events -- go through all events ...
if name then evs = {evs[name]} end -- ... or through 1 event only
for eventName,fns in pairs(evs) do
local n = #fns
for i=0,n-1 do
local j = n-i -- go backward because of remove, ipairs not suitable
local f = fns[j]
local match
if request == "remove this event"
then match=(f.func==fn and f.obj==obj)
elseif request == "remove all events with this function"
then match=(f.func==fn)
elseif request == "remove all events with this object"
then match=(f.obj==obj)
end
if match then
table.remove(fns,j)
end
end
end
end
return self
end
function EventMngr:trigger(name, ...)
self.lastTrigger = name
local evs = (self.events and self.events[name]) or {}
for i = #evs,1,-1 do
local fa = evs[i]
local func,obj = fa.func, fa.obj
if obj then func(obj,...)
else func(...) end
end
--trigger all
local evs = (self.events and self.events["all"]) or {}
for i,fa in ipairs(evs) do
local func,obj = fa.func, fa.obj
if obj then func(obj,name,...)
else func(name,...) end
end
end
-- to transform a table into an event manager
function EventMngr:extend(target)
for k, v in pairs(self) do
if type(v) == "function" and v ~= EventMngr.extend
then target[k] = v
end
end
return target
end
-- ############## END of EVENT MANAGER ##################
--# World
World = class()
EventMngr:extend(World)
function World:init()
end
function World:draw()
self:trigger("draw")
end
function World:touched(touch)
self:trigger("touched", touch)
end
--# Edge
Edge = class()
EventMngr:extend(Edge)
function Edge:init(data)
if data.x then
local x = data.x
self.pos0 = vec2(x,0)
self.pos1 = vec2(x,HEIGHT)
elseif data.y then
local y = data.y
self.pos0 = vec2(0, y)
self.pos1 = vec2(WIDTH, y)
else
error("please define x or y")
end
self.body = physics.body(EDGE, self.pos0, self.pos1)
self.body.info = self
-- events
World:on("draw",self.draw,self)
World:executeNextCallBeforeOthers()
World:on("touched",self.touched,self)
end
function Edge:draw()
fill(223, 223, 223, 255)
line(self.pos0.x, self.pos0.y, self.pos1.x, self.pos1.y)
end
function Edge:touched(touch)
end
--# Ball
Ball = class()
function Ball:init(x,y,r)
self.x = x
self.y = y
self.r = r
self:updateColor()
self.body = physics.body(CIRCLE, self.r)
self.body.x = self.x
self.body.y = self.y
self.body.sleepingAllowed = false
self.body.gravityScale = 1
self.body.linearVelocity = vec2(400,200)
self.body.linearDamping = 0
self.body.angularDamping = 0
self.body.friction = 0
self.body.restitution = 1
self.body.info = self
self.body.bullet = true
-- events
World:on("draw",self.draw,self)
self.outside = false
end
function Ball:delete()
self.body:destroy()
World:off(self)
end
function Ball:draw()
local pos = self.body.position
local x0,y0 = self.x, self.y
if self.outside==false and (pos.x<0 or pos.y<0 or pos.x>WIDTH or pos.y>HEIGHT) then
self.outside=true
print("out!")
sound("Game Sounds One:Zapper 1")
self.color = color(255, 0, 0, 255)
end
if self.outside==false then
self.x, self.y = pos.x, pos.y
end
fill(self.color)
ellipse(self.x,self.y,self.r*2)
end
local rand = math.random
function Ball:updateColor()
-- lets change color
local r,g = rand(255),rand(255)
local b = 255 - g
self.color = color(r,g,b)
end
local abs = math.abs
function Ball:touched(t)
if abs(self.x-t.x)<self.r and abs(self.x-t.x)<self.r then
if t.state == BEGAN then
end
end
end
--# MotherBall
MotherBall = class(Ball)
function MotherBall:init(x,y,r)
Ball.init(self, x,y,r)
self.body.gravityScale = 0
World:on("draw",self.drawText,self)
end
function MotherBall:drawText()
fill(0)
fontSize(30)
local t = World.events["delete"]
if t then text( tostring(#t), self.x,self.y) end
end
function MotherBall:babyBall()
local baby = Ball(self.x,self.y,self.r/3)
World:on("delete",baby.delete,baby)
end
function MotherBall:collide(c)
-- lets change color
self:updateColor()
-- if i touch a wall, make a baby ball
if c.bodyA.info:is_a(Edge) or c.bodyB.info:is_a(Edge) then self:babyBall() end
end
--# Main
-- eventsExample
-- Use this function to perform your initial setup
function setup()
e1 = Edge({x=0})
e2 = Edge({x=WIDTH})
e3 = Edge({y=0})
e4 = Edge({y=HEIGHT})
b = MotherBall(WIDTH/2, HEIGHT/2, 50)
parameter.action("delete",function() World:trigger("delete") collectgarbage() end )
end
-- This function gets called once every frame
function draw()
background(40, 40, 50)
strokeWidth(1.5)
noSmooth()
World:draw()
end
function touched(t)
World:touched(t)
end
function collide(c)
if c.state == BEGAN then
fA = c.bodyA.info.collide
if fA then fA(c.bodyA.info, c) end
fB = c.bodyB.info.collide
if fB then fB(c.bodyB.info, c) end
end
end
very strange!
i’ve added physics.contnuous=true, but
1/ some balls still bo through.
2/ they go through further from the wall than before!!! ???
This seems to be okay:
--# EventMngr
-- ############## START of EVENT MANAGER ##################
-- @tnlogy & @JMV38 & @Briarfox
-- example of usage:
-- EventMngr:extend(evMngr) -- extend an existing table with event manager funcs
-- evMngr:on("touch",func) -- register func() to fire on "touch" event
-- evMngr:on("touch", obj.func, obj) -- register obj:func() to fire on "touch" event
-- evMngr:trigger("touch",10,50) -- fires func(10,50) and obj:func(10,50)
-- evMngr:off("touch", func) -- unregister func()
-- evMngr:off("touch", obj.func, obj) -- unregister obj:func()
-- evMngr:off("touch") -- unregister all "touch" listeners
-- evMngr:off(obj.func) -- unregister all listeners with obj.func
-- evMngr:off(obj) -- unregister events with obj listening
-- "all" captures all events and passes the event name as the first param:
-- evMngr:on("all", func)
EventMngr = {}
local fifo = true -- first in (to register) first out (to be triggered)
function EventMngr:on(eventName, fn, obj)
if not self.events then self.events = {} end -- init event table if does not exist
-- if not self.events[eventName] then self.events[eventName] = {} end -- init this event name
if not self.events[eventName] then self.events[eventName] = {} end -- init this event name
local new = true -- confirm it is a new request
for i,fa in ipairs(self.events[eventName]) do
if fa.func == fn and fa.obj == obj then new = false end
end
local p -- insertion point in the table
if new then
if fifo then p = 1 else p = #self.events[eventName] +1 ; fifo=true end
local listener = {func = fn, obj = obj }
table.insert(self.events[eventName], p, listener)
end
return self
end
function EventMngr:executeNextCallBeforeOthers()
fifo = false
end
function EventMngr:off(nameOrFnOrObj, fn, obj)
local name
local fn,obj = fn,obj -- manage the case when they are nil
local firstType = type(nameOrFnOrObj)
local request
if firstType == "string" or firstType == "number" then
name = nameOrFnOrObj
if name == "all" then request = "remove all events"
elseif fn == nil then request = "remove all instances of this event"
else request = "remove this event" end
elseif firstType == "function" then
fn = nameOrFnOrObj
request = "remove all events with this function"
else
obj = nameOrFnOrObj
request = "remove all events with this object"
end
if request == "remove all instances of this event" then
self.events[name] = nil
elseif request == "remove all events" then
self.events = {}
else
local evs = self.events -- go through all events ...
if name then evs = {evs[name]} end -- ... or through 1 event only
for eventName,fns in pairs(evs) do
local n = #fns
for i=0,n-1 do
local j = n-i -- go backward because of remove, ipairs not suitable
local f = fns[j]
local match
if request == "remove this event"
then match=(f.func==fn and f.obj==obj)
elseif request == "remove all events with this function"
then match=(f.func==fn)
elseif request == "remove all events with this object"
then match=(f.obj==obj)
end
if match then
table.remove(fns,j)
end
end
end
end
return self
end
function EventMngr:trigger(name, ...)
self.lastTrigger = name
local evs = (self.events and self.events[name]) or {}
for i = #evs,1,-1 do
local fa = evs[i]
local func,obj = fa.func, fa.obj
if obj then func(obj,...)
else func(...) end
end
--trigger all
local evs = (self.events and self.events["all"]) or {}
for i,fa in ipairs(evs) do
local func,obj = fa.func, fa.obj
if obj then func(obj,name,...)
else func(name,...) end
end
end
-- to transform a table into an event manager
function EventMngr:extend(target)
for k, v in pairs(self) do
if type(v) == "function" and v ~= EventMngr.extend
then target[k] = v
end
end
return target
end
-- ############## END of EVENT MANAGER ##################
--# World
World = class()
EventMngr:extend(World)
function World:init()
end
function World:draw()
self:trigger("draw")
end
function World:touched(touch)
self:trigger("touched", touch)
end
--# Edge
Edge = class()
EventMngr:extend(Edge)
function Edge:init(data)
if data.x then
local x = data.x
self.pos0 = vec2(x,0)
self.pos1 = vec2(x,HEIGHT)
elseif data.y then
local y = data.y
self.pos0 = vec2(0, y)
self.pos1 = vec2(WIDTH, y)
else
error("please define x or y")
end
self.body = physics.body(EDGE, self.pos0, self.pos1)
self.body.info = self
-- events
World:on("draw",self.draw,self)
World:executeNextCallBeforeOthers()
World:on("touched",self.touched,self)
end
function Edge:draw()
fill(223, 223, 223, 255)
line(self.pos0.x, self.pos0.y, self.pos1.x, self.pos1.y)
end
function Edge:touched(touch)
end
--# Ball
Ball = class()
function Ball:init(x,y,r)
self.x = x
self.y = y
self.r = r
self:updateColor()
self.body = physics.body(CIRCLE, self.r)
self.body.x = self.x
self.body.y = self.y
self.body.sleepingAllowed = false
self.body.gravityScale = 1
self.body.linearVelocity = vec2(400,200)
self.body.linearDamping = 0
self.body.angularDamping = 0
self.body.friction = 0
self.body.restitution = 1
self.body.info = self
self.body.bullet = true
-- events
World:on("draw",self.draw,self)
self.outside = false
end
function Ball:delete()
self.body:destroy()
World:off(self)
end
function Ball:draw()
local pos = self.body.position
local x0,y0 = self.x, self.y
if self.outside==false and (pos.x<0 or pos.y<0 or pos.x>WIDTH or pos.y>HEIGHT) then
self.outside=true
print("out!")
sound("Game Sounds One:Zapper 1")
self.color = color(255, 0, 0, 255)
end
if self.outside==false then
self.x, self.y = pos.x, pos.y
end
fill(self.color)
ellipse(self.x,self.y,self.r*2)
end
local rand = math.random
function Ball:updateColor()
-- lets change color
local r,g = rand(255),rand(255)
local b = 255 - g
self.color = color(r,g,b)
end
local abs = math.abs
function Ball:touched(t)
if abs(self.x-t.x)<self.r and abs(self.x-t.x)<self.r then
if t.state == BEGAN then
end
end
end
--# MotherBall
MotherBall = class(Ball)
function MotherBall:init(x,y,r)
Ball.init(self, x,y,r)
self.body.gravityScale = 0
World:on("draw",self.drawText,self)
end
function MotherBall:drawText()
fill(0)
fontSize(30)
local t = World.events["delete"]
if t then text( tostring(#t), self.x,self.y) end
end
function MotherBall:babyBall(norm)
local baby = Ball(self.x+norm.x*50,self.y+norm.y*50,self.r/3)
baby.body.linearVelocity = (norm*300)
World:on("delete",baby.delete,baby)
end
function MotherBall:collide(c)
-- lets change color
self:updateColor()
-- if i touch a wall, make a baby ball
if c.bodyA.info:is_a(Edge) then print(c.id,c.state) self:babyBall(c.normal) end
end
--# Main
-- eventsExample
-- Use this function to perform your initial setup
function setup()
e1 = Edge({x=0})
e2 = Edge({x=WIDTH})
e3 = Edge({y=0})
e4 = Edge({y=HEIGHT})
b = MotherBall(WIDTH/2, HEIGHT/2, 50)
parameter.action("delete",function() World:trigger("delete") collectgarbage() end )
physics.iterations(10,10)
physics.pixelToMeterRatio = 32
physics.continuous = true
end
-- This function gets called once every frame
function draw()
background(40, 40, 50)
strokeWidth(1.5)
noSmooth()
World:draw()
end
function touched(t)
World:touched(t)
end
function collide(c)
if c.state == BEGAN then
fA = c.bodyA.info.collide
if fA then fA(c.bodyA.info, c) end
fB = c.bodyB.info.collide
if fB then fB(c.bodyB.info, c) end
end
end
The problem as it seems is if you create a ball inside the mother ball at the centre then the physics engine has to pick a direction to leave, if that direction is towards the wall then the ball will pass right through. It does this because it is ejected from the mother ball in one iteration, meaning it pretty much just sets the balls position to outside the mother ball without a collision check. I’ve added a passing of the collision normal to the baby ball function to set the direction to the normal of a the collision, removing the above problem.
@Luatee I don’t think that’s the whole problem. I added code to assign a number to each ball as its created. I then printed the number of the ball that went out of bounds and the number of the last created ball. The numbers weren’t the same.
@Jmv38 I noticed that the linearVelocity of the balls going out of bounds fast. They were either above 1000 or below -1000. Those speeds aren’t that high, so something else is going on.
@Dave1707 i agree, the balls passing through are fast ones.
@luatee it is not a problem at ball creation (although there may be a problem there too), but long after the ball has been created.
@luatee i’ve tried your code and you are right, there is no bug with it. Is the ball creation the only thing you have changed? Because the desappearing balls are not just created ones… Then it must be a ‘second order’ bug generated by the initial bug of throwing some balls out of the bounds…
Nice finding, anyway. ^:)^
Here’s something I was playing with. 3840 is the max linearVelocity no matter how high the applyForce is set to. None of the balls go through the edge when the screen is tapped. The linearVelocity is a lot higher here than in the above code when a ball goes through an edge. Comment out the line physics.continuous=true just to see what it does. So something else is happening in the above code.
displayMode(FULLSCREEN)
supportedOrientations(LANDSCAPE_ANY)
function setup()
physics.continuous=true
max=0
tab={}
e=physics.body(EDGE,vec2(WIDTH-100,0),vec2(WIDTH-100,HEIGHT))
for z=1,70 do
a=physics.body(CIRCLE,5)
a.x=z*4
a.y=z*10
a.gravityScale=0
table.insert(tab,a)
end
end
function draw()
background(40, 40, 50)
fill(255)
stroke(255)
strokeWidth(2)
line(WIDTH-100,0,WIDTH-100,HEIGHT)
for a,b in pairs(tab) do
ellipse(b.x,b.y,10)
end
if tab[1].linearVelocity.x>max then
max=tab[1].linearVelocity.x
end
text("max linear velocity "..max,WIDTH/2,HEIGHT-100)
text("tap screen to apply force",WIDTH/2,HEIGHT-150)
end
function touched(t)
if t.state==BEGAN then
for a,b in pairs(tab) do
b:applyForce(vec2(8000,0))
end
end
end
@Jmv38 When I watch what’s going on, I see balls going out of bounds even when the large ball is in the middle of the screen an hasn’t created a new ball for awhile.
@Dave1707 right. But with luatee code, no ball EVER goes out of bounds. So this bug is linked in some strange way with throwing balls out of bounds much earlier… Maybe they anihilate with in bounds balls? Like anti-mater?
Maybe we’ve just discovered the ‘Digital Tunnel Effect’? Maybe we get the Nobel prize for that? Or, more likely, the Ig-Nobel prize.
@Jmv38 I made another change to your code. After 20 balls are created, I don’t create anymore, but balls still go out of bounds after that.
I’ve been up to 270 balls with luatee version. No out of bounds. But Codea freezes…
@Jmv38 That may be the solution, but I’m still curious why balls go out of bounds even when I don’t create any new ones. Their linearVelocity doesn’t seem high enough for them to go through a wall.
@Jmv38 I don’t know why I didn’t think of this sooner, but I did a video recording of your running program and stopped it after I saw an out of bounds ball. I played the video back and used the drag bar to run the video backward and forward so I could see what happened when a ball went out of bounds. It was the result of a collision with a fast ball that just collided with another ball. Maybe I’ll try some more videos.
nice detective work!