文章目录
重新审视
如果按照上一节讲的来做, 那么后续我们可以以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;
}