Error: attempt to compare nil to a number

Howdy!

I have been learning from the documentation recently, I reached the end of making a rocket shooting asteroids in the chapter 3, “Bringing it to Life”. So I have done everything it stated and learnt everything it taught me, thankfully it was really good.

However, after finishing the game, an error occurs every time a laser bullet hits an asteroid claiming that I can’t “compare nil to a number”. My assumption is that when a laser bullet collides with an asteroid, both of the laser and the asteroid get removed from the screen and the value of the asteroid will be nil in the table of the asteroids ( asteroidsTable ), hence it will no longer have a value (nil) which is then incomparable with a true number.

On the other hand, I downloaded the main script of chapter 3 and faced no errors at all. What would the error be caused by?

Here is the code: 

The error is " main.lua:94: attempt to compare nil with number".

----------------------------------------------------------------------------------------- -- -- main.lua -- ----------------------------------------------------------------------------------------- lives = 3; score = 0; died = false; asteroidsTable = {}; ship = nil; gameLoopTimer = nil; livesText = nil; scoreText = nil; backGroup = display.newGroup(); mainGroup = display.newGroup(); uiGroup = display.newGroup(); physics = require("physics"); physics.start(); physics.setGravity(0, 0); math.randomseed(os.time()); function updateText() livesText.text = "Lives: "..lives; scoreText.text = "Score: "..score; end function createAsteroid() newAsteroid = display.newImageRect(mainGroup, objectSheet, 1, 102, 85); table.insert(asteroidsTable, newAsteroid); physics.addBody(newAsteroid, "dynamic", {radius = 40, bounce = 0.8}); newAsteroid.myName = "asteroid"; whereFrom = math.random(3); if whereFrom == 1 then newAsteroid.x = -60; newAsteroid.y = math.random(500); newAsteroid:setLinearVelocity(math.random(40, 120), math.random(20, 60)); elseif whereFrom == 2 then newAsteroid.x = math.random(display.contentWidth); newAsteroid.y = -60; newAsteroid:setLinearVelocity(math.random(-40, 40), math.random(40, 120)); elseif whereFrom == 3 then newAsteroid.x = display.contentWidth+60; newAsteroid.y = math.random(500); newAsteroid:setLinearVelocity(math.random(-120, -40), math.random(20, 60)); end newAsteroid:applyTorque(math.random(-6, 6)); end function fireLaser() local newLaser = display.newImageRect(mainGroup, objectSheet, 5, 14, 40); physics.addBody(newLaser, "dynamic", {isSensor = true}); newLaser.myName = "laser"; newLaser.isBullet = true; newLaser.x = ship.x; newLaser.y = ship.y; newLaser:toBack(); transition.to(newLaser, {y=-40, time=500, onComplete = function() display.remove(newLaser) end}); end function dragShip(event) local ship = event.target; local phase = event.phase; if "began" == phase then display.currentStage:setFocus(ship); ship.touchOffsetX = event.x - ship.x; ship.touchOffsetY = event.y - ship.y; elseif "moved" == phase then ship.x = event.x - ship.touchOffsetX; ship.y = event.y - ship.touchOffsetY; elseif "ended" == phase or "canceled" == phase then display.currentStage:setFocus(nil); end return true; end function gameLoop() createAsteroid(); for i = #asteroidsTable, 1, -1 do local thisAsteroid = asteroidsTable[i]; if thisAsteroid.x \< -100 or thisAsteroid.x \> display.contentWidth + 100 or thisAsteroid.y \< -100 or thisAsteroid.y \> display.contentHeight + 100 then display.remove(thiAsteroid); table.remove(asteroidsTable, i); end end end function restoreShip() ship.isBodyActive = false; ship.x = display.contentCenterX; ship.y = display.contentHeight - (display.contentHeight \* 0.1); transition.to(ship, {alpha=1, time=4000, onComplete = function() ship.isBodyActive = true; died = false; end}); end function onCollision(event) if event.phase == "began" then obj1 = event.object1; obj2 = event.object2; if obj1.myName == "asteroid" and obj2.myName == "laser" or obj1.myName == "laser" and obj2.myName == "asteroid" then display.remove(obj1); display.remove(obj2); for i = #asteroidsTable, -1 do if asteroidsTable[i] == obj1 or asteroidsTable[i] == obj2 then table.remove(asteroidsTable, i); break; end end score = score + 100; scoreText.text = "Score: "..score; elseif obj1.myName == "asteroid" and obj2.myName == "ship" or obj1.myName == "ship" and obj2.myName == "asteroid" then if died == false then died = true; lives = lives - 1; livesText.text = "Lives: "..lives; if lives == 0 then display.remove(ship); else ship.alpha = 0; timer.performWithDelay(1000, restoreShip); end end end end end sheetOptions = { frames = { { x = 0, y = 0, width = 102, height = 85 }, { x = 0, y = 85, width = 90, height = 83 }, { x = 0, y = 168, width = 100, height = 97 }, { x = 0, y = 265, width = 98, height = 79 }, { x = 98, y = 265, width = 14, height = 40 }, } } objectSheet = graphics.newImageSheet("gameObjects.png", sheetOptions); background = display.newImageRect(backGroup, "background.png", 800, 1400); background.x = display.contentCenterX; background.y = display.contentCenterY; ship = display.newImageRect(mainGroup, objectSheet, 4, 98, 79); ship.x = display.contentCenterX; ship.y = display.contentHeight - (display.contentHeight \* 0.1); physics.addBody(ship, {radius = 30, isSensor = true}); ship.myName = "ship"; livesText = display.newText(uiGroup, "Lives: "..lives, 200, 80, native.systemFont, 36); scoreText = display.newText(uiGroup, "Score: "..score, 400, 80, native.systemFont, 36); display.setStatusBar(display.HiddenStatusBar); createAsteroid(); ship:addEventListener("tap", fireLaser); ship:addEventListener("touch", dragShip); gameLoopTimer = timer.performWithDelay(500, gameLoop, 0); Runtime:addEventListener("collision", onCollision);

