Page 1 of 2

Why is table.remove making my program crash?

Posted: Thu Oct 27, 2011 6:35 pm
by enderspike
Newb here,

So I have collision checks for entities down, but I'm having trouble removing entities from tables when I want. I remove enemies or bullets from thier respective tables, and everything explodes. Why? What's going wrong? How can I fix it?

Thanks!

Re: Why is table.remove making my program crash?

Posted: Thu Oct 27, 2011 6:58 pm
by ivan

Code: Select all

		for first = 1, #PlayerBullets do
			for second=1, #Enemies do
				if CollisionCheck(PlayerBullets[first], Enemies[second]) then
					table.remove(PlayerBullets, first)
					table.remove(Enemies, second)
				end
			end
		end
When you remove an element from a table at index "i", it shifts the indices of all following elements.

Try replacing the 2 for loops with something like:

Code: Select all

local i = 1
while i < #playerBullets do
  if CollisionCheck ( ) == true then
    table.remove ( playerBullets, i )
  else
     i = i + 1
  end
end
Or use pairs to iterate "playerBullets" and instead of calling "table.remove" do
playerBullets = nil
Notice that in the second case, you can't use "#playerBullets" to check the table size.

Re: Why is table.remove making my program crash?

Posted: Thu Oct 27, 2011 7:14 pm
by bartbes
Other common tricks include marking the values or storing their indexes so you can remove them in a second loop, which may or may not do a reverse traversal.

Re: Why is table.remove making my program crash?

Posted: Thu Oct 27, 2011 7:48 pm
by Robin
bartbes wrote:which may or may not do a reverse traversal.
I wouldn't recommend not reversing. That way, you get the same index shifting again.

Unless you are storing the values. Then it works.

Of course, that also makes it an order of n less efficient, because then you have to look up the index each time.

Re: Why is table.remove making my program crash?

Posted: Thu Oct 27, 2011 9:43 pm
by enderspike
Got it fixed! Thanks guys. Now I just have to figure out why sometimes multiple enemies get deleted when I hit one. Only sometimes.

Re: Why is table.remove making my program crash?

Posted: Thu Oct 27, 2011 11:21 pm
by Taehl
My usual method goes like this:

Code: Select all

for i=#mobs, 1, -1 do
	local m = mobs[i]
	if m.health < 0 then table.remove(mobs, i) end
end
Since going backwards means you don't have to worry about the shift.

Re: Why is table.remove making my program crash?

Posted: Fri Oct 28, 2011 7:28 am
by miko
Taehl wrote:My usual method goes like this:

Code: Select all

for i=#mobs, 1, -1 do
	local m = mobs[i]
	if m.health < 0 then table.remove(mobs, i) end
end
Since going backwards means you don't have to worry about the shift.
If you have one table to traverse, this will work. But not in this case, where you traverse both tables, and any item from both of tables can be removed anytime. So you either mark items for later removal, or create a copy of the tables in your iterator function (so the original tables can be modified without affecting the iterator).

Re: Why is table.remove making my program crash?

Posted: Fri Oct 28, 2011 7:20 pm
by Taehl
miko wrote:
Taehl wrote:My usual method goes like this:

Code: Select all

for i=#mobs, 1, -1 do
	local m = mobs[i]
	if m.health < 0 then table.remove(mobs, i) end
end
Since going backwards means you don't have to worry about the shift.
If you have one table to traverse, this will work. But not in this case, where you traverse both tables, and any item from both of tables can be removed anytime. So you either mark items for later removal, or create a copy of the tables in your iterator function (so the original tables can be modified without affecting the iterator).
*Makes a few tweaks*

Code: Select all

for i=math.max(#mobs, #ents), 1, -1 do
	local m,e = mobs[i], ents[i]
	if m and m.health < 0 then table.remove(mobs, i) end
	if e and e.health < 0 then table.remove(ents, i) end
end

Re: Why is table.remove making my program crash?

Posted: Fri Oct 28, 2011 9:10 pm
by miko
Taehl wrote:
miko wrote:
Taehl wrote:My usual method goes like this:

Code: Select all

for i=#mobs, 1, -1 do
	local m = mobs[i]
	if m.health < 0 then table.remove(mobs, i) end
end
Since going backwards means you don't have to worry about the shift.
If you have one table to traverse, this will work. But not in this case, where you traverse both tables, and any item from both of tables can be removed anytime. So you either mark items for later removal, or create a copy of the tables in your iterator function (so the original tables can be modified without affecting the iterator).
*Makes a few tweaks*

Code: Select all

for i=math.max(#mobs, #ents), 1, -1 do
	local m,e = mobs[i], ents[i]
	if m and m.health < 0 then table.remove(mobs, i) end
	if e and e.health < 0 then table.remove(ents, i) end
end
That could work in your case, but in the OP code there was checking for collisions between any of elements from both tables. So it was real 2-D problem, which can not (easily) be "linearized", like your case (in which there are no interactions between mobs and ents elements).

Re: Why is table.remove making my program crash?

Posted: Fri Oct 28, 2011 9:26 pm
by Taehl
miko wrote:So it was real 2-D problem, which can not (easily) be "linearized", like your case (in which there are no interactions between mobs and ents elements).
Argh, fine!

Code: Select all

for i=math.max(#mobs, #ents), 1, -1 do
	local m,isDead = mobs[i]
	for i=#ents, 1, -1 do
		local e = ents[i]
		if whateverItIsYoureDoing then
			isDead = true
			table.remove(ents, i)
		end
	end
	if isDead then table.remove(mobs, i) end
end
The same basic method I presented still applies.