Timer issue within an OOP object after restarting level with composer.

I’m trying out some OOP and its killing me, particularly with timers.

I have a simple class object for a snake. I create the snake and when it is touched, it pauses for 2 seconds and then resumes moving. My timer has an id (self.hurtTimer)and check for self.iAlive. My Destroy activity cancels this timer, and sets isAlive to false. 

local physics = require('physics') local sounds = require('libs.sounds') local \_M = {} local newPuff = require('classes.puff').newPuff function \_M.newSnake(params) local snake = display.newImageRect(params.g, 'images/ammo/normal.png', 64, 64) snake.x, snake.y = params.x, params.y if params.speed \> 0 then physics.addBody(snake, 'dynamic', {density = 1000, friction = 0, bounce = 1, radius = snake.width \* 0.45}) else physics.addBody(snake, 'static', {density = 1000, friction = 0, bounce = 1, radius = snake.width \* 0.45}) end snake.lives = params.lives print(snake.lives) snake:setFillColor(.9,0.1,0.1,.5) snake.isHungry = true snake.isAlive = true function snake:destroy() self.isHungry = false self.isAlive = false if self.hurtTimer then timer.cancel(self.hurtTimer) end self.hurtTimer = nil self:removeSelf() end --Motion if params.speed \> 0 then local direction = math.random(-180,180)/180\*math.pi snake:setLinearVelocity( math.cos(direction) \* params.speed\*50, math.sin(direction) \*params.speed\*50, snake.x, snake.y ) local vx, vy = snake:getLinearVelocity() snake.bearing = math.atan2(vy, vx) end --touch function snake:touch(event) if event.phase == 'began' and self.isHungry then if self.lives \> 1 then self.lives = self.lives - 1 print("Lives Left: " .. self.lives) self.isHungry = false if params.speed \> 0 then self:setLinearVelocity( 0,0, self.x, self.y ) end self.hurtTimer = timer.performWithDelay( 2000, function() if self.isAlive then local direction = math.random(-180,180)/180\*math.pi self:setLinearVelocity( math.cos(direction) \* params.speed\*50, math.sin(direction) \*params.speed\*50, self.x, self.y ) local vx, vy = self:getLinearVelocity() self.bearing = math.atan2(vy, vx) self.isHungry = true end end ) else sounds.play('bug') newPuff({g = params.g, x = self.x, y = self.y}) self:destroy() end end return true end snake:addEventListener('touch') return snake end return \_M

On my game scene, I have some code in the scene:hide function to cleanup the scene when I exit the stage:

for i = 1, #self.snakes do if self.snakes[i] then self.snakes[i]:destroy() table.remove(self.snakes,i) end end

I am basing this project on the cannon game example, and it is using the intermediate scene.  

So according to my logic, destroying the snake should cancel the timer, or at least make sure the timer doesn’t do anything. And yet, when I restart or go to the next level:

snake.lua:67: attempt to call method 'setLinearVelocity' (a nil value)

This happens when I have more than 2+ snakes on the screen, and press on them , then quickly pause and restart the level. 

What am I doing wrong? I think there’s something I’m not doing right in regards to scene changing and clearing objects but I am truly stumped. Could someone please help me, or at least point me to some tutorial / example of how one should be doing this?

What is line 67?

Ask yourself, how can the object that you’re calling setLinearVelocity for be nil at that point? Has something destroyed it, but it could still be called from an enterFrame listener, timer, etc? If it’s in the middle of a touch event or collision event, you can’t kill it without putting it in a brief timer.

“Currently, the Box2D physics engine is liable to crash during a collision if Corona code attempts to modify objects still involved in the collision. This is because Box2D is still working out the iterated mathematics on these objects. However, your collision handler may set a flag or include a time delay via timer.performWithDelay() so that the action can occur in the next application cycle or later.”

Some more details here: https://docs.coronalabs.com/guide/physics/collisionDetection/index.html#global-collision-handling

Cheers.

Thank you for your responses.

I remember reading about this issue with removing object during a physics collision, but didn’t know it also applied to touch events. My snakes are not colliding with anything.

The Line 67 error occurs inside a touch event. I have a performWithDelay function inside the touch event, so I guess when I try to remove the object in the middle of this wait, it likely fails to remove itself and then causes an issue when the level reloads. I will try when I get home… However I’m not fully sure how to avoid this. 

Would the remove with slight delay trick work for me here since the delay inside the touch event is 2 seconds? I am basically trying to have these snakes which run off a physics calculation stop moving for 2 seconds when I touch them, and then resume moving. 

Basically any time you’re in an event handler that is tied to an object and you try to delete the object while that event function is still running, you can run into this issue.

Collisions are probably the most common time this shows up.

Rob

What is line 67?

Ask yourself, how can the object that you’re calling setLinearVelocity for be nil at that point? Has something destroyed it, but it could still be called from an enterFrame listener, timer, etc? If it’s in the middle of a touch event or collision event, you can’t kill it without putting it in a brief timer.

“Currently, the Box2D physics engine is liable to crash during a collision if Corona code attempts to modify objects still involved in the collision. This is because Box2D is still working out the iterated mathematics on these objects. However, your collision handler may set a flag or include a time delay via timer.performWithDelay() so that the action can occur in the next application cycle or later.”

Some more details here: https://docs.coronalabs.com/guide/physics/collisionDetection/index.html#global-collision-handling

Cheers.

Thank you for your responses.

I remember reading about this issue with removing object during a physics collision, but didn’t know it also applied to touch events. My snakes are not colliding with anything.

The Line 67 error occurs inside a touch event. I have a performWithDelay function inside the touch event, so I guess when I try to remove the object in the middle of this wait, it likely fails to remove itself and then causes an issue when the level reloads. I will try when I get home… However I’m not fully sure how to avoid this. 

Would the remove with slight delay trick work for me here since the delay inside the touch event is 2 seconds? I am basically trying to have these snakes which run off a physics calculation stop moving for 2 seconds when I touch them, and then resume moving. 

Basically any time you’re in an event handler that is tied to an object and you try to delete the object while that event function is still running, you can run into this issue.

Collisions are probably the most common time this shows up.

Rob