Problem z usuwaniem przeciwnika z tablicy std::vector


#1

Witam serdecznie, jest to mój pierwszy post na tym forum i wystawiłem temat na stack’u ale jakoś przykłady kodu od innych programistów nie pomagały, więc może ktoś tutaj będzie wiedział jak pomóc. Problem wygląda dokładnie tak jak na filmie. Kiedy strzelam tylko w jednego przeciwnika mogę wyeliminować resztę za pomocą jednego. Wybaczcie najmocniej za to, że oglądając to można dostać epilepsji :smiley: . Przy okazji czy ktoś z was wie jak lepiej wyczyścić taką konsolę? Próbowałem używać przykładów z MSDN za pomocą FillConsoleOuputAttribute ale efekt był identyczny. Poniżej przedstawiam również kod odpowiedzialny za kolizję pocisku z przeciwnikiem, a reszta dotyczy gracza z przeciwnikiem, która działa poprawnie.

Link: https://www.youtube.com/watch?v=e1TigO6LBC8&feature=youtu.be

void Wave::update(Bullet* bullet, Player* player) {

    	for (int i = enemyArray.size() - 1; i >= 0; i--) {

    		//Bullet Collision with Enemy
    		//---------------------------
    		if (player->checkShoot()) {
    			if ((bullet->getPositionX() >= enemyArray.at(i).getPositionX()) && (bullet->getPositionX() <= enemyArray.at(i).getPositionX() + 5)) {
    				if ((bullet->getPositionY() >= enemyArray.at(i).getPositionY()) && (bullet->getPositionY() <= enemyArray.at(i).getPositionY() + 5)) {
    					player->addScore(10);
    					enemyArray.erase(enemyArray.begin() + i);
    				}
    			}
    		}

    		
    		//CREATE A TIME CLASS
    		//Player Collision with Enemy
    		//---------------------------
    		if (!player->checkShoot() && !player->checkTouchEnemy()) {
    			if ((player->getPositionX() >= enemyArray.at(i).getPositionX()) && (player->getPositionX() <= enemyArray.at(i).getPositionX() + 5)) {
    				if ((player->getSpritePositionY() >= enemyArray.at(i).getPositionY()) && (player->getSpritePositionY() <= enemyArray.at(i).getPositionY() + 5)) {
    					timer.setEnableCount(true);
    					player->deincrementLive();
    				}
    			}
    		}

    		if (timer.getEnableCount()) {
    			player->setTouchEnemy(true);
    			timer.incrementTime();
    		}

    		if (timer.getTime() >= TIME_AWAY) {
    			timer.setEnableCount(false);
    			timer.setTime(0);
    			player->setTouchEnemy(false);
    		}
    	}

    	if (enemyArray.size() <= 0) {
    		std::cout << "WAVE COMPLETE";
    		Sleep(2000);
    		incrementEnemyCount();
    		createWave(player);
    	}
    }

#2

Nie jestem jakimś wielkim znawcą cpp, ale to trochę wygląda jakby kasował ostatniego z wektora patrząc na zachowanie z filmiku. Zajrzyj debuggerem (albo wyprintuj :slight_smile: )- którego faktycznie tam usuwa i czy to ten, który powinien być. To pewnie się trochę rozjaśni co jest nie tak :slight_smile:

Tam jakiś wyjątek Ci nie leci jak usunie ostatni element z tablicy, a później próbuje się do niego odnieść w kolejnym ifie? (trudno się poruszać w tym kodzie bez znajomości reszty kodu)


#3

Spróbuję zrobić tak jak mówisz. Jedyne co wyświetlałem to indeks przeciwnika w trakcie kolizji z pociskiem i kiedy kula dotyka przeciwnika to pobierany indeks się zgadza , więc powinien wsadzać go do funkcji erase(). Przy okazji szanuję za projekt TAnima(śledzę na bieżąco) i oczywiście dziękuję za pomoc.


#4

Czasem są jakieś głupie błędy, które najlepiej podglądać debuggerem :slight_smile: No albo wprawne cpp oko może by coś tam znalazło, ale moje do nich nie należy :slight_smile:

Dzięki :smiley: i nie ma za co :slight_smile:


#5

Pozycje sprawdzasz na indeksie “i” ale usuwasz już nie na “i” tylko:


#6

Owszem tylko, że w funkcji erase() musi być podany początek tablicy, a znowu w funkcji at() nie mogę zastosować: enemyArray.at(enemyArray.begin() + i), chyba że po prostu brak mi wiedzy jak ominąć ten problem, co jest pewne :D. Mógłbyś przedstawić jakieś rozwiązanie ? Pozdrawiam.


#7

Ja nie wiem jakiego typu jest enemyArray, i co za API używasz (erase, at) ale na pierwszy rzut oka powinieneś usuwać obiekt, w którego współrzędne trafił pocisk czyli “i” a nie “begin() + i”, to pewnie jest inny obiekt dlatego znikają Ci nie te obiekty z mapy.


#8

