Gå til innhold

[Løst]Trenger hjelp med fjerning av elementer i en vector.


Anbefalte innlegg

Hei,

 

Jeg driver å lager et enkelt spill, hvor man skal ta noen sprites. De skal da forsvinne når figuren kommer over dem.

 

Jeg har alle spritsene i en vector. Så har jeg da denne testen når de skal fjernes:

void Game::doSprites(Player p) {
//Function sp test about a sprite should be deleted or not...
for(int i = 0; i < Sprites.size(); i++) {
	/*if(p.rect.x > this->Sprites[i]->rect.x && (p.rect.x+p.rect.w < (this->Sprites[i]->rect.x+this->Sprites[i]->rect.w)+10)){
		std::cout << "Remove" << std::endl;
	}*/http://www.diskusjon.no/style_images/main_gfx/folder_editor_images/rte-code-button.png
	if(p.rect.x == this->Sprites[i]->rect.x)
		this->Sprites.erase(Sprites.begin() + i);
}
}//End doSprites()

 

Jeg vet at jeg har bare sjekker på x, men skal bare ha det til å fungere på denne måten først.

 

Problemet mitt er at det er kun på en sprite hvor det fungerer. Den spriten fjerner seg uten problemer. Men skjønner ikke helt hvorfor de andre ikke blir borte.

 

Noen som ser hva som er feil?

 

Setter pris på all hjelp.

Lenke til kommentar
Videoannonse
Annonse

Jeg ville brukt en list istedet for vektor, den er bedre egnet.

Grunnen er at list er en doubly-linked list som det heter, og det å slette et element med en iterator (som du uansett bruker her) vil være lynraskt, siden det aldri er nødvendig med noen stor dataflytting.

 

for(list<Sprite>::iterator it = Sprites.begin(); it != Sprites.end(); it++)
{
 if(p.rect.x == it->rect.x)
Sprites.erase(it);
}

 

edit: hva slags datatype er rect.x? for hvis det er float eller double, burde du ikke bruke == for å sammenligne.

Endret av GeirGrusom
Lenke til kommentar
Jeg har alle spritsene i en vector. Så har jeg da denne testen når de skal fjernes:

void Game::doSprites(Player p) {
//Function sp test about a sprite should be deleted or not...
for(int i = 0; i < Sprites.size(); i++) {
	/* ... */
	if(p.rect.x == this->Sprites[i]->rect.x)
		this->Sprites.erase(Sprites.begin() + i);
}
}//End doSprites()

 

(...)

 

Problemet mitt er at det er kun på en sprite hvor det fungerer. Den spriten fjerner seg uten problemer. Men skjønner ikke helt hvorfor de andre ikke blir borte.

 

La oss si du har 10 elementer og fjerner element #0. i == 0 og du fjerner Sprites.begin(). Før neste neste iterasjon økes i med 1 (dvs i == 1). I neste iterasjon ønsker du å se på neste element. Dette elementet er nå under indeks 0 (du har slettet et element), men du ser på Sprites, dvs. Sprites[1] som er ett element "for langt" fram. Ellers misforstod jeg totalt hva koden gjorde?

Endret av zotbar1234
Lenke til kommentar
edit: hva slags datatype er rect.x? for hvis det er float eller double, burde du ikke bruke == for å sammenligne.

 

rect.x er en Sint16.

 

Prøver ut å bruke list i stedet for vector, så får jeg se om det fungerer bedre.

 

Jeg endret nå ut til en std::list<Sprite*> Sprites;

 

Så laget jeg denne draw for-loopen:

for(std::list<Sprite*>::iterator it = this->Sprites.begin(); it != this->Sprites.end(); it++)
{
		it->draw(screen);
}

 

Men får feilmelding på at draw ikke er et member i std::list.

Error 3 error C2039: 'draw' : is not a member of 'std::list<_Ty>::_Iterator<_Secure_validation>' game.cpp 61

Error 2 error C2839: invalid return type 'Sprite **' for overloaded 'operator ->' game.cpp 61

Endret av TeisL
Lenke til kommentar
Det er fordi du har Sprite* i listen.

