Page 1 of 1

Question about loops

Posted: Tue Aug 20, 2019 2:08 am
by narffx
Hi!, i've been struggling with the differences or not between this two ways of doing this.
From my beginner point of view, it's the same but maybe there are potencial bugs coming once the proyect moves forward so that's why i'm asking:

Code: Select all

for i, m in ipairs(monsters) do
	for j, b in ipairs(bullets) do
		if check_collision(b.x, b.y, b.width, b.height, m.x, m.y, m.width, m.height) then
			table.remove(bullets, j)
			m.hp = m.hp - b.damage
			if m.hp == 0 then
				table.remove(monsters, i)
			end
		end
	end
end
and the other way i was doing it was:

Code: Select all

for i, m in ipairs(monsters) do
		for j, b in ipairs(bullets) do
			if check_collision(b.x, b.y, b.width, b.height, m.x, m.y, m.width, m.height) then
				b.dead = true
				m.hp = m.hp - b.damage
				if m.hp == 0 then
                                m.dead = true
				end
			end
		end
	end

for i=#monsters,1,-1 do
    local m = monsters[i]
    if m.dead == true then
      table.remove(monsters, i)
    end
  end

for i=#bullets, 1, -1 do
    local b = bullets[i]
    if b.dead == true then
      table.remove(bullets, i)
    end
  end

is it really the same? is one way better than the other? both are wrong? any suggestions?

Thanks in advance!

Re: Question about loops

Posted: Tue Aug 20, 2019 10:36 am
by pgimeno
The second one is more reliable.

The first, single loop has two problems. One is that it can skip elements; the other one is that simultaneous collisions might not be detected. The problem of skipping elements can be solved by iterating backwards; the problem of simultaneous collisions can only be solved by marking the elements and removing them afterwards like the last method does.

How does the first version skip elements? Well, let's say you have three monsters, A, B and C, in positions 1, 2 and 3, and a bullet hits monster A. Your loop sets i to values from 1 to 3. When i is 1, the collision is detected, and A is removed. The problem is that when an element is removed, the elements are shifted! After that happens, the table has B in position 1 and C in position 2. In the next loop iteration, i is set to 2, and B, which is now in position 1, is never checked. The same happens to bullets.

The problem of skipping simultaneous collisions is as follows. If the same bullet hits two monsters at the same time (which can be more or less likely depending on how big your bullets are), first it checks collisions between the bullet and one of the monsters. If a collision is detected, then both the bullet and the monster are removed. That means that the collision between the bullet and the other monster is never checked.

Both problems are solved by deferring deletion until all checks are finished, and making the deletion loop iterate backwards, like in the second version.

Re: Question about loops

Posted: Tue Aug 20, 2019 4:44 pm
by narffx
pgimeno wrote: Tue Aug 20, 2019 10:36 am The second one is more reliable.

The first, single loop has two problems. One is that it can skip elements; the other one is that simultaneous collisions might not be detected. The problem of skipping elements can be solved by iterating backwards; the problem of simultaneous collisions can only be solved by marking the elements and removing them afterwards like the last method does.

How does the first version skip elements? Well, let's say you have three monsters, A, B and C, in positions 1, 2 and 3, and a bullet hits monster A. Your loop sets i to values from 1 to 3. When i is 1, the collision is detected, and A is removed. The problem is that when an element is removed, the elements are shifted! After that happens, the table has B in position 1 and C in position 2. In the next loop iteration, i is set to 2, and B, which is now in position 1, is never checked. The same happens to bullets.

The problem of skipping simultaneous collisions is as follows. If the same bullet hits two monsters at the same time (which can be more or less likely depending on how big your bullets are), first it checks collisions between the bullet and one of the monsters. If a collision is detected, then both the bullet and the monster are removed. That means that the collision between the bullet and the other monster is never checked.

Both problems are solved by deferring deletion until all checks are finished, and making the deletion loop iterate backwards, like in the second version.
Thanks for taking the time to reply! really good answer, you clarified everything!! Now, time to make some modifications, and again, thanks!!!