Page 1 of 1

Table.remove removing things twice

Posted: Sun Apr 17, 2016 8:55 am
by HaftSwimmingly
Well, I've seem to hit a bit of a snag.

You see, when developing a mini-game of sorts, I decided to have the number of enemies be randomized, each spawning into a table, and being subsequently removed when the enemy comes in contact with a "bullet". However, the collision function that's supposed to remove the table ends up removing 2 enemies when 3-4 enemies are on screen, 3 enemies when 6 are on screen, etc. i've racked my brain with this one, and making the table check "backwards" isn't working. Maybe y'all can help.

Here's the enemy removal function:

Code: Select all

function bullet_ene_coll(obj, ta)
  
 remE = {}
  
for i,v in ipairs(p1.shots) do

  for ii,vv in ipairs(ta) do
    if coll(v.x, v.y, shots.w, shots.h, obj.x, obj.y, obj.w, obj.h) then
      
      table.insert(remE, ii)
    
      table.remove(p1.shots, i)
      shots.on = false
      shots.dir = "nil"
    end
  end
end

  for i,v in ipairs(remE) do
    table.remove(ta, v)
  end

  
end
and it's application:

Code: Select all

function dun:update(dt)

 
 UPDATE_PL(dt)
 skin.update(dt)
 
  for i, v in ipairs(new_e) do
    new_e[i]:update(dt) --"new_e" is the table that holds all the randomized enemies
    bullet_ene_coll(new_e[i], new_e)
  end
end

Re: Table.remove removing things twice

Posted: Sun Apr 17, 2016 12:09 pm
by bobbyjones
Make sure when you remove the enemy that you remove it's index from the remove enemy table other wise it will repeatedly remove the enemy at index 3 or whatever.

Code: Select all

for i,v in ipairs(remE) do
    table.remove(ta, v)
    remE[I] = nil
end

Re: Table.remove removing things twice

Posted: Sun Apr 17, 2016 2:42 pm
by HaftSwimmingly
bobbyjones wrote:Make sure when you remove the enemy that you remove it's index from the remove enemy table other wise it will repeatedly remove the enemy at index 3 or whatever.

Code: Select all

for i,v in ipairs(remE) do
    table.remove(ta, v)
    remE[I] = nil
end
While this is very helpful, it doesn't solve my problem.

I'm thinking the problem lies in my enemy-spawn function. As it inserts each randomized enemy into a table, i'm assuming that the index reshuffles whenever one index is deleted, causing the other to be deleted. But how do I prevent this (if it's possible?)

Re: Table.remove removing things twice

Posted: Sun Apr 17, 2016 2:50 pm
by ivan
Hi there.
Removing elements DURING iteration is done by going backwards:

Code: Select all

for i = #t, 1, -1 do
  if some condition then
    table.remove(t, i)
  end
end
So don't use "ipairs" if you are going to remove elements.

Also, the code is a little bit confusing, I suggest changing the first function to:

Code: Select all

-- find the "last" bullet inside the rectangle x,y => w,h
function queryBullets(x, y, w, h)
  local x2, y2 = x + w, y + h
  for i = 1, #bulletsList do
    local b = bulletsList[i]
    local bx, by = b.x, b.y
    if bx > x and bx < x2 and by > y and by < y2 then
      return b
    end
  end
end
As seen in the example above, treating the bullets as points
when checking for intersections makes the code simpler and faster.
Then you iterate the enemies and check each one against the bullets:

Code: Select all

for i = #enemiesList, 1, -1 do
  local e = enemiesList[i]
  local b = queryBullets(e.x, e.y, e.w, e.h)
  if b then
    table.remove(enemiesList, i)
    -- todo: remove bullet "b" returned by the query
  end
end

Re: Table.remove removing things twice

Posted: Mon Apr 18, 2016 12:54 am
by HaftSwimmingly
ivan wrote:Hi there.
Removing elements DURING iteration is done by going backwards:

Code: Select all

for i = #t, 1, -1 do
  if some condition then
    table.remove(t, i)
  end
end
So don't use "ipairs" if you are going to remove elements.

Also, the code is a little bit confusing, I suggest changing the first function to:

Code: Select all

-- find the "last" bullet inside the rectangle x,y => w,h
function queryBullets(x, y, w, h)
  local x2, y2 = x + w, y + h
  for i = 1, #bulletsList do
    local b = bulletsList[i]
    local bx, by = b.x, b.y
    if bx > x and bx < x2 and by > y and by < y2 then
      return b
    end
  end
end
As seen in the example above, treating the bullets as points
when checking for intersections makes the code simpler and faster.
Then you iterate the enemies and check each one against the bullets:

Code: Select all

for i = #enemiesList, 1, -1 do
  local e = enemiesList[i]
  local b = queryBullets(e.x, e.y, e.w, e.h)
  if b then
    enemiesList[i] = nil
    -- todo: remove bullet "b" returned by the query
  end
end
After some rewrites, it's working like a charm! Thanks so much, I don't know what i'd do without you forum-bros.

Re: Table.remove removing things twice

Posted: Tue Apr 19, 2016 10:05 am
by Robin
That should be table.remove(enemiesList, i), though

otherwise you end up with holes in your list

Re: Table.remove removing things twice

Posted: Fri Apr 22, 2016 8:26 am
by ivan
Good catch Robin! Fixed the code example.