Physics not quite resetting

Although… that still doesn’t explain why it works flawlessly the first time that you play the game, and only becomes a problem from the second run-through onwards… that’s a bit of a head-scratcher

AFAIK, if you create the physics objects in setup, box2D won’t update any of them until setup is done, and I think it updates just before draw runs. So you won’t get collisions starting in the middle of a class init function.

I still think the best way to solve it is to cut the code down to the absolute minimum that creates the problem, because this isolates potential problem areas (and lets the rest of us help you).

@Ignatz you’re right, I’ve been working on a minimal working example, and learned a lot! The solution, I think, is not to destroy all the bodies immediately before you create them all again, to leave a cycle or two inbetween destruction and recreation. The code below is very stable. But if you comment out the destruction loop in GameOver:init(), and uncomment the later destruction loop in GameOver:touched(), it becomes very unstable, crashing the whole of Codea. But I’d be very interested if other people can recreate this instability by moving the destruction loop to different parts of the code.

--# Main
-- Collision Manager MWE, game reset stress test

displayMode(OVERLAY)

function setup()
    font("DINAlternate-Bold")
    fill(31, 31, 95, 255)
    fontSize(30)
    textMode(CENTER)
    textAlign(CENTER)
    centre=vec2(WIDTH*0.5,HEIGHT*0.5)
    game=Game(math.random(30,100))
    delay=1 --number of frames to wait before calling collision routine. if you set this to 0, ie no delay at all, generally the programme is stable, and only falls over if you tap really fast (and it is unlikely you'd want to restart that quickly)
    print ("test stability by tapping the screen to reset the game at increasing speed")
end

function draw()
    sprite("SpaceCute:Background", centre.x, centre.y, WIDTH, HEIGHT)
    scene:draw()
    frameCount = frameCount + 1
end

function collide(contact)
  if frameCount>=delay then scene:collide(contact) end
end

function touched(touch)
   if frameCount>=delay and touch.state==ENDED then scene:touched(touch) end
end

--# Game
Game = class()

mask={
wall=1,
ball=2}

function Game:init(number)
    print (number.." bodies")
    
    walls={}
    local p={vec2(0,0), vec2(0,HEIGHT), vec2(WIDTH, HEIGHT), vec2(WIDTH, 0)}
    for a=1, 4 do
        if a==4 then b=1 else b=a+1 end
        local w=physics.body(EDGE, p[a], p[b])
        w.categories={mask.wall}
        walls[a]=w 
    end
    
    bodies={}  
    bodiesMesh=mesh()
    bodiesMesh.texture=readImage("Platformer Art:Coin")
    local diff=(WIDTH-50)/number
    for i=1,number do
        Body(i*diff, math.random(100, HEIGHT-100))
    end
    frameCount = 0
    scene=self
end

function Game:draw()
    for i,v in pairs(bodies) do --hash table, therefore pairs
        v:draw()
    end
    bodiesMesh:draw()
end

function Game:touched()
    GameOver()
end

function Game:collide(contact)
    local bodA, bodB, ball = contact.bodyA, contact.bodyB
    if bodA.categories[1]==mask.ball then ball=bodA
    elseif bodB.categories[1]==mask.ball then ball=bodB
    end
    if ball and bodies[ball] then
        bodies[ball]:collide(contact)
    else
        print("ERROR: no self for this body yet") --you'll only see this if you set delay to zero and tap really fast
    end
end

--# GameOver
GameOver = class()

function GameOver:init()
    scene=self
    frameCount = 0
    for i,v in pairs(bodies) do --hash table, therefore pairs
        v.body:destroy()
    end
    for i,v in ipairs(walls) do --sequential array therefore ipairs
        v:destroy()
    end       
end

function GameOver:draw()
    bodiesMesh:draw()
    text("GAME OVER\
Tap anywhere to restart", centre.x, centre.y)
end

function GameOver:collide()
  --  print ("You should only see this if delay is totally turned off")
end

function GameOver:touched(touch)
    --[[ --killing the bodies here seems to create instability. If you restart enough times Codea crashes. It seems to be best to at least have a draw cycle or two in between killing all the bodies and setting up the new ones.
    for i,v in pairs(bodies) do --hash table, therefore pairs
        v.body:destroy()
    end
    for i,v in ipairs(walls) do --sequential array therefore ipairs
        v:destroy()
    end   
      ]]
    bodiesMesh:clear()   
    game=Game(math.random(30,100))
end

--# Body
Body = class()

function Body:init(x,y)
    local d=math.random(50,100)
    local b=physics.body(CIRCLE, d*0.5)
    b.x,b.y=x,y
    b.interpolate=true
    b.bullet=true
    b.restitution=1
    b.categories={mask.ball}
    local c=color(math.random(255), math.random(255), math.random(255))
    local r=bodiesMesh:addRect(x,y,d,d)
    bodiesMesh:setRectColor(r, c)
    self.body=b
    self.rect=r
    self.color=c
    self.diameter=d
    self.seed=math.random(16416)
    bodies[b]=self --key set to part of value
end

function Body:draw()
    bodiesMesh:setRect(self.rect, self.body.x, self.body.y, self.diameter, self.diameter, math.rad(self.body.angle))
end

function Body:collide(contact)
    if contact.state==BEGAN then
        bodiesMesh:setRectColor(self.rect, self.color.r, self.color.g, self.color.b, 100)
        sound(SOUND_BLIT, self.seed, contact.normalImpulse)
    else
        bodiesMesh:setRectColor(self.rect, self.color.r, self.color.g, self.color.b, 255)
    end
end

```

@yojimbo2000 I tried your example. I commented out the destroy in GameOver:init and uncommented the destroy in GameOver:touched and it crashed Codea like you said. Here’s why. Even though you destroyed the bodies in GameOver:touched, you have to exit that function before Codea actually destroyes the bodies. You can display information about a body after it’s been destroyed as long as you haven’t exited the function. But once you exit the function, the information will be nil. I guess Codea eventually crashes because you’re creating bodies before it has a chance to destroy bodies.

@yojimbo2000 Here a small example showing that information is retained after a destroy until the function is exited.


function setup()
    a=physics.body(CIRCLE,20)
    a.x=WIDTH/2
    a.y=HEIGHT/2
    a.type=STATIC
    count=0
end

function draw()
    background(40, 40, 50)
    fill(255)
    text("tap screen to destroy circle",WIDTH/2,HEIGHT/2-100)
    if count==0 then
        ellipse(a.x,a.y,40)
    end
    if count>0 and count<2 then
        count=count+1
        print("in draw function after touched")
        print("x =",a.x,"y =",a.y)
    end
end

function touched(t)
    if t.state==BEGAN then
        print("START of touched")
        print("before destroy \
x =",a.x,"y =",a.y)
        a:destroy()
        print("DESTROY")
        print("after destroy \
x =",a.x,"y =",a.y)
        count=1
        print("END of touched")
    end
end

@dave1707 Thank you for solving this conundrum, and for the proof, that’s incredibly helpful to know!