Search code examples
c++classpolymorphism

I can't use subclass specific funcitons when I add them to a vector using parent class pointers


I am trying to create an "alien invaders" game by myself. In order to create enemies and player, I created a class called "entity" and made subclasses of it. Like Player, shootingEnemy, IdleEnemy. When coding I realised gathering them in a vector<Entity> would make my collision detection function much easier.

After searching on the internet I learned this is called "object slicing" and makes copies of of ony le base part of objects.

So the final version became this.

int main()
{
    int BoardWidth = 50;
    int BoardLength = 30;
    vector<Bullet> bullets;
    vector<Entity*> SpaceShips;
    
    setup(SpaceShips, BoardWidth, BoardLength); 

    double ElapsedTime = 0;
    int PreviousRoundSec = 0;
    int PreviousRoundQSec = 0;

    DrawGame(BoardWidth, BoardLength, SpaceShips, bullets);
    int IsGameOver = 0;
    auto start = chrono::steady_clock::now();
    while(!IsGameOver)
    {
        // Updates EverySecond
        if ((int)(ElapsedTime / 1000) > PreviousRoundSec)
        {
            PreviousRoundSec = (int)(ElapsedTime / 1000);
            

        }

        // Updates every quarter of a second
        if ((int)(ElapsedTime / 250) > PreviousRoundQSec)
        {
            PreviousRoundQSec = (int)(ElapsedTime / 250);
            
        }

        // To keep time
        auto end = chrono::steady_clock::now();
        ElapsedTime = chrono::duration_cast<chrono::milliseconds>(end - start).count();
    }
    if (IsGameOver == 1)
    {
        // conjualations
    }
    else if (IsGameOver == 2)
    {
        // GameOver
    }

    return 0;
}

But when I try use some subclass specific functions I get an compiler error saying 'CLASS "Entity" does not have any member called "shoot"'.

I am trying to practice classes and polymorphism so I do not even know this has a solution because compiler doesn't have any way of knowing which element of this vector belongs to which subclass.

Also this is my classes header page in case needed.

class Entity
{
public:

    int x;
    int y;

    int width;
    int length;

    int hp;
    bool shooting;

public:

    Entity(int x, int y, int width, int length, int hp, bool shooting): x(x), y(y), width(width), length(length), hp(hp), shooting(shooting) {}
};

class Bullet : public Entity
{
private:
    char dir;
    int ID;

public:
    Bullet(int x, int y, char GivenDir, int GivenID) : Entity(x, y, 1, 1, 1, false) { dir = GivenDir; ID = GivenID; }

    void Move();
    void IfHit(vector<Entity>& SpaceShips);
    void IfOut();

};

class Player : public Entity
{
private:
    char action = 'a';

public:
    Player(int x, int y, int hp) : Entity(x, y, 3, 2, hp, true) {}

    void GetAction();
    void Move();
    void Shoot(vector<Bullet>& bullets);
    bool IfHit(vector<Entity>& SpaceShips, vector<Bullet>& bullets);

    
};

class IdleEnemy : public Entity
{
public:
    
    IdleEnemy(int x, int y, int hp) : Entity(x, y, 3, 2, hp, false){}
    
    bool IfHit(Player* player, vector<Bullet> &bullets);
    void Move(char HordDir);
    
};

class ShootingEnemy : public Entity
{


public:
    ShootingEnemy(int x, int y, int hp) : Entity(x, y, 3, 2, hp, true) {}

    void Shoot(vector<Bullet> &bullets);
    bool IfHit(Player* player, vector<Bullet> &bullets);
    void Move(char HordDir);
};

Solution

  • You need to check runtime polymorphism in C++. Let's check it out how can you do that. First of all, you need to change your Entity class interface. You need to add virtual or pure virtual functions. I have added pure virtual function;

    class Entity
    {
    public:
    
        int x;
        int y;
    
        int width;
        int length;
    
        int hp;
        bool shooting;
    
    public:
    
        Entity(int x, int y, int width, int length, int hp, bool shooting) : x(x), y(y), width(width), length(length), hp(hp), shooting(shooting) {}
    
        void virtual Move() = 0; // pure virtual function
        void virtual IfHit() = 0; // pure virtual function 
    };
    

    Virtual functions are overridable functions. Also, they have implementations but when we are talking about pure virtual functions they only provide an interface for the class. You need to override that function in your derived class. When you are implementing your derived class you need to do like this,

    class Bullet : public Entity
    {
    private:
        char dir;
        int ID;
    
    public:
        Bullet(int x, int y, char GivenDir, int GivenID) : Entity(x, y, 1, 1, 1, false) { dir = GivenDir; ID = GivenID; }
    
        void Move()override;
        void IfHit();
        void IfOut();
    
    };
    
    class Player : public Entity
    {
    private:
        char action = 'a';
    
    public:
        Player(int x, int y, int hp) : Entity(x, y, 3, 2, hp, true) {}
    
        void GetAction();
        void Move();
        void Shoot(vector<Bullet>& bullets);
        void IfHit()override {//code};
    
    
    };
    
    class IdleEnemy : public Entity
    {
    public:
    
        IdleEnemy(int x, int y, int hp) : Entity(x, y, 3, 2, hp, false) {}
    
        void IfHit()override;
        void Move()override;
    
    };
    
    class ShootingEnemy : public Entity
    {
    
    
    public:
        ShootingEnemy(int x, int y, int hp) : Entity(x, y, 3, 2, hp, true) {}
    
        void Shoot(vector<Bullet>& bullets);
        void IfHit()override;
        void Move()override;
    };
    

    These functions can be implemented either inline or in a source file. Also, there is an important point of these functions is the return value, function signature, and names' must be identical unless you do not use covariant return type.

    As seen in the derived classes some of the functions are not common. I know your question how can I use that :) As mentioned ttemple in the comments you need to use dynamic_cast operator.

    int main()
    {
    
        Entity* ptr = new ShootingEnemy{ 1,2,4 };
        ptr->Move();
        ptr->IfHit();
    
        if (auto SE = dynamic_cast<ShootingEnemy*>(ptr))
            SE->Shoot(...);
    }
    

    dynamic_cast operator is a runtime conversion operator. It converts the type of base class pointer to the derived class. It is called downcasting. Also, it checks that base class pointer points to the target derived class. If the dynamic_cast operation is completed with fail then it returns null and if statement becomes fail. Via that way, you can use runtime polymorphism and class member functions.

    By the way, avoid object slicing as possible. You are losing derived class properties.

    To better understanding please refer classes dynamic_cast