Du må enten legge Sprite i listen, eller dobbel-dereferere:

(*it)->draw(screen);

Takk så mye, da var feilmeldingen borte. Nå må jeg bare finne ut hvorfor det kræsjer når jeg paserer spritsene mine.

Endret av TeisL
Lenke til kommentar

Hei igjen,

 

for(std::list<Sprite*>::iterator it = Sprites.begin(); it != Sprites.end(); it++)
{
if(p.rect.x == (*it)->rect.x)
	Sprites.erase(it);
}

 

Nå har jeg denne loopen for å slette, men den reagerer fortsatt bare på den samme spriten som på starten. Nå, så kræsjer den bare når jeg kommer til den aktuelle spriten.

 

Noen som vet råd?

Lenke til kommentar
Hmmmm prøv dette:

for(std::list<Sprite*>::iterator it = Sprites.begin(); it != Sprites.end();)
{
std::list<Sprite*>::iterator next = it + 1;
if(p.rect.x == (*it)->rect.x)
	Sprites.erase(it);
it = next;
}

 

Ser tankegangen, så prøvde det, men det kom historisk mange feilmeldinger.

 

Men glemte å si hva som var feilmeldingen på den første: "List iterator not incrementable".

Lenke til kommentar
for(std::list<Sprite*>::iterator it = Sprites.begin(); it != Sprites.end(); it++)
{
if(p.rect.x == (*it)->rect.x)
	Sprites.erase(it);
}

 

Nå har jeg denne loopen for å slette, men den reagerer fortsatt bare på den samme spriten som på starten. Nå, så kræsjer den bare når jeg kommer til den aktuelle spriten.

 

erase() gjør iteratorne ugyldige. Dvs. etter en erase(it), kan du ikke lenger aksessere it (for å gå til neste element, f.eks.).

 

last = it;
++it;
if ( <condition> )
Sprites.erase(last);

Lenke til kommentar

Som zootbar1234 sier, blir ugyldige, men hvis ikke jeg tar helt feil returnerer erase en gyldig iterator til neste element etter det som ble sletta, så den løser problemet for deg.

if(<condition>)
it = Sprites.erase(it);

 

Edit: Sjekka på cppreference.com, og det ser ut til å stemme: http://www.cppreference.com/wiki/stl/vector/erase

Endret av Dead_Rabbit
Lenke til kommentar
Som zootbar1234 sier, blir ugyldige, men hvis ikke jeg tar helt feil returnerer erase en gyldig iterator til neste element etter det som ble sletta, så den løser problemet for deg.

if(<condition>)
it = Sprites.erase(it);

 

Edit: Sjekka på cppreference.com, og det ser ut til å stemme: http://www.cppreference.com/wiki/stl/vector/erase

Det var riktig ja, for det fungerte.

for(std::list<Sprite*>::iterator it = Sprites.begin(); it != Sprites.end(); it++)
{
if(p.rect.x == (*it)->rect.x)
	it = Sprites.erase(it);
}

 

Så dette fungerte, eller det vil si, det er bare en som den fjerner, når jeg går over resten, så vet jeg ikke hvorfor de ikke forsvinner.

Lenke til kommentar
Det var riktig ja, for det fungerte.

for(std::list<Sprite*>::iterator it = Sprites.begin(); it != Sprites.end(); it++)
{
if(p.rect.x == (*it)->rect.x)
	it = Sprites.erase(it);
}

 

