Table.remove removing things twice

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
HaftSwimmingly
Prole
Posts: 13
Joined: Fri Dec 11, 2015 8:13 am

Table.remove removing things twice

Post 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
tomaki on IRC
bobbyjones
Party member
Posts: 730
Joined: Sat Apr 26, 2014 7:46 pm

Re: Table.remove removing things twice

Post 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
User avatar
HaftSwimmingly
Prole
Posts: 13
Joined: Fri Dec 11, 2015 8:13 am

Re: Table.remove removing things twice

Post 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?)
tomaki on IRC
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Table.remove removing things twice

Post 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
Last edited by ivan on Fri Apr 22, 2016 8:06 am, edited 1 time in total.
User avatar
HaftSwimmingly
Prole
Posts: 13
Joined: Fri Dec 11, 2015 8:13 am

Re: Table.remove removing things twice

Post 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.
tomaki on IRC
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Table.remove removing things twice

Post by Robin »

That should be table.remove(enemiesList, i), though

otherwise you end up with holes in your list
Help us help you: attach a .love.
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Table.remove removing things twice

Post by ivan »

Good catch Robin! Fixed the code example.
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 76 guests