Wykrywanie kolizji pocisku, problem z iteratorem.

1

Cześć robię grę typu Space Invaders na projekt w SFML, i zderzyłem się z problemem który przewyższa moje umiejętności, albowiem sprawdzam kolizję pocisku z przeciwnikiem i wszystko działa tak jak powinno, do momentu kiedy wewnątrz kontenera jest jeden obiekt typu Bullet, kiedy są już dwa lub więcej i nastąpi kolizja to otrzymuję błąd "Unhandled exception at 0x00007FFF7BA9DD7E (ucrtbase.dll) in SI.exe: Fatal program exit requested.". Część zakomentowana spełnia tą samą funkcję co ta wyżej (tak mi się przynajmniej wydaje). Shots fired przyjmuje wartość 0, po dodaniu obiektu Bullet do wektora bullets jest inkrementowana. Co warto wspomnieć każdy Bullet posiada wartość int ID która jest równa Shots fired (pierwszy pocisk 0 drugi 1 itp.) Proszę was o pomoc jak to można rozwiązać, wydaje mi się że winny jest tutaj iterator, albo bardziej jego specyfika.
Kod:

void Bullet::hit(vector<Enemy>& enemies,vector<Pixel>& oneShield, vector<Bullet>& bullets, int &killedEnemies, int shotsFired) {
	for (auto& enemy : enemies) {
		if (enemy.shape.getGlobalBounds().intersects(this->shape.getGlobalBounds())) {
			for (auto i = enemies.begin(); i < enemies.end(); i++) {
				if (i->ID == enemy.ID) {
					enemies.erase(i);
					break;
				}
			}
			killedEnemies = killedEnemies + 1;
			auto it = bullets.begin(); 
			bullets.erase(it);
			/*
			for (auto j = bullets.begin(); j < bullets.end(); j++) {
				if (j->ID == this->ID) {
					bullets.erase(j);
					break;
				}
			}
			*/
		}
	}

screenshot-20220605194305.png

2

std::vector<T,Allocator>::erase - cppreference.com

Erases the specified elements from the container.

  1. Removes the element at pos.

  2. Removes the elements in the range [first, last).

Invalidates iterators and references at or after the point of the erase, including the end() iterator.

Użyj std::erase_if.

1
MarekR22 napisał(a):

std::vector<T,Allocator>::erase - cppreference.com

Erases the specified elements from the container.

  1. Removes the element at pos.

  2. Removes the elements in the range [first, last).

Invalidates iterators and references at or after the point of the erase, including the end() iterator.

Użyj std::erase_if.

Czy chodziło Ci o remove_if, bo nie mogę znaleźć erase_if, zrobiłem to samo za pomocą remove_if i wynik jest dokładnie taki sam, wszystko działa dopóki w wektorze jest jeden obiekt.

int actualBulletID = this->ID;
auto end = remove_if(bullets.begin(),
				bullets.end(),
				[actualBulletID](Bullet &bullet) {
					return bullet.ID == actualBulletID;    
				});

			bullets.erase(end, bullets.end());
1

Zmień Id dla tych co mają być skasowane na 0 czy tam -1 po czym kasuj wszystko z takim Id

3

Pomijając błędy z obsługą iteratorów i bezsensowną pętlę wewnętrzną (szukasz tam obiektu, który już przecież masz) - moim zdaniem coś tu nie pasuje z architekturą...
Skoro jest to funkcja klasy Bullet, to przekazywanie do niej vector<Bullet> już na dzień dobry wydaje mi się być podejrzane. Ja bym to zrobił w osobnej klasie (może coś w stylu CollisionManager) - ale może masz jakiś powód by zrobić tak jak zrobiłeś...

Tak czy inaczej

Na wejściu masz dwa wektory:

  1. bullets
  2. enemies

Dla każdego pocisku chcesz sprawdzić, czy trafia jakiegoś wroga - jeśli tak, to chcesz usunąć ten pocisk i trafionego wroga

Przetestuj sobie:

int main()
{
    std::vector<int> bullets { 1, 3, 9, 12};
    std::vector<int> enemies { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    auto it = bullets.begin();
    while (it != bullets.end())
    {
        auto hit = std::find_if( enemies.begin(), enemies.end(),  [&it](int enemy) { return enemy == *it; } );

        if (hit != enemies.end())
        {
            enemies.erase(hit);
            it = bullets.erase(it);
        }
        else
            ++it;
    }
}

0

Udało mi się w końcu to zaimplementować zrobiłem to w następujący sposób:
Utworzyłem nową klasę BulletVec która w polach publicznych zawiera vector<Bullet> bullets
w funkcji main tworzę obiekt BulletVec oraz wywyołuję poniższą funkcję w głównej pętli programu.

Poniżej funkcja wykrywająca kolizję

void BulletVec::bulletCollision(vector<Enemy>& enemies) {
	auto it = this->bullets.begin();
	while (it != this->bullets.end())
	{
		auto hit = std::find_if(enemies.begin(), enemies.end(), [&it](Enemy enemy) { return it->shape.getGlobalBounds().intersects(enemy.shape.getGlobalBounds()); });

		if (hit != enemies.end())
		{
			enemies.erase(hit);
			it = this->bullets.erase(it);
		}
		else
			++it;
	}
}

Zostawiam rozwiązanie dla potomnych i dziękuję wszystkim za pomoc :)

0
Dawid Grabowski napisał(a):

Poniżej funkcja wykrywająca kolizję

To jest bardzo naiwne podejście:
##### - przeciwnik
* - kula
Klatka X+0



#####


  *

Klatka X+1



 #####
   *

  

Klatka X+2


    *
  #####
   

  

Ups, brak kolizii?

1 użytkowników online, w tym zalogowanych: 0, gości: 1