cyborgzombieninjapirate


C++ is Not Always as It Seems

Posted on 11.01.2010 07:46 pm

I've been programming a little side project over the holidays in C++ using a multimedia library called SFML. It's worked out pretty well, simple to learn and use.

C++ is an interesting language. I learned the basics in a few weeks and got to pointers and dynamic memory. After a little wresting with it I think I have an OK understanding of it, well enough to mess around in and not shoot my legs off.

I can safely say that C++ is a language that is kind to the users that know what they are doing but a cruel beast to those who try to wing it.

A small example is from my little project, it's a game and in this game I need to handle bullets coming from a shooter and handle what happens if those bullets hit a target and what if the target is dead before the bullet hits etc.

The bullets are homing bullets and have a set target and follow that target, but they are dynamically added since we don't know the target until the bullet fires. So I have a std::list of pointers to a Bullet class, and to iterate through that class I have to do this.

for(std::list<Bullets*>::iterator b = tHandler.getBulletList().begin(); 
b != tHandler.getBulletList().end(); b++)
{
    // Insert magic here
}

I just love list for loops, they are actually pretty simple but look so damn complicated. I even thought I knew how to work with list of pointers to a class. But oh no, C++ struck me down. "You fool, why are you so stupid!".

The next step was to check if the target was dead. The bullet class has a method to check if the target is dead or not. So I would just call it, check its value and if it's dead, then I would remove the bullet from the list.

Sounds simple right? Well for those who know C++, its very simple. But for little old me, not so.

if(b->targetDead()) 

// error: request for member `targetDead' in
*(&b)->std::_List_iterator<_Tp>::operator-> 
[with _Tp = Bullets*]()', 
which is of non-class type `Bullets*'|

Ahh of course.... yes its a pointer.

if(*b->targetDead()) 

// error: request for member `targetDead' in
*(&b)->std::_List_iterator<_Tp>::operator-> 
[with _Tp = Bullets*]()', 
which is of non-class type `Bullets*'|

Uhh wait what? Hmmm..... I actually had to look up examples of people calling pointers to classes in lists or vectors, (and there aren't that many btw) and I found this.

if((*b)->targetDead())

Oh of course. I need to be explicit to what the pointer is referring to.

Next was to create the logic within the conditional, first I had to delete the pointer so no memory would leak. Then I could erase the entity from the list and then skip to the next iteration.

if((*b)->targetDead())
{
    delete *b;
    b = tHandler.getBulletList().erase(b);
    continue;
}

I had something like this and it worked, big smile on my sleep deprived face when I saw working. But once in a while I would get a crash. I had no idea what was happening, so I joined the great folks of ##c++ on Freenode and pasted this example. I got a quick reply that if after an erase, the list was at the end, I could not loop trough it and could result in a crash. So I was prompted with this fix.

if((*b)->targetDead())
{
    delete *b;
    b = tHandler.getBulletList().erase(b);
    if(b == tHandler.getBulletList().end())
    {
        break;
    }
    continue;
}

I probably know more now than I did before and hopefully that will help me when I'm stranded in the claws of C++ waiting to be squished.

0 2    Like it or hate it?  -  Comment (5)


Andri Jan

0 1  / Posted on 11.01.2010 10:23 pm

I've been wondering about stuff like this. How expensive are these calculations when you have, let's say 16 players at the same time all shooting constantly?

Ólafur Waage

0 0  / Posted on 11.01.2010 11:20 pm

It all depends on the calculation done.

We have the loops for all movement of players. This is usually arithmetic of X and Y variables (adding or subtracting from them) but can be simple trigonometry as well (if we have curved movement)

Then there is the loop of all bullet movement, this is the same as player movement.

Then we have hit checks, now this can get expensive if you want good hit detection, an example of simple hit detection code is here (http://www.sfml-dev.org/wiki/en/sources/simple_collision_detection), currenly I'm using CircleTest() but there are BoundingBoxTest() and PixelPerfectTest(). CircleTest() is the fastest and works ok with square items in my case.

Kranar

0 2  / Posted on 14.01.2010 03:22 pm

delete *b;
b = tHandler.getBulletList().erase(b);
continue;

This is not the correct thing to do within a for loop. If you're going to mutate the list itself rather than just the members of the list, then you need to use a while loop instead of a for loop.

Your for loop will skip over an element every time you iterate over a dead target.

It should instead look like this:

std::list::iterator b = tHandler.getBulletList().begin();
while(b != tHandler.getBulletList().end())
{
Bullets* bullet = *b;
if(goingToDeleteBullet)
{
b = tHandler.getBulletList().erase(b);
delete bullet;
} else {
++b;
}
}

Otherwise what happens is when you erase the bullet, say the second bullet, the iterator will point to the third bullet, but then you invoke continue, which will make the iterator point to the fourth bullet and continue, skipping over the third bullet.

Ólafur Waage

0 0  / Posted on 15.01.2010 12:32 pm

@Kranar thanks for the tip. I used your method and this is now how every if(goingtodelete) condition is. I also changed the for loops into while loops.

if(goingToDeleteBullet)
{
delete *b;
b = tHandler.getBulletList().erase(b);
continue;
}

And then at the bottom of the while loop I have ++b;

This fixed the flicker every bullet nr 3 got when bullet nr 2 was deleted. So thanks a lot for this :)

Petr

0 1  / Posted on 09.05.2010 08:20 am

Thanks for writing this post, I was stuck exactly in the first issue - looping over a list of pointers to class -- getting the error ’, which is of non-class type ‘Type*’. I tried to google it and here it is -- spared me maybe quite long clueless wondering what the heck is wrong.

I agree with your opinion on C++. It's very logical and quite easy to learn to use its power but you must always know what you're doing.

(Used for gravatar only, never displayed)

What is 3 + 9


Memory allocated for your request: 194.55 Kb
Process time: 0.018168 seconds