简体   繁体   中英

C++ Polymorphism with Lists issue

First of all thank you for your time and apologize for the long of this post but i couldn't find any other way to make it shorter and also for me english! if you don't understand something, just ask ^^. Hope you can find the error because is driving me crazy.

I'm currently learning DirectX 11 and i'm making This Little Game from this website but applying OOP and DirectX 11 instead of 9 just taking certain things from that project.

Ok, now that you have a little context here is the problem.

I made an abstract class called GameObject which encapsulates all the functionalities concerning to rendering, like storing the image(s), animation, transition between frames, etc. This GameObject class is used to define every other object that will interact in my game.

GameObject Class

////////////////////////////////////////////////////////////////////////////////
// Filename: GameObject.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _GAME_OBJECT_H_
#define _GAME_OBJECT_H_

///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "Sprite.h"
#include "InputHandler.h"
#include "Timer.h"

class GameObject
{
public:
    GameObject();
    GameObject(const GameObject& other);
    ~GameObject();

    virtual bool Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen) = 0;
    bool Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen, WCHAR* spriteFileName, Bitmap::DimensionType bitmap, Bitmap::DimensionType sprite, int numberOfFramesAcross, int initialFrame, bool useTimer);
    virtual void Shutdown();
    virtual bool Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX wordMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix);

    void Move();
    void Move(const D3DXVECTOR2 vector);

    virtual void Frame(const InputHandler::ControlsType& controls);
    void SortFrameArray(const int* framesOrder, int size);

    void SetPosition(const POINT& position);
    const POINT GetPosition();

    void SetVelocity(const D3DXVECTOR2& velocity);
    const D3DXVECTOR2 GetVelocity();

    void SetStatus(const bool status);
    bool GetStatus();

    float GetMovementDelayTime();
    void ResetMovementDelayTime();

    float GetAnimationDelayTime();
    void ResetAnimationDelayTime();

    //Both of this objects i think i'll remove them from this class. I don't think they belong here.
    ID3D11Device* GetDevice();
    HWND GetHWND();

    Sprite* GetSprite();

protected:
    ID3D11Device* m_device;
    HWND m_hwnd;
    Sprite* m_Sprite;
    Timer* m_Timer;
    POINT m_position;
    D3DXVECTOR2 m_velocity;
    bool m_active;
    float m_movementDelay;
    float m_animationDelay;
};
#endif

Cpp

////////////////////////////////////////////////////////////////////////////////
// Filename: GameObject.cpp
////////////////////////////////////////////////////////////////////////////////
#include "GameObject.h"

GameObject::GameObject()
{
    this->m_Sprite = nullptr;
    this->m_Timer = nullptr;
    this->m_movementDelay = 0.0f;
    this->m_animationDelay = 0.0f;
}

GameObject::GameObject(const GameObject& other)
{
}

GameObject::~GameObject()
{
}

bool GameObject::Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen, WCHAR* spriteFileName, Bitmap::DimensionType bitmap, Bitmap::DimensionType sprite, int numberOfFramesAcross, int initialFrame, bool useTimer)
{
    bool result;

    this->m_device = device;
    this->m_hwnd = hwnd;

    this->m_Sprite = new Sprite();
    if (!this->m_Sprite)
    {
        return false;
    }

    result = this->m_Sprite->Initialize(device, hwnd, screen, spriteFileName, bitmap, sprite, numberOfFramesAcross, initialFrame);
    if (!result)
    {
        return false;
    }

    if (useTimer)
    {
        this->m_Timer = new Timer();
        if (!this->m_Timer)
        {
            return false;
        }

        result = this->m_Timer->Initialize();
        if (!result)
        {
            return false;
        }
    }
    return true;
}

void GameObject::Shutdown()
{
    SAFE_SHUTDOWN(this->m_Sprite);
    SAFE_DELETE(this->m_Timer);
}

bool GameObject::Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX wordMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
    return this->m_Sprite->Render(deviceContext, this->m_position, wordMatrix, viewMatrix, projectionMatrix);
}

void GameObject::Move()
{
    this->m_position.x += this->m_velocity.x;
    this->m_position.y += this->m_velocity.y;
}

void GameObject::Move(const D3DXVECTOR2 vector)
{
    this->m_position.x += vector.x;
    this->m_position.y += vector.y;
}

void GameObject::Frame(const InputHandler::ControlsType& controls)
{
    if (this->m_Timer)
    {
        this->m_Timer->Frame();

        this->m_movementDelay += this->m_Timer->GetTime();
        this->m_animationDelay += this->m_Timer->GetTime();
    }
}

void GameObject::SortFrameArray(const int* framesOrder, int size)
{
    this->m_Sprite->SortFrameArray(framesOrder, size);
}

void GameObject::SetPosition(const POINT& position)
{
    this->m_position = position;
}