Tablica enemyArray jest typu mojej klasy “Enemy”. Natomiast funkcje erase() i at() należą do tablicy std::vector z STL. Problem jest taki, że w funkcji at() mogę podać tylko liczbę “int” i moje IDE buntuję się, kiedy wpisuję do funkcji at(enemyArray.begin() + i), znowu funkcja erase() wymaga w argumencie początku tablicy enemyArray.begin(). Oby dwie te funkcje są wbudowane w domyślną tablice std::vector. Poniżej przedstawiam kod nagłówka “Wave.h”:

#pragma once
#ifndef WAVE_H
#define WAVE_H

#include “Window.h”
#include “Enemy.h”
#include “Bullet.h”
#include “CollisionTimer.h”
#include
#include

#define RESPAWN_AREA 25
#define TIME_AWAY 500

class Wave
{
public:
Wave() : enemyCount(0){}
~Wave();

void setEnemyCount(int count);
void incrementEnemyCount();

bool createWave(Player* player);
void drawWave(Window* window);
void update(Bullet* bullet, Player* player);
void restart(Player* player);

//Getting methods
int getEnemyCount() const;

private:
int enemyCount;
std::vector enemyArray;
CollisionTimer timer;

private:
void respawnArea(int& posX, int& posY, Player* player);

};

#endif WAVE_H

A tutaj “Wave.cpp”:

#include "stdafx.h"
#include "Wave.h"

Wave::~Wave()
{
}

void Wave::setEnemyCount(int count) {
	enemyCount = count;
}

void Wave::incrementEnemyCount() {
	enemyCount += 5;
}

bool Wave::createWave(Player* player) {
	for (int i = 0; i < enemyCount; i++) {
		enemyArray.push_back(Enemy());
	}

	int randomPositionX = 0;
	int randomPositionY = 0;

	for (int i = 0; i < enemyArray.size(); i++) {
		randomPositionX = rand() % 150 + 10;
		randomPositionY = rand() % 50 + 10;
		//respawnArea(randomPositionX, randomPositionY, player);

		if (!enemyArray.at(i).init(randomPositionX, randomPositionY)) {
			std::cout << "Create wave(init enemies) - failed\n";
			return false;
		}
	}

	return true;
}

void Wave::drawWave(Window* window) {
	window->setCursor({ 0, 2 });
	window->setColor(Window::Color::RED);
	std::cout << "Enemies: " << enemyArray.size();
	window->setColor(Window::Color::WHITE);

	for (int i = 0; i < enemyArray.size(); i++) {
		enemyArray.at(i).draw(window);
		//std::cout << i;
	}
}

void Wave::update(Bullet* bullet, Player* player) {

	for (int i = enemyArray.size() - 1; i >= 0; i--) {

		//Bullet Collision with Enemy
		//---------------------------
		if (player->checkShoot()) {
			if ((bullet->getPositionX() >= enemyArray.at(i).getPositionX()) && (bullet->getPositionX() <= enemyArray.at(i).getPositionX() + 5)) {
				if ((bullet->getPositionY() >= enemyArray.at(i).getPositionY()) && (bullet->getPositionY() <= enemyArray.at(i).getPositionY() + 5)) {
					player->addScore(10);
					enemyArray.erase(enemyArray.begin() + i);
				}
			}
		}

		
		//Player Collision with Enemy
		//---------------------------
		if (!player->checkShoot() && !player->checkTouchEnemy()) {
			if ((player->getPositionX() >= enemyArray.at(i).getPositionX()) && (player->getPositionX() <= enemyArray.at(i).getPositionX() + 5)) {
				if ((player->getSpritePositionY() >= enemyArray.at(i).getPositionY()) && (player->getSpritePositionY() <= enemyArray.at(i).getPositionY() + 5)) {
					timer.setEnableCount(true);
					player->deincrementLive();
				}
			}
		}

		if (timer.getEnableCount()) {
			player->setTouchEnemy(true);
			timer.incrementTime();
		}

		if (timer.getTime() >= TIME_AWAY) {
			timer.setEnableCount(false);
			timer.setTime(0);
			player->setTouchEnemy(false);
		}
	}

	if (enemyArray.size() <= 0) {
		std::cout << "WAVE COMPLETE";
		Sleep(2000);
		incrementEnemyCount();
		createWave(player);
	}
}

int Wave::getEnemyCount() const {
	return enemyCount;
}

void Wave::restart(Player* player) {
	enemyArray.clear();
	setEnemyCount(10);
	createWave(player);
}

void Wave::respawnArea(int& posX, int& posY, Player* player) {

	if (posX > player->getPositionX() - RESPAWN_AREA) {
		posX -= RESPAWN_AREA;
	}
	else if (posX < player->getPositionX() + RESPAWN_AREA) {
		posX += RESPAWN_AREA;
	}

	if (posY > player->getSpritePositionY() - RESPAWN_AREA) {
		posY -= RESPAWN_AREA;
	}
	else if (posY < player->getSpritePositionY() + RESPAWN_AREA) {
		posY += RESPAWN_AREA;
	}
}

#9

Z tego co widzę w referencji std::vector to erase wymaga iteratora, begin zwraca iterator.

Rozwiązanie to przerób całą pętle for na iterator [1]

[1] http://www.cplusplus.com/reference/vector/vector/begin/


#10

Z tego co pamiętam to próbowałem już użyć pętli z iteratorem ale chyba nie wsadzałem owega iteratora do funkcji erase(), więc pozmieniam kod i dam odpowiedź.