SDL2游戏开发教程:SDL2 游戏开发最小知识栈 - 没事造轮子
项目环境配置: SDL2+2048小游戏开发-环境配置 | Mind City
项目代码详解:SDL2+2048小游戏开发-代码详解 | Mind City
项目源代码:Invisiphantom/Game-2048
class GameModule
游戏的最顶层模块是GameModule
游戏模块类
负责初始化SDL模块、执行游戏循环和释放SDL模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
class GameModule { private: SDL_Window* pWin = nullptr; SDL_Renderer* pRenderer = nullptr;
const char* title; unsigned int FPS = 0u; SIZE winSize = {0l, 0l};
GameMechanic* gameMechanic = nullptr;
public: explicit GameModule(const char* _title, LONG _winSizeWidth, LONG _winSizeHeight, unsigned int _FPS) : title(_title), winSize({_winSizeWidth, _winSizeHeight}), FPS(_FPS) {} ~GameModule() {}
void initGameModules(); void uninitGameModules(); void runGame() const;
SDL_Window* getpWin() { return pWin; } SDL_Renderer* getpRenderer() { return pRenderer; }
void embedGameMechanic(GameMechanic& _GameMechanic) { gameMechanic = &_GameMechanic; } };
|
GameModule::runGame()
GameModule::runGame()
是游戏的主体循环部分
通过SDL的计时器来记录游戏时间并按帧率更新游戏
在游戏循环内通过调用gameMechanic
的三个接口函数来实现游戏
- 游戏键鼠交互
gameMechanic->processGameEvent(evt);
- 游戏界面渲染
gameMechanic->renderGame();
- 游戏状态更新
gameMechanic->updateGame(frameMS);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| void GameModule::runGame() const { int quit = 0; SDL_Event evt;
Uint64 nFrequency, nPrevCounter, nCurrCounter, nElapseCounter; float elapseMS = 0.0f, lagMS = 0.0f, frameMS = 1000.0f / FPS;
nFrequency = SDL_GetPerformanceFrequency(); nPrevCounter = SDL_GetPerformanceCounter();
while (!quit) { if (SDL_PollEvent(&evt)) if (evt.type == SDL_QUIT) quit = 1; else gameMechanic->processGameEvent(evt); else { nCurrCounter = SDL_GetPerformanceCounter(); nElapseCounter = nCurrCounter - nPrevCounter; nPrevCounter = nCurrCounter;
elapseMS = (nElapseCounter * 1000.0f) / nFrequency; lagMS += elapseMS;
SDL_SetRenderDrawColor(pRenderer, 250, 248, 239, 255); SDL_RenderClear(pRenderer); gameMechanic->renderGame(); SDL_RenderPresent(pRenderer);
while (lagMS >= frameMS) { gameMechanic->updateGame(frameMS); lagMS -= frameMS; } }
Sleep(10); } }
|
class GameMechanic
游戏的中间层是抽象基类GameMechanic
它定义了规范派生类需要实现的那三个接口函数
processGameEvent(evt)
、updateGame(frameMS)
和renderGame()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
class GameMechanic { public: GameMechanic() {} virtual ~GameMechanic() {}
virtual void processGameEvent(const SDL_Event evt) = 0;
virtual void updateGame(const float ms) = 0;
virtual void renderGame() const = 0; };
|
Game2048
游戏的最底层是游戏的实现类Game2048
它继承自GameMechanic,实现了2048游戏所需的三个接口
通过GameModule类的embedGameMechanic()被嵌入到游戏模块类中
服务于GameModule::runGame()的游戏主体循环实现
主要内部变量:
- 游戏状态的七个阶段
- SDL的渲染组件
- SDL窗口指针
- SDL渲染器指针
- SDL字体指针
- SDL贴图指针
- 游戏的机制组件
- 随机数生成器
- 当前游戏状态
- 方格内数字
- 历史最高分
- 当前分数
主要成员函数:
- 游戏的机制函数
- 随机放置新数字
- 初始化方格
- 向上移动
- 向下移动
- 向左移动
- 向右移动
- 检查是否胜利
- 检查是否失败
- 游戏的资源管理函数
- 游戏的接口函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
class Game2048 : public GameMechanic { enum UIState { UI_READY = 0, UI_GO, UI_INIT, UI_RUN, UI_WIN, UI_DEAD, UI_INFINITY };
private: SDL_Window* pWin = nullptr; SDL_Renderer* pRenderer = nullptr;
TTF_Font* titleFont = nullptr; TTF_Font* scoreFont = nullptr; TTF_Font* textFont = nullptr; SDL_Texture* tScore = nullptr; SDL_Texture* tBoard = nullptr; std::map<int, SDL_Texture*> tNums;
std::mt19937 randGen; UIState GameState = UI_READY; int board[4][4] = {0}; int bestScore = 0; int score = 0;
private: void putNumber(); void initBoard(); void moveUp(); void moveDown(); void moveLeft(); void moveRight(); void checkWin(); void checkDead();
public: explicit Game2048(SDL_Window* _pWin, SDL_Renderer* _pRenderer) : pWin(_pWin), pRenderer(_pRenderer), GameState(UI_READY) { randGen.seed(std::random_device()()); } ~Game2048() {}
void loadResources(); void unloadResources();
void processGameEvent(const SDL_Event evt); void updateGame(const float ms); void renderGame() const; };
|
Summary
通过GameModule、GameMechanic和Game2048三个类的组合,实现了一个简单的2048游戏
并且此架构可以很方便地扩展到其他游戏上,只需要实现GameMechanic的三个接口函数即可