Sign in to follow this  
AhmedCoeia

Getting array boundary exception that is so weird

Recommended Posts

I'm trying to implement a space invader game, now I'm trying to check if bullets hit an invader, then I remove it.

I'm ONLY get an exception when I try to shoot the last invader in the row. For e.g If I have just one row and 8 invaders, the last one, when the bullet hits it, I got a strange boundary out of exception.

The initialization is like that, in pseudo code:

struct Invaders_t
{
vector<Invader*> *invaders;
}
for (int j = 0; j < 1; j++)
{
x_spacing = 15;
for (int i = 0; i < 8; i++)
{
Invader *invader = new Invader();
g_Invaders.invaders->push_back(invader);

}

for each frame:

for (int x = 0; x < 1; x++)
{
for (int y = 0; y < 8; y++)
{
Invader *pInvader = (*(g_Invaders.invaders))[x*8 + y];

if (pInvader != nullptr && pInvader->AlienAtals->GetImage() != nullptr)
{

for (list<Bullet*>::iterator it = ship->Bullets->begin(); it != ship->Bullets->end(); it++)
{
float left = (*it)->Position.x - 5;
float top = (*it)->Position.y - 13;

if (CheckCollision(left, top, pInvader->AlienSprite->m_X, pInvader->AlienSprite->GetImage()->GetWidth()/4
, pInvader->AlienSprite->m_Y, pInvader->AlienSprite->GetImage()->GetHeight()/4))
{
std::vector<Invader*>::iterator iter = g_Invaders.invaders->begin();
std::advance(iter, (x *8) + y);
Invader *invader = *iter;
g_Invaders.invaders->erase(iter);
delete invader;
}

}
}
}
}

Share this post


Link to post
Share on other sites

Also worth pointing out if you change to using iterators you can't modify an stl container whilst iterating it, e.g. By inserting or deleting entries, unless you take special measures with your loop.

 

If you really need to, You should do something like:

for (iter i = container.begin(); i != container.end();)
{
        If (deleting)
                  i = container.erase(i);
        else
                 ++i;
}
Edited by braindigitalis

Share this post


Link to post
Share on other sites
You have to choose between a static or dynamic container here. You're indexing like you have a 2-D grid of objects, but you're erasing elements like you have a dynamic array.

Either switch to an actual 2-D grid (e.g. array with nullable elements), or iterate over the vector's current elements, not the grid of elements you originally created.

Share this post


Link to post
Share on other sites

I changed the code, but I still get exception while shooting the last element 

 

for (vector<Invader*>::iterator invIter = g_Invaders.invaders->begin(); invIter != g_Invaders.invaders->end(); invIter++)
{
 
for (list<Bullet*>::iterator it = ship->Bullets->begin(); it != ship->Bullets->end(); it++)
{
float left = (*it)->Position.x - 5;
float top = (*it)->Position.y - 13;


if (CheckCollision(left, top, (*invIter)->AlienSprite->m_X, (*invIter)->AlienSprite->GetImage()->GetWidth() / 4
, (*invIter)->AlienSprite->m_Y, (*invIter)->AlienSprite->GetImage()->GetHeight() / 4))
{
Iw2DSetColour(0xff0000ff); // Set red
Iw2DDrawRect(CIwFVec2(left, top), CIwFVec2((*invIter)->AlienSprite->m_W, (*invIter)->AlienSprite->m_H)); // Draw red outline
invIter = g_Invaders.invaders->erase(invIter);
  
}
 
}
}

Share this post


Link to post
Share on other sites

 

Also worth pointing out if you change to using iterators you can't modify an stl container whilst iterating it, e.g. By inserting or deleting entries, unless you take special measures with your loop.

 

If you really need to, You should do something like:

for (iter i = container.begin(); i != container.end();)
{
        If (deleting)
                  i = container.erase(i);
        else
                 ++i;
}

 

You need to do it like this. Note how the increment is not done in the for loop... thing, but in the else clause. You get the new value of i EITHER by incrementing (++i), OR from the erase method (because you modified the vector and now the iterator held in i is somehow not valid so you cant just increment it).

Edited by Waterlimon

Share this post


Link to post
Share on other sites

I changed the code, but I still get exception while shooting the last element
[snip code]


Here you get the exception because when you reach the last element, you erase it properly, and that makes the invIter iterator invalid. Right after that you try calling invIter++, which is what causes your error.
Do what braindigitalis did.

Share this post


Link to post
Share on other sites

Also this won't fix your bug, but this is important in my opinion for readability. Instead of writing vector<Invader*>::iterator everywhere,  do this in a header file:

typedef std::vector<Invader*> InvaderVec
typedef InvaderVec::iterator InvaderVecIter

This means you can use the shorter, more readable InvaderVec and InvaderVecIter instead to declare loops etc.

 

?...and here endeth the lesson smile.png

Edited by braindigitalis

Share this post


Link to post
Share on other sites

Thanks.

 

I have done the following, but the game goes into forever loop

for (vector<Invader*>::iterator invIter = g_Invaders.invaders->begin(); invIter != g_Invaders.invaders->end(); )
{

for (list<Bullet*>::iterator it = ship->Bullets->begin(); it != ship->Bullets->end(); it++)
{
float left = (*it)->Position.x - 5;
float top = (*it)->Position.y - 13;


if (CheckCollision(left, top, (*invIter)->AlienSprite->m_X, (*invIter)->AlienSprite->GetImage()->GetWidth() / 4
, (*invIter)->AlienSprite->m_Y, (*invIter)->AlienSprite->GetImage()->GetHeight() / 4))
{
Iw2DSetColour(0xff0000ff); // Set red
Iw2DDrawRect(CIwFVec2(left, top), CIwFVec2((*invIter)->AlienSprite->m_W, (*invIter)->AlienSprite->m_H)); // Draw red outline
invIter = g_Invaders.invaders->erase(invIter);

}
else
{
invIter++;
}

}
}

Share this post


Link to post
Share on other sites

 

Thanks.

 

I have done the following, but the game goes into forever loop

for (vector<Invader*>::iterator invIter = g_Invaders.invaders->begin(); invIter != g_Invaders.invaders->end(); )
{

for (list<Bullet*>::iterator it = ship->Bullets->begin(); it != ship->Bullets->end(); it++)
{
float left = (*it)->Position.x - 5;
float top = (*it)->Position.y - 13;


if (CheckCollision(left, top, (*invIter)->AlienSprite->m_X, (*invIter)->AlienSprite->GetImage()->GetWidth() / 4
, (*invIter)->AlienSprite->m_Y, (*invIter)->AlienSprite->GetImage()->GetHeight() / 4))
{
Iw2DSetColour(0xff0000ff); // Set red
Iw2DDrawRect(CIwFVec2(left, top), CIwFVec2((*invIter)->AlienSprite->m_W, (*invIter)->AlienSprite->m_H)); // Draw red outline
invIter = g_Invaders.invaders->erase(invIter);

}
else
{
invIter++;
}

}
}

Your iterator porobably goes somewere out of range so:

nvIter != g_Invaders.invaders->end();

is never true.  You need to update nvIter per outer lool, not in inner loop.

for (vector<Invader*>::iterator invIter = g_Invaders.invaders->begin(); invIter != g_Invaders.invaders->end(); )
{
    bool isThisInvaderHit=false;
    for (list<Bullet*>::iterator it = ship->Bullets->begin(); it != ship->Bullets->end(); it++)
    {

        float left = (*it)->Position.x - 5;
        float top = (*it)->Position.y - 13;

        if (CheckCollision(left, top, (*invIter)->AlienSprite->m_X, (*invIter)->AlienSprite->GetImage()->GetWidth() / 4
        , (*invIter)->AlienSprite->m_Y, (*invIter)->AlienSprite->GetImage()->GetHeight() / 4))
        {
            Iw2DSetColour(0xff0000ff); // Set red
            Iw2DDrawRect(CIwFVec2(left, top), CIwFVec2((*invIter)->AlienSprite->m_W, (*invIter)->AlienSprite->m_H)); // Draw red outline
            isThisInvaderHit=true;   
        }
    }

    if(isThisInvaderHit)
    {
        invIter = g_Invaders.invaders->erase(invIter);
    }
    else
    {
        ++invIter;
    }

}

Edited by Mritke

Share this post


Link to post
Share on other sites

I tried to delete the bullet that collided with the invader, but still on the second iteration over the bullets, I get a nullable iterator, of course, because It was removed, then a crash happens when accesing it

 

 

if (CheckCollision(left, top, (*invIter)->AlienSprite->m_X, (*invIter)->AlienSprite->GetImage()->GetWidth() / 4
, (*invIter)->AlienSprite->m_Y, (*invIter)->AlienSprite->GetImage()->GetHeight() / 4))
{
Iw2DSetColour(0xff0000ff); // Set red
Iw2DDrawRect(CIwFVec2(left, top), CIwFVec2((*invIter)->AlienSprite->m_W, (*invIter)->AlienSprite->m_H)); // Draw red outline
isBulletsInvaderCollided = true;


Bullet *bullet = *it;
ship->Bullets->erase(it);
delete bullet;
}
else
{
it++;
}

Share this post


Link to post
Share on other sites

In rip-off's example look closely at the line where the iterator is deleted from the container. The iterator it is assigned the return value of erase(). That is essential. Your code doens't have it.

Edited by nobodynews

Share this post


Link to post
Share on other sites

Hi.

if you can't get it to work, skin the cat another way.

 

process you 2d grid the way you do but where you delete copy that index to a new vector and when your done in the 2d loops

remove by the other list clean and works

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this