const POINT GameObject::GetPosition()
{
    return this->m_position;
}

void GameObject::SetVelocity(const D3DXVECTOR2& velocity)
{
    this->m_velocity = velocity;
}

const D3DXVECTOR2 GameObject::GetVelocity()
{
    return this->m_velocity;
}

void GameObject::SetStatus(const bool status)
{
    this->m_active = status;
}

bool GameObject::GetStatus()
{
    return this->m_active;
}

Sprite* GameObject::GetSprite()
{
    return this->m_Sprite;
}

float GameObject::GetAnimationDelayTime()
{
    return this->m_animationDelay;
}

void GameObject::ResetMovementDelayTime()
{
    this->m_movementDelay = 0.0f;
}

float GameObject::GetMovementDelayTime()
{
    return this->m_animationDelay;
}

void GameObject::ResetAnimationDelayTime()
{
    this->m_animationDelay = 0.0f;
}

ID3D11Device* GameObject::GetDevice()
{
    return this->m_device;
}

HWND GameObject::GetHWND()
{
    return this->m_hwnd;
}

And i made the derived class Fighter which represents my Spaceship and has a FighterFlame which i think is not relevant to the problem, and a list of pointers to pointers of Bullet ( m_Bullets ) which will be the bullets coming out from the Ship.

////////////////////////////////////////////////////////////////////////////////
// Filename: Fighter.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _FIGHTER_H_
#define _FIGHTER_H_

//////////////
// INCLUDES //
//////////////
#include <list>

///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "GameObject.h"
#include "Bullet.h"
#include "FighterFlame.h"

class Fighter : public GameObject
{
public:
    Fighter();
    Fighter(const Fighter& other);
    ~Fighter();

    virtual bool Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen) override;
    virtual void Shutdown();
    virtual bool Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX wordMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix) override;

    virtual void Frame(const InputHandler::ControlsType& controls) override;

private:
    void GenerateTriBullet();
    void ValidateBulletsBounds();

private:
    int m_life;
    int m_lives;

    FighterFlame* m_FighterFlame;
    std::list<Bullet**> m_Bullets;

    const int SHIP_SPEED = 3;
    const float MOVEMENT_DELAY = 16.0f;
    const float ANIMATION_DELAY = 20.0f;
    const float SHOOT_DELAY = 30.0f;
};
#endif

Cpp

////////////////////////////////////////////////////////////////////////////////
// Filename: Fighter.cpp
////////////////////////////////////////////////////////////////////////////////
#include "Fighter.h"

Fighter::Fighter() : GameObject()
{
    this->m_life = 100;
    this->m_lives = 3;

    this->m_FighterFlame = nullptr;
}

Fighter::Fighter(const Fighter& other)
{
}

Fighter::~Fighter()
{
}

bool Fighter::Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen)
{
    bool result;

    this->m_life = 100;
    this->m_lives = 3;

    result = GameObject::Initialize(device, hwnd, screen, L"Fighter.dds", Bitmap::DimensionType{ 1152, 216 }, Bitmap::DimensionType{ 144, 108 }, 8, 7, true);
    if (!result)
    {
        MessageBox(hwnd, L"Could not initialize Fighter", L"Error", MB_OK);
        return false;
    }

    this->m_position = POINT{ 0, 0 };

    int order[16] = { 7, 6, 5, 4, 3, 2, 1, 0, 8, 9, 10, 11, 12, 13, 14, 15 };
    GameObject::SortFrameArray(order, 16);

    this->m_FighterFlame = new FighterFlame();
    if (!this->m_FighterFlame)
    {
        return false;
    }

    result = this->m_FighterFlame->Initialize(device, hwnd, screen);
    if (!result)
    {
        MessageBox(hwnd, L"Could not initialize FighterFlame", L"Error", MB_OK);
        return false;
    }

    return true;
}

bool Fighter::Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX wordMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
    bool result;

    result = GameObject::Render(deviceContext, wordMatrix, viewMatrix, projectionMatrix);
    if (!result)
    {
        return false;
    }

    result = this->m_FighterFlame->Render(deviceContext, wordMatrix, viewMatrix, projectionMatrix);
    if (!result)
    {
        return false;
    }

    for (Bullet** bullet : this->m_Bullets)
    {
        if (bullet)
        {
            result = (*bullet)->Render(deviceContext, wordMatrix, viewMatrix, projectionMatrix);
            if (!result)
            {
                return false;
            }
        }
    }

    return true;
}

void Fighter::Shutdown()
{
    GameObject::Shutdown();
    SAFE_SHUTDOWN(this->m_FighterFlame);

    for (Bullet** bullet : this->m_Bullets)
    {
        SAFE_SHUTDOWN(*bullet);
    }
    this->m_Bullets.clear();
}