Hopper ikke du over 2 elementer når du sletter? (en med erase() og en med postinkrement (bruk preinkement, forresten)

Lenke til kommentar

Det er ingen forskjell på post- og preinkrementering i dette tilfellet.

 

	for(i = 0; i < 100; i++)
00AB1395  mov		 dword ptr [i],0 
00AB139C  jmp		 main+37h (0AB13A7h) 
00AB139E  mov		 eax,dword ptr [i] 
00AB13A1  add		 eax,1 
00AB13A4  mov		 dword ptr [i],eax 
00AB13A7  cmp		 dword ptr [i],64h 
00AB13AB  jge		 main+3Fh (0AB13AFh) 
{
}
00AB13AD  jmp		 main+2Eh (0AB139Eh) 
for(i = 0; i < 100; ++i)
00AB13AF  mov		 dword ptr [i],0 
00AB13B6  jmp		 main+51h (0AB13C1h) 
00AB13B8  mov		 eax,dword ptr [i] 
00AB13BB  add		 eax,1 
00AB13BE  mov		 dword ptr [i],eax 
00AB13C1  cmp		 dword ptr [i],64h 
00AB13C5  jge		 main+59h (0AB13C9h) 
{
}
00AB13C7  jmp		 main+48h (0AB13B8h) 

++i;
00AB13C9  mov		 eax,dword ptr [i] 
00AB13CC  add		 eax,1 
00AB13CF  mov		 dword ptr [i],eax 

i++;
00AB13D2  mov		 eax,dword ptr [i] 
00AB13D5  add		 eax,1 
00AB13D8  mov		 dword ptr [i],eax

Lenke til kommentar

Hm, at jeg hopper over en ville ikke forundre meg i alle fall, for den som blir slettet nå er den som ligger på plass 1 i lista (som nummer 2).

 

Men hopper jeg over et element her når jeg bruker erase()? For hvis jeg skal slette element it, og bruker erase(it), så trodde jeg da ut ifra det som vi kom frem til tidligere at iteratoren it blir satt til neste pga. at erase() returnerte til neste element?

Lenke til kommentar

Grunnen er at iteratoren vil fortsette en gang for mye dersom du sletter et element.

Løsningn er bare å fjerne it++ fra for løkken, og legge den inn i en else blokkk.

 

for(std::list<Sprite*>::iterator it = Sprites.begin(); it != Sprites.end();)
{
if(p.rect.x == (*it)->rect.x)
	it = Sprites.erase(it);
else
	it++;
}

Lenke til kommentar
Det er ingen forskjell på post- og preinkrementering i dette tilfellet.

 

Men denne versjonen av denne kompilatoren for iteratoren for denne containertypen. Preinkrement er i utgangspunktet billigere, fordi man ikke trenger å lage en kopi (semantikken til postinkrement krever en slik kopi, MEN den kan fjernes av kompilatoren, dersom forholdene tilsier det). Like greit å lære å bruke den rette operatoren.

Lenke til kommentar

Hei igjen,

Jeg begynner å lure på om det at grunnen til at jeg bare får slettet en kan ha noe med måten jeg tegner spritesene på.

 

For jeg har en funksjon run() i game som er den som blir kjørt når spillet starter. Der initialiserer jeg vectoren med alle sprites, slik at jeg får tegnet alle på begynnelsen av hvert spill.

if(this->drawAll) {
	this->Sprites.push_back(new Sprite(this->spriteClass->GFX_SOPP,119,336));
	this->Sprites.push_back(new Sprite(this->spriteClass->GFX_SOPP,230,415));
	this->Sprites.push_back(new Sprite(this->spriteClass->GFX_SOPP,304,272));
	this->Sprites.push_back(new Sprite(this->spriteClass->GFX_SOPP,458,240));
	this->Sprites.push_back(new Sprite(this->spriteClass->GFX_SOPP,583,192));
	this->Sprites.push_back(new Sprite(this->spriteClass->GFX_SOPP,282,109));

	this->drawAll = false;
}

 

Så bruker jeg inne i game-loopen en for løkke tl å iterere over vectoren over da be som skal tegnes.

for(std::list<Sprite*>::iterator it = this->Sprites.begin(); it != this->Sprites.end(); it++)
		(*it)->draw(screen);

 

Hvis noen ser noen problemer i denne måten å gjøre det på så tar jeg i mot alle tilbakemeldinger.

 

Edit: Selvfølgelig, nå kom jeg på hva som selvfølgelig er problemet... Når beveger figuren min øker jo jeg rect.x med 10... Så da var det bare en sprites som sto eksakt på posisjonen :p

Endret av TeisL
Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
×
×
  • Opprett ny...