简体   繁体   中英

Killing the invaders doesn't work in C++

I know that in order to kill invaders in C++, I need to make a collider. However, nothing will ever kill the invaders in that game. Here's the code in the header:

bool DoCollision(float Xbpos, float Ybpos, int BulWidth, int BulHeight, float Xipos, float Yipos, int InvWidth, int InvHeight);

This is the function I'm initializing:

bool Game::DoCollision(float Xbpos, float Ybpos, int BulWidth, int BulHeight, float Xipos, float Yipos, int InvWidth, int InvHeight) {
if (Xbpos+BulWidth < Xipos || Xbpos > Xipos+InvWidth) return false;
if (Ybpos+BulHeight < Yipos || Ybpos > Yipos+InvHeight) return false;

return true;
}

And this is what happens if somebody presses the space key:

if (code == 57) { //Space
    myKeyInvader.MeBullet.Active = true;
    myKeyInvader.MeBullet.Xpos = myKeyInvader.Xpos + 10;
    myKeyInvader.MeBullet.Ypos = myKeyInvader.Ypos - 10;
    myKeyInvader.MeBullet.yvuel = 0.2;
    myKeyInvader.MeBullet.BulletP->CopyTo(m_Screen,myKeyInvader.Xpos,myKeyInvader.Ypos);

    if (DoCollision(Invaders[counter].MyBullet.Xbpos,Invaders[counter].MyBullet.Ybpos,Invaders[counter].MyBullet.BulWidth,
    Invaders[counter].MyBullet.BulHeight,Invaders[counter].Xipos,Invaders[counter].Yipos,Invaders[counter].InvWidth,Invaders[counter].InvHeight)) {
        //myKeyInvader.Ypos = 100;
        Invaders[counter].Active = false;
        printf("Collide!\n");
    }
}

Does anybody know what's going wrong?

The problem isn't C++. The problem is how you are using it. The only way you'll get a kill with your code as written is if the invader is right on top of you. But that's too late. The alien invader has already killed you.

What you need to do is make those bullets into objects that you propagate over time, just like your invaders are objects that you propagate over time. The response to the user pressing a space key should be to add a new instance of a bullet to the set of active bullets. Each of those active bullets has a position that changes with time. On each time step, you should advance the states of the active invaders per the rules that dictate how invaders move and advance the states of the active bullets per the rules that dictate how bullets move. Remove bullets when they reach the top of the screen, and if an alien invader reaches the bottom of the screen, game over.

After propagating, removing off-screen bullets, and checking for game over, you want to check for collisions between each of the N bullets with each of the M invaders. When a collision is detected, remove the bullet from the set of active bullets and delete the alien invader from the set of active invaders. And of course you'll want some nifty graphics to show the user that another alien bit the dust.

Aside: Being an NxM problem, this check might be the biggest drain on CPU usage. You can speed this up with some simple heuristics.

You could manage the collections of alien invaders and bullets yourself, carefully using new and delete so as to prevent your invaders and bullets from killing your program with a memory leak. You don't have to do this. C++ gives you some nifty tools to manage these collections. Use one of the C++ standard library collections instead of rolling your own collection. For example, std::vector<AlienInvader> invaders; or std::list<AlienInvader> invaders , and the same for bullets. You'll be deleting from the middle a lot, which suggests that std::list or std::deque might be more appropriate than std::vector here.

You test the collision for the fired item just when they are created

Shouldn't be the test collision done in the main loop for each existing item at each frame ?

Don't worry, C++ has got all you need to kill invaders :)))

It's not easy to give advice based on so little code, but here the only logical error seems to be you test for collision only when space is pressed; you should test for it in an outside loop probably:

if (code == 57) { //Space
    myKeyInvader.MeBullet.Active = true;
    myKeyInvader.MeBullet.Xpos = myKeyInvader.Xpos + 10;
    myKeyInvader.MeBullet.Ypos = myKeyInvader.Ypos - 10;
    myKeyInvader.MeBullet.yvuel = 0.2;
    myKeyInvader.MeBullet.BulletP->CopyTo(m_Screen,myKeyInvader.Xpos,myKeyInvader.Ypos);
}

From a logical point of view, pressing Space should fire a bullet: the starting position for the bullet is set, and so is its speed on the Y axis (so that it goes up).

The code that check for collision should go outside of this if block. In fact, this block of code is executed only if you're still pressing space -that is: still firing -. Should collision be checked only if you're "still firing"? Do the fact that you fired a bullet and started waiting for it to destroy the invader interfere in some way with the fact that this bullet can reach the invader and, indeed, destroy it? Of course not!

if (DoCollision(Invaders[counter].MyBullet.Xbpos,Invaders[counter].MyBullet.Ybpos,Invaders[counter].MyBullet.BulWidth,
    Invaders[counter].MyBullet.BulHeight,Invaders[counter].Xipos,Invaders[counter].Yipos,Invaders[counter].InvWidth,Invaders[counter].InvHeight)) {
    //myKeyInvader.Ypos = 100;
    Invaders[counter].Active = false;
    printf("Collide!\n");
}

You want collision to be checked in an outside loop, the same that probably also contains the checks for key presses. In this way, even if you're just looking at the screen and waiting, the program keeps testing the condition and, when it's fulfilled, code associated with the event of collision is executed (that is: an invader is "inactivated").

You say //Space , is that what it is or should it be 32 (if ASCII) instead of 57? Does the program flow into the if==57 block?

Your code looks fine, but you need two loops around the collision checker: one for checking all invaders (not just one of them) and another one to check at every bullet position along its trajectory, not just the moment when it leaves the gun.

I will assume we have an auxiliary function that moves the bullet and returns whether it is still inside the screen:

bool BulletIsInScreen();

Then we can write the loops:

if (code == 57) { // Space
  while (BulletIsInScreen()) {
    for (size_t i = 0; i < counter; ++i) { // counter is the number of invaders,
                                           // according to your comment to your own answer
      myKeyInvader.MeBullet.Active = true;
      myKeyInvader.MeBullet.Xpos = myKeyInvader.Xpos + 10;
      myKeyInvader.MeBullet.Ypos = myKeyInvader.Ypos - 10;
      myKeyInvader.MeBullet.yvuel = 0.2;
      myKeyInvader.MeBullet.BulletP->CopyTo(m_Screen,myKeyInvader.Xpos,myKeyInvader.Ypos);

      if (DoCollision(Invaders[i].MyBullet.Xbpos, Invaders[i].MyBullet.Ybpos,
                      Invaders[i].MyBullet.BulWidth, Invaders[i].MyBullet.BulHeight,
                      Invaders[i].Xipos, Invaders[i].Yipos,
                      Invaders[i].InvWidth, Invaders[i].InvHeight)) {
        //myKeyInvader.Ypos = 100;
        Invaders[i].Active = false;
        printf("Collide!\n");
      }
    }
  }
}

Now this should work as expected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM