7. 重新审视

文章目录

重新审视

如果按照上一节讲的来做, 那么后续我们可以以GameObject来衍生处很多其他类, 但是这里有个问题. 如果导出类忘记了实现其中的几个方法, 那么游戏运行中可能会出现意外情况. 还有就是无法实现统一管理, 因此需要借助抽象类. 因此重新定义GameObject.

// GameObject.h
class GameObject
{
public:
    virtual void draw() = 0;
    virtual void update() = 0;
    virtual void clean() = 0;
protected:
    GameObject(const LoaderParams* pParams) {}
    virtual ~GameObject() {}
};

// LoaderParams.h
#ifndef LOADERPARAMS_H_INCLUDED
#define LOADERPARAMS_H_INCLUDED

#include <string>

class LoaderParams
{
public:
    LoaderParams(int x, int y, int width, int height, std::string textureID)
            : m_x(x), m_y(y), m_width(width), m_height(height), m_textureID(textureID)
    {

    }

    int getX() const { return m_x; }
    int getY() const { return m_y; }
    int getWidth() const { return m_width; }
    int getHeight() const { return m_height; }
    std::string getTextureID() const { return m_textureID; }

private:
    int m_x;
    int m_y;

    int m_width;
    int m_height;

    std::string m_textureID;
};

#endif // LOADERPARAMS_H_INCLUDED

// SDLGameObject.h
// 我们需要一个基类来保存共性数据
#ifndef SDLGAMEOBJECT_H_INCLUDED
#define SDLGAMEOBJECT_H_INCLUDED

#include "GameObject.h"
#include "LoaderParams.h"

class SDLGameObject : public GameObject
{
public:
    SDLGameObject(const LoaderParams* pParams);

    virtual void draw();
    virtual void update();
    virtual void clean();

protected:
    int m_x;
    int m_y;

    int m_width;
    int m_height;

    int m_currentRow;
    int m_currentFrame;

    std::string m_textureID;
};

#endif // SDLGAMEOBJECT_H_INCLUDED

// SDLGameObject.cpp
#include "SDLGameObject.h"
#include "Game.h"

SDLGameObject::SDLGameObject(const LoaderParams* pParams) : GameObject(pParams)
{
    m_x = pParams->getX();
    m_y = pParams->getY();
    m_width = pParams->getWidth();
    m_height = pParams->getHeight();
    m_textureID = pParams->getTextureID();

    m_currentRow = 1;
    m_currentFrame = 1;
}

void SDLGameObject::draw()
{
    TextureManager::Instance()->drawFrame(m_textureID, m_x, m_y, m_width, m_height,
                    m_currentRow, m_currentFrame,
                    TheGame::Instance()->getRenderer());
}

void SDLGameObject::update()
{

}

void SDLGameObject::clean()
{

}

这样我们可以避免忘记实现关键函数, 并且可以将所有继承自该类的对象以指针的方式保存在vector或其他数据结构中. 方便管理, 同时接口统一.

另外我们应该将前面的Game类重新设计成singleton模式, 毕竟游戏只有一个嘛.

// Game.h
#ifndef GAME_H_INCLUDED
#define GAME_H_INCLUDED

#include <vector>
#include <SDL.h>
#include "TextureManager.h"
#include "GameObject.h"

class Game
{
public:
    bool init(const char* title,
              int xpos = SDL_WINDOWPOS_CENTERED,
              int ypos = SDL_WINDOWPOS_CENTERED,
              int width = 640, int height = 480,
              int flags = SDL_WINDOW_SHOWN);
    void render();
    void update();
    void handleEvents();
    void clean();

    SDL_Renderer* getRenderer() const { return m_pRenderer; }
    bool running() const { return m_bRunning; }

    static Game* Instance();

private:
    Game();

private:
    SDL_Window* m_pWindow;
    SDL_Renderer* m_pRenderer;

    static Game* s_pInstance;
    std::vector<GameObject*> m_gameObjects;

    bool m_bRunning;
};

typedef Game TheGame;

#endif // GAME_H_INCLUDED
// Game.cpp
#include "Game.h"
#include "SDL_image.h"
#include <iostream>

Game* Game::s_pInstance = nullptr;

Game::Game()
{

}

bool Game::init(const char* title, int xpos, int ypos, int width, int height, int flags)
{
    bool success = true;
    if (SDL_Init(SDL_INIT_EVERYTHING) == 0) {
        std::cout << "SDL init success\n";
        m_pWindow = SDL_CreateWindow(title, xpos, ypos, width, height, flags);
        if (m_pWindow) {
            std::cout << "window creation success\n";
            m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, 0);
            if (m_pRenderer) {
                std::cout << "render creation success\n";
                SDL_SetRenderDrawColor(m_pRenderer, 0, 0, 0, 255);
            }
            else {
                std::cout << "render creation fail\n";
                success = false;
            }
        }
        else {
            std::cout << "window creation fail\n";
            success = false;
        }
    }
    else {
        std::cout << "SDL init fail\n";
        success = false;
    }

    if (success) {
        if (IMG_Init(IMG_INIT_PNG) == IMG_INIT_PNG) {
            std::cout << "IMG_init success.\n";
        }
        else {
            std::cout << "IMG_init fail.\n";
            success = false;
        }
    }

    if (success) {
        if (TextureManager::Instance()->load("assets/Beast1_alpha.png", "animate", m_pRenderer) == false) {
            std::cout << "Fail to load assets/Beast1_alpha.png.\n";
            success = false;
        }
    }

    if (success) {
        std::cout << "init success\n";
        m_bRunning = true;
    }
    else {
        std::cout << "init fail.\n";
        m_bRunning = false;
    }

    return success;
}

void Game::render()
{
    SDL_RenderClear(m_pRenderer);

    for (std::vector<GameObject*>::size_type i = 0; i != m_gameObjects.size(); i++) {
        m_gameObjects[i]->draw(m_pRenderer);
    }

    SDL_RenderPresent(m_pRenderer);
}

void Game::update()
{
    for (std::vector<GameObject*>::size_type i = 0; i != m_gameObjects.size(); i++) {
        m_gameObjects[i]->update();
    }
}

void Game::clean()
{
    std::cout << "cleaning game\n";
    IMG_Quit();

    SDL_DestroyRenderer(m_pRenderer);
    m_pRenderer = nullptr;
    SDL_DestroyWindow(m_pWindow);
    m_pWindow = nullptr;

    SDL_Quit();
}

void Game::handleEvents()
{
    SDL_Event event;

    if (SDL_PollEvent(&event)) {
        switch (event.type) {
        case SDL_QUIT:
            m_bRunning = false;
            break;

        default:
            break;
        }
    }
}

Game* Game::Instance()
{
    if (s_pInstance == nullptr) {
        s_pInstance = new Game();
    }

    return s_pInstance;
}

猜你喜欢

转载自blog.csdn.net/creambean/article/details/88666258