Removing display objects from a table

The code below illustrates my problem. I can spawn a moving bullet in the centre of the screen with a tap. When it goes outside the screen, it gets removed. This works fine until I fire off a couple of bullets in quick succession. Then I get an error that reads:    ‘main.lua:26: attempt to index field ‘?’ (a nil value)’.

Any enlightenment would be appreciated.

BULLETSPEED = 15 local bullets = {} local function addBullet(event) --spawns circle at screen centre, travelling in direction of tap, adds it to "bullets" table local index = #bullets + 1 bullets[index] = display.newCircle(display.contentCenterX, display.contentCenterY, 10) local dY = event.y - display.contentCenterY local dX = event.x - display.contentCenterX local bulletAngle = math.atan2(dY, dX) bullets[index].velocity = {math.cos(bulletAngle) \* BULLETSPEED, math.sin(bulletAngle) \* BULLETSPEED} end local function frameUpdate(event) -- destroy out-of-bounds bullets for i = #bullets, 1, -1 do if bullets[i].x \< 0 or bullets[i].y \< 0 or bullets[i].y \> display.contentHeight or bullets[i].x \> display.contentWidth then bullets[i]:removeSelf() bullets[i] = nil end end -- move bullets for i = #bullets, 1, -1 do bullets[i].x = bullets[i].x + bullets[i].velocity[1] -- line 26 bullets[i].y = bullets[i].y + bullets[i].velocity[2] end end Runtime:addEventListener("tap", addBullet) Runtime:addEventListener("enterFrame", frameUpdate)

The problem here is that you are setting an entry in the table to nil:

for i = #bullets, 1, -1 do if bullets[i].x \< 0 or bullets[i].y \< 0 or bullets[i].y \> display.contentHeight or bullets[i].x \> display.contentWidth then bullets[i]:removeSelf() bullets[i] = nil --setting index i to nil here end end

 and then trying to access that table entry afterward:

for i = #bullets, 1, -1 do --buttons[i] (where i == the value removed above) no longer exists, so there is no bullets[i].x value to perform arithmetic on bullets[i].x = bullets[i].x + bullets[i].velocity[1] -- line 26 bullets[i].y = bullets[i].y + bullets[i].velocity[2] end

The same principle is easier to demonstrate when the data in the table is not a display object e.g:

local myTable = {1, 2, 3, 4, 5} myTable[3] = nil -- myTable is now {1, 2, nil, 4, 5}

What you need to do is remove the entry entirely from the table, and not just set it to nil:

for i = #bullets, 1, -1 do if bullets[i].x \< 0 or bullets[i].y \< 0 or bullets[i].y \> display.contentHeight or bullets[i].x \> display.contentWidth then bullets[i]:removeSelf() table.remove(bullets, i) --remove entry from table entirely end end

This way, when the next loop runs it will be looping over a table which has all valid entries. This function also moves any entries with a higher index down to close the “hole” left be removing an entry, so your bullet creation function should be unaffected by the change.

Nicely explained, thanks! 

The problem here is that you are setting an entry in the table to nil:

for i = #bullets, 1, -1 do if bullets[i].x \< 0 or bullets[i].y \< 0 or bullets[i].y \> display.contentHeight or bullets[i].x \> display.contentWidth then bullets[i]:removeSelf() bullets[i] = nil --setting index i to nil here end end

 and then trying to access that table entry afterward:

for i = #bullets, 1, -1 do --buttons[i] (where i == the value removed above) no longer exists, so there is no bullets[i].x value to perform arithmetic on bullets[i].x = bullets[i].x + bullets[i].velocity[1] -- line 26 bullets[i].y = bullets[i].y + bullets[i].velocity[2] end

The same principle is easier to demonstrate when the data in the table is not a display object e.g:

local myTable = {1, 2, 3, 4, 5} myTable[3] = nil -- myTable is now {1, 2, nil, 4, 5}

What you need to do is remove the entry entirely from the table, and not just set it to nil:

for i = #bullets, 1, -1 do if bullets[i].x \< 0 or bullets[i].y \< 0 or bullets[i].y \> display.contentHeight or bullets[i].x \> display.contentWidth then bullets[i]:removeSelf() table.remove(bullets, i) --remove entry from table entirely end end

This way, when the next loop runs it will be looping over a table which has all valid entries. This function also moves any entries with a higher index down to close the “hole” left be removing an entry, so your bullet creation function should be unaffected by the change.

Nicely explained, thanks!