The error is on this part of the code ? :

if obj1.myName == "asteroid" and obj2.myName == "laser" or obj1.myName == "laser" and obj2.myName == "asteroid" then display.remove(obj1); display.remove(obj2); for i = #asteroidsTable, -1 do if asteroidsTable[i] == obj1 or asteroidsTable[i] == obj2 then table.remove(asteroidsTable, i); break; end end

The iteration over the asteroidsTable is not correct : 

it’s from #asteroidsTable to 1 , with -1 , so :

for i = #asteroidsTable,1, -1 do ...

Or you can just remove by the using the index of the objects instead of browsing the whole table :

local i,j=table.indexOf(obj1),table.indexOf(obj2) if i then table.remove(asteroidsTable, i ) end if j then table.remove(asteroidsTable, j ) end

I thought the  code  tag would append the number of the line on the left. And silly me, how couldn’t I observe such an obvious iteration mistake like that? The game worked fine and gave no error, so far.

By the way, the way you suggested to eliminate an element from a table is really cool and better in performance I assume, though I don’t know if I will use it in my current script or not. Nevertheless, thank you so much for helping me!

The error is on this part of the code ? :

if obj1.myName == "asteroid" and obj2.myName == "laser" or obj1.myName == "laser" and obj2.myName == "asteroid" then display.remove(obj1); display.remove(obj2); for i = #asteroidsTable, -1 do if asteroidsTable[i] == obj1 or asteroidsTable[i] == obj2 then table.remove(asteroidsTable, i); break; end end

The iteration over the asteroidsTable is not correct : 

it’s from #asteroidsTable to 1 , with -1 , so :

for i = #asteroidsTable,1, -1 do ...

Or you can just remove by the using the index of the objects instead of browsing the whole table :

local i,j=table.indexOf(obj1),table.indexOf(obj2) if i then table.remove(asteroidsTable, i ) end if j then table.remove(asteroidsTable, j ) end

I thought the  code  tag would append the number of the line on the left. And silly me, how couldn’t I observe such an obvious iteration mistake like that? The game worked fine and gave no error, so far.

By the way, the way you suggested to eliminate an element from a table is really cool and better in performance I assume, though I don’t know if I will use it in my current script or not. Nevertheless, thank you so much for helping me!