Search code examples
c++sfml

Getting player position, cannot pass object instance over due to include loop


I am currently working on a clone of Space Invaders in C++ and SFML and seem to be having a problem with the players position.

Currently, when the invaders shoot, the hit-box of the player continues to be the start position of the player recorded as 900, 500. This hit-box does not move the player. I am unsure of why and how to fix it. I know I cannot include game.h into invaders.h as game.h includes invaders.h hence causing an include loop. I need to take the player instance that is created in game.h, and pass that on to invaders.h for the hit-box of the player ton be passed along as well.

This is the code that relates to the invaders shots and the players position.

Game.h

#include "Player.h"
#include "invaders.h"
class Game
{
private:
Player* player;
vector<Invaders*> vInvaders;
public:
void updateInputs();
};

Game.cpp

#include "Game.h"
void Game::updateInputs()
{
    //Updates player movement.
    player->updateInputs();

    //Creating bullets.
    if ((Keyboard::isKeyPressed(Keyboard::Space)) && (this->player->bCanAttack()))
    {
        this->vBullets.push_back(new Bullet(this->textures["BULLET"], this->player->getPos().x, this->player->getPos().y, 0.f, -1.f, 5.f));
        SoundEngine::playShot();
    }
//Move player.
    if (Keyboard::isKeyPressed(Keyboard::Left))
    {
        move(-1.f, 0);
        getBounds();
        //cout << player->getBounds().left << " " << player->getBounds().top << endl;
    }
    if (Keyboard::isKeyPressed(Keyboard::Right))
    {
        move(1.f, 0);
        getBounds();
        //cout << player->getBounds().left << " " << player->getBounds().top << endl;
    }
}

Player.h

class Player
{
public:
const FloatRect getBounds() const;
void move(const float kfDirX, const float kfDirY);
};

Player.cpp

#include "Player.h"
const FloatRect Player::getBounds() const
{
    return this->sprite.getGlobalBounds(); //Returns the bounding box of player.
}

void Player::move(const float kfDirX, const float kfDirY)
{
    this->sprite.move(this->fSpeed * kfDirX, 0); //Moves player across screen.
}

Invaders.h

#include "Player.h" //It is not needed, but doesn't work without it.
class Invaders
{
private:
Player player; //It is not needed, but does't work without it. Need the instance from Game.h, don't need to make a new instance here.
public:
void updateBullets();
};

Invaders.cpp

#include "Invaders.h"
void Invaders::updateBullets()
{
    unsigned int iCounter = 0;
    for (auto* invaderBullet : this->vInvaderBullets)
    {
        invaderBullet->update();

        for (size_t k = 0; k < this->vInvaderBullets.size(); k++)
        {
            //THIS DOES NOT WORK!!!!
            if (this->vInvaderBullets[k]->getBounds().intersects(player.getBounds()))
            {
                cout << player.getBounds().left << " " << player.getBounds().top << endl;
                this->vInvaderBullets.erase(this->vInvaderBullets.begin() + k);
                this->player.isDead(true);
                Variables::iLives--;
            }
            else
            {
                this->player.isDead(false);
            }
        }

        //Bullet culling at bottom of screen.
        if ((invaderBullet->getBounds().top + invaderBullet->getBounds().height) > 1100.f)
        {
            //std::cout << this->invaderBullets.size() << std::endl;
            delete this->vInvaderBullets.at(iCounter);
            this->vInvaderBullets.erase(this->vInvaderBullets.begin() + iCounter);
            iCounter--;
            //Check to see if bullets are deleted.
            //std::cout << this->invaderBullets.size() << std::endl;
        }
        iCounter++;

        for (size_t k = 0; k < this->vInvaderBullets.size(); k++)
        {
            if (this->vInvaderBullets[k]->getBounds().intersects(this->barrier.getBounds(1)))
            {
                SoundEngine::playBarrierHit();
                this->vInvaderBullets.erase(this->vInvaderBullets.begin() + k);
                this->barrier.barrierHit(1);
            }
            else if (this->vInvaderBullets[k]->getBounds().intersects(this->barrier.getBounds(2)))
            {
                SoundEngine::playBarrierHit();
                this->vInvaderBullets.erase(this->vInvaderBullets.begin() + k);
                this->barrier.barrierHit(2);
            }
            else if (this->vInvaderBullets[k]->getBounds().intersects(this->barrier.getBounds(3)))
            {
                SoundEngine::playBarrierHit();
                this->vInvaderBullets.erase(this->vInvaderBullets.begin() + k);
                this->barrier.barrierHit(3);
            }
            else if (this->vInvaderBullets[k]->getBounds().intersects(this->barrier.getBounds(4)))
            {
                SoundEngine::playBarrierHit();
                this->vInvaderBullets.erase(this->vInvaderBullets.begin() + k);
                this->barrier.barrierHit(4);
            }
        }
    }
}

Solution

  • Putting a member of type Player in Invaders means it's a separate object, and probably not the same Player used by Game. So most likely player movement happens to the Game's Player, but the invaders are targeting their own distinct Player which hasn't moved.

    Since Invaders::updateBullets() needs the Player object, several options are:

    • Pass a Player& or Game& to the updateBullets function.
    • Make Game a singleton, with a public way to get the Game object and therefore its Player object, from any context.
    • Keep a Player* or Game* pointer in class Invaders - but if those objects could ever be destroyed before Invaders objects, dealing with that will be tricky. And this may be a bit "wasteful".

    And in general, remember a class definition isn't needed if you're just dealing with pointers or references to that class, and this can help reduce the needed #include directives. For example, you could change Game.h and Game.cpp:

    // Game.h
    #ifndef GAME_H
    #define GAME_H
    
    #include <vector>
    
    class Player;
    class Invaders;
    
    class Game
    {
    private:
        Player* player;
        std::vector<Invaders*> vInvaders;
    public:
        void updateInputs();
    };
    
    #endif
    
    // Game.cpp
    #include "Game.h"
    #include "Player.h"
    #include "Invaders.h"
    
    // ...