void Fighter::Frame(const InputHandler::ControlsType& controls)
{
    GameObject::Frame(controls);
    this->m_FighterFlame->SetPosition(POINT{ this->m_position.x - 26, this->m_position.y + 47});
    this->m_FighterFlame->Frame(controls);

    for (Bullet** bullet : this->m_Bullets)
    {
        (*bullet)->Frame(controls);
    }

    if (GameObject::GetMovementDelayTime() > MOVEMENT_DELAY)
    {
        if (controls.up ^ controls.down)
        {
            if (controls.up)
            {
                if (GameObject::GetPosition().y > 0)
                {
                    GameObject::Move(D3DXVECTOR2(0, -SHIP_SPEED));
                }

                if (GameObject::GetAnimationDelayTime() > ANIMATION_DELAY)
                {
                    GameObject::GetSprite()->IncrementFrame();
                    GameObject::ResetAnimationDelayTime();
                }
            }
            else if (controls.down)
            {
                if (GameObject::GetPosition().y < (GameObject::GetSprite()->GetBitmap()->GetScreenDimensions().height - GameObject::GetSprite()->GetBitmap()->GetBitmapDimensions().height))
                {
                    GameObject::Move(D3DXVECTOR2(0, SHIP_SPEED));
                }
                if (GameObject::GetAnimationDelayTime() > ANIMATION_DELAY)
                {
                    GameObject::GetSprite()->DecrementFrame();
                    GameObject::ResetAnimationDelayTime();
                }
            }
        }
        else
        {
            if (GameObject::GetSprite()->GetCurrentFrame() > (GameObject::GetSprite()->GetAmountOfFrames() / 2))
            {
                if (GameObject::GetAnimationDelayTime() > ANIMATION_DELAY)
                {
                    GameObject::GetSprite()->DecrementFrame();
                    GameObject::ResetAnimationDelayTime();
                }
            }
            if (GameObject::GetSprite()->GetCurrentFrame() < (GameObject::GetSprite()->GetAmountOfFrames() / 2))
            {
                if (GameObject::GetAnimationDelayTime() > ANIMATION_DELAY)
                {
                    GameObject::GetSprite()->IncrementFrame();
                    GameObject::ResetAnimationDelayTime();
                }
            }
        }
        if (controls.right ^ controls.left)
        {
            if (controls.right)
            {
                if (GameObject::GetPosition().x < (GameObject::GetSprite()->GetBitmap()->GetScreenDimensions().width - GameObject::GetSprite()->GetBitmap()->GetBitmapDimensions().width))
                {
                    GameObject::Move(D3DXVECTOR2(SHIP_SPEED, 0));
                }
            }
            else if (controls.left)
            {
                if (GameObject::GetPosition().x > 0)
                {
                    GameObject::Move(D3DXVECTOR2(-SHIP_SPEED, 0));
                }
            }
        }
        GameObject::ResetMovementDelayTime();
    }

    if (controls.spaceBar)
    {
        Fighter::GenerateTriBullet();
    }
    Fighter::ValidateBulletsBounds();
}

void Fighter::GenerateTriBullet()
{
    Bullet* upBullet = new Bullet();
    upBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
    upBullet->SetVelocity(D3DXVECTOR2(20, 2));
    upBullet->SetPosition(GameObject::GetPosition());
    upBullet->Move();
    this->m_Bullets.push_back(&upBullet);

    Bullet* middleBullet = new Bullet();
    middleBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
    middleBullet->SetVelocity(D3DXVECTOR2(20, 0));
    middleBullet->SetPosition(GameObject::GetPosition());
    middleBullet->Move();
    this->m_Bullets.push_back(&middleBullet);

    Bullet* downBullet = new Bullet();
    downBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
    downBullet->SetVelocity(D3DXVECTOR2(20, -2));
    downBullet->SetPosition(GameObject::GetPosition());
    downBullet->Move();
    this->m_Bullets.push_back(&downBullet);
}

void Fighter::ValidateBulletsBounds()
{
    for (std::list<Bullet**>::iterator it = this->m_Bullets.begin(); it != this->m_Bullets.end(); it++)
    {
        if ((*(*(&(it)._Ptr->_Myval)))->GetPosition().x > GameObject::GetSprite()->GetBitmap()->GetScreenDimensions().width)
        {
            SAFE_SHUTDOWN(**it);
            this->m_Bullets.erase(it);
        }
    }
}

And finally the problematic one, The Bullet class who is also derived from GameObject and will represent the bullets that the spaceship can shoot.

////////////////////////////////////////////////////////////////////////////////
// Filename: Bullet.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _BULLET_H_
#define _BULLET_H_

///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "GameObject.h"

class Bullet : public GameObject
{
public:
    Bullet();
    Bullet(const Bullet& other);
    ~Bullet();

    virtual bool Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen) override;

    virtual void Frame(const InputHandler::ControlsType& controls) override;

private:
    const float MOVEMENT_DELAY = 16.0f;
};
#endif

Cpp

////////////////////////////////////////////////////////////////////////////////
// Filename: Bullet.cpp
////////////////////////////////////////////////////////////////////////////////
#include "Bullet.h"

Bullet::Bullet() : GameObject()
{
}

Bullet::Bullet(const Bullet& other)
{
}

Bullet::~Bullet()
{
}

bool Bullet::Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen)
{
    bool result;

    result = GameObject::Initialize(device, hwnd, screen, L"Bullet.dds", Bitmap::DimensionType{ 18, 3 }, Bitmap::DimensionType{ 18, 3 }, 1, 0, true);
    if (!result)
    {
        return false;
    }

    return true;
}

void Bullet::Frame(const InputHandler::ControlsType& controls)
{
    GameObject::Frame(controls);
    if (GameObject::GetMovementDelayTime() > MOVEMENT_DELAY)
    {
        GameObject::Move();
    }
}

And the problem:

When the Gameloop is running and i press space bar, this occurs

// this if is from Fighter::Frame
if (controls.spaceBar)
{
    Fighter::GenerateTriBullet();
}
Fighter::ValidateBulletsBounds();

It enters to the GenerateTriBullet method, which stores 3 bullets on the m_Bullets list.

void Fighter::GenerateTriBullet()
{
    Bullet* upBullet = new Bullet();
    upBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
    upBullet->SetVelocity(D3DXVECTOR2(20, 2));
    upBullet->SetPosition(GameObject::GetPosition());
    upBullet->Move();
    this->m_Bullets.push_back(&upBullet);

    Bullet* middleBullet = new Bullet();
    middleBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
    middleBullet->SetVelocity(D3DXVECTOR2(20, 0));
    middleBullet->SetPosition(GameObject::GetPosition());
    middleBullet->Move();
    this->m_Bullets.push_back(&middleBullet);

    Bullet* downBullet = new Bullet();
    downBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
    downBullet->SetVelocity(D3DXVECTOR2(20, -2));
    downBullet->SetPosition(GameObject::GetPosition());
    downBullet->Move();
    this->m_Bullets.push_back(&downBullet);
}

When it leaves the method, i check the list and the bullets are still there as well as before entering the ValidateBulletsBound , but as soon as it enters the method and before doing ANYTHING , the bullets on the list are simply gone, and with this i mean, the m_Bullets list still has three objects, but happens that they are all NULL.

To explain myself a little better, what i want to do is that every time i press space-bar 3 bullets appears on the screen, and I'm trying to do that by asking if the space-bar value is true, add 3 Bullets to the m_Bullet list, then validate that the bullets in the list are still between the screen space, otherwise remove it. But as you can see , i successfully store the bullets on the list and as soon as i enter to validate, they are gone... poof !

I don't know why any of this is happening, they are different instance of a class who doesn't share anything between them (memory-wise speaking), there's no static method or pointers shared by then, and even though they would it shouldn't be a problem given that they are just entering in another method and no operation has been done in the middle of that "entering the another method" part, or whatsoever. They even are in the same class, same context, no complex operation or leak (that i know of). I really don't know what's going on!

I want to finish by acknowledging that there are some serious design problems like the one's on GenerateTriBullet, and the fact that i'm not using matrices to move the objects. I'm just trying to finish it first (this is the first game i make on DirectX, really exited btw!!! ), then when i can see the big picture, start to put everything where it belongs. Also, how do i get the value from the list iterator, i read that it was (*it) for simple values, but i have a pointer to a pointer, so i thought it would be **it, but it always resolves to nullptr.

I really hope you can help me. Here's the project, if you feel like you didn't understand and want to go a little further. You just have to run the project, a ship will appear in a little black window, put a breakpoint on line 182 of the Fighter class then press spacebar on the game window, then from there see what happens with m_Bullets when it leaves GenerateTriBullet and Enters ValidateBulletsBounds .

THANK YOU!

One clear problem:

m_Bullets is a list<Bullet**> . When you add to it you are adding the address of a local variable, eg,

Bullet* upBullet = new Bullet();
...
this->m_Bullets.push_back(&upBullet);

Went this method returns, the address stored from the &upBullet expression is no longer valid, as that variable no longer exits.

I think you mean to have m_Bullets as list<Bullet*> , and to add to it:

Bullet* upBullet = new Bullet();
...
this->m_Bullets.push_back(upBullet);

I think the best solution is to let list deal with the memory management and just have m_Bullets as list<Bullet> , then:

this->m_Bullets.emplace_back();

However this will probably require rethinking some of the polymorphism.

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