SDL2 游戏开发日志(二)
构建框架:场景,渲染。
渲染类
负责加载图片和渲染,它将可以添加到指定的【场景】中,当【场景】被【场景管理类】调用时,它将每一帧都被调用和更新。
#pragma once
#include <SDL.h>
#include <string>
using namespace std;
class Renderable{
protected:
bool mIsDeleted;
bool mIsVisible;
int mLayer;
SDL_Rect *mRenderRect;
SDL_Rect *mClipRect;
SDL_Texture *mTexture;
int mTextureWidth, mTextureHeight;
public:
Renderable() :mIsDeleted(false), mIsVisible(true){
mTexture = NULL;
mRenderRect = NULL;
mClipRect = NULL;
mLayer = 0;
}
virtual ~Renderable(){
if (mTexture != NULL){
SDL_DestroyTexture(mTexture);
mTexture = NULL;
}
if (mRenderRect != NULL){
delete mRenderRect;
mRenderRect = NULL;
}
if (mClipRect != NULL){
delete mClipRect;
mClipRect = NULL;
}
}
void SetLayer(int layer){
mLayer = layer;
}
int GetLayer(){
return mLayer;
}
virtual bool LoadTexture(string fileName);
void SetPos(int x,int y){
if (mRenderRect != NULL){
mRenderRect->x = x;
mRenderRect->y = y;
}
}
bool IsDeleted(){
return mIsDeleted;
}
void Delete(){
mIsDeleted = true;
}
bool IsVisible(){
return mIsVisible;
}
void SetVisible(bool visible){
mIsVisible = visible;
}
virtual void Update(float time_step){}
virtual void Render();
int GetWidth(){
return mTextureWidth;
}
int GetHeight(){
return mTextureHeight;
}
};
bool Renderable::LoadTexture(string fileName){
bool bResult = false;
if (mTexture != NULL){
SDL_DestroyTexture(mTexture);
mTexture = NULL;
}
SDL_Surface *surface = IMG_Load(fileName.c_str());
if (surface != NULL){
mTexture = SDL_CreateTextureFromSurface(theGame.getRenderer(), surface);
if (mTexture != NULL){
if (mRenderRect == NULL)
mRenderRect = new SDL_Rect();
mRenderRect->w = surface->w;
mRenderRect->h = surface->h;
mTextureWidth = surface->w;
mTextureHeight = surface->h;
bResult = true;
}
SDL_FreeSurface(surface);
}
return bResult;
}
void Renderable::Render(){
if (!mIsVisible || mTexture == NULL || mRenderRect == NULL){
return;
}
SDL_RenderCopyEx(theGame.getRenderer(), mTexture, mClipRect, mRenderRect, 0, NULL, SDL_FLIP_NONE);
}
字体渲染类
派生于Renderable类
#include "Renderable.h"
#include <string>
#include <SDL_ttf.h>
using namespace std;
class TextRenderable : public Renderable{
private:
string mText;
uint32_t mColor;
int mFontSize;
public:
virtual bool LoadTexture(string fileName){
return false;
}
bool SetText(string text, int fontSize, SDL_Color color);
virtual void Update(float time_step){
}
};
bool TextRenderable::SetText(string text, int fontSize, SDL_Color color){
uint32_t iColor = SDL_ColorToInt(color);
bool bSuccess = false;
//文字,颜色,大小不一样时重新创建SDL_Texture
if (mText != text || mColor != iColor || mFontSize != fontSize){
if (mTexture != NULL){
SDL_DestroyTexture(mTexture);
mTexture = NULL;
}
TTF_Font *font = theGame.GetFont(fontSize);
if (font != NULL){
SDL_Surface* textSurface = TTF_RenderUTF8_Blended(font, text.c_str(), color);
if (textSurface != NULL){
mTexture = SDL_CreateTextureFromSurface(theGame.getRenderer(), textSurface);
if (mTexture != NULL){
mTextureWidth = textSurface->w;
mTextureHeight = textSurface->h;
if (mRenderRect == NULL)
mRenderRect = new SDL_Rect();
mRenderRect->w = textSurface->w;
mRenderRect->h = textSurface->h;
bSuccess = true;
}
SDL_FreeSurface(textSurface);
}
}
}
if (bSuccess){
mText = text;
mColor = iColor;
mFontSize = fontSize;
}
return bSuccess;
}
字体类
负责加载游戏字体
#pragma once
#include <SDL_ttf.h>
#include <map>
#include <string>
#define FontMap map<int,TTF_Font*>
using namespace std;
class GameFont{
private:
FontMap mFonts;
string mFontName;
public:
GameFont(string fontName){
mFontName = fontName;
}
~GameFont(){
FontMap::iterator iter = mFonts.begin();
while (iter != mFonts.end())
{
TTF_CloseFont(iter->second);
iter++;
}
mFonts.clear();
}
TTF_Font *GetFont(int fontSize){
FontMap::iterator iter = mFonts.find(fontSize);
TTF_Font *font = NULL;
if (iter != mFonts.end()){
font = iter->second;
}
else{
font = TTF_OpenFont(mFontName.c_str(), fontSize);
if (font != NULL){
mFonts.insert(make_pair(fontSize, font));
}
}
return font;
}
void DeleteFont(int fontSize){
FontMap::iterator iter = mFonts.find(fontSize);
if (iter != mFonts.end()){
TTF_CloseFont(iter->second);
iter->second = NULL;
mFonts.erase(iter);
}
}
};
场景类
管理场景中的所有渲染物体
#pragma once
#include <map>
#include <vector>
#include <SDL.h>
#include "Renderable.h"
typedef std::vector<Renderable*> RenderList;
typedef std::map<int, RenderList> RenderLayers;
class Scene {
protected:
RenderLayers mLayers;
public:
Scene(){
}
virtual void LoadScene(){}
virtual ~Scene();
void AddRenderable(int layer, Renderable *renderObj);
void AddRenderable(Renderable*renderObject);
RenderLayers getLayers();
void Update(float time_step);
void Render();
virtual void HandleEvent(SDL_Event &_event){
}
};
void Scene::Update(float time_step){
RenderLayers::iterator layerIter = mLayers.begin();
while (layerIter != mLayers.end()){
RenderList::iterator listIter = layerIter->second.begin();
while (listIter != layerIter->second.end()){
(*listIter)->Update(time_step);
listIter++;
}
layerIter++;
}
}
void Scene::Render(){
RenderLayers::iterator layerIter = mLayers.begin();
while (layerIter != mLayers.end()){
RenderList::iterator renderIter = layerIter->second.begin();
while (renderIter != layerIter->second.end()){
if ((*renderIter)->IsDeleted()){
delete (*renderIter);
renderIter = layerIter->second.erase(renderIter);
}
else{
(*renderIter)->Render();
renderIter++;
}
}
layerIter++;
}
}
场景管理类
管理游戏场景,负责场景的加载,场景的切换,单例模式
#pragma once
#include "Scene.h"
#include <map>
using namespace std;
#define theSceneManager SceneManager::Instance()
typedef map<int, Scene*> SceneList;
class SceneManager{
private:
Scene *mCurrentScene;
SceneManager(const SceneManager &){}
void operator=(const SceneManager &){}
SceneManager(){
mCurrentScene = NULL;
}
SceneList mSceneList;
public:
static SceneManager &Instance(){
static SceneManager instance;
return instance;
}
~SceneManager(){
SceneList::iterator iter = mSceneList.begin();
while (iter != mSceneList.end()){
Scene *scene = iter->second;
delete scene;
iter++;
}
mSceneList.clear();
}
bool AddScene(int index, Scene *scene){
SceneList::iterator iter = mSceneList.find(index);
if (iter != mSceneList.end()){
return false;
}
mSceneList.insert(make_pair(index, scene));
return true;
}
bool ChangeScene(int index){
SceneList::iterator iter = mSceneList.find(index);
if (iter != mSceneList.end()){
mCurrentScene = iter->second;
return true;
}
return false;
}
void DeleteScene(int index){
SceneList::iterator iter = mSceneList.find(index);
if (iter != mSceneList.end()){
delete iter->second;
mSceneList.erase(iter);
}
}
void Update(float time_step){
if (mCurrentScene != NULL)
mCurrentScene->Update(time_step);
}
void Render(){
if (mCurrentScene != NULL)
mCurrentScene->Render();
}
void HandleEvent(SDL_Event &_event){
if (mCurrentScene != NULL){
mCurrentScene->HandleEvent(_event);
}
}
};
窗口类更新
在主循环中调用场景管理类进行更新和渲染
void SDLGame::Run(){
while (IsRunning)
{
int start = SDL_GetTicks();
while (SDL_PollEvent(&mEvent)){
if (mEvent.type == SDL_QUIT){
IsRunning = false;
}
theSceneManager.HandleEvent(mEvent);
}
theSceneManager.Update(mTimeStep);
SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(mRenderer);
//
//绘图
//
theSceneManager.Render();
SDL_RenderPresent(mRenderer);
int remaining_time = mFrameTime - (SDL_GetTicks() - start);
if (remaining_time > 0){
SDL_Delay(remaining_time);
}
mTimeStep = (SDL_GetTicks() - start) / 1000.f;
}
if (mRenderer != NULL)
SDL_DestroyRenderer(mRenderer);
if (mWindow != NULL)
SDL_DestroyWindow(mWindow);
IMG_Quit();
TTF_Quit();
SDL_Quit();
}
测试
框架初步完成,代码中估计还有bug,下一步将用它写一个【俄罗斯方块】来测试和修改
void LoadingScene::LoadScene(){
TextRenderable *loading = new TextRenderable();
SDL_Color color = { 255, 255, 255, 255 };
if (loading->SetText(charToUTF8("加载中……"), 30, color)){
int x = theGame.GetWindowWidth() / 2 - loading->GetWidth() / 2;
int y = theGame.GetWindowHeight() / 2 - loading->GetHeight() / 2;
loading->SetPos(x, y);
this->AddRenderable(loading);
}
else{
delete loading;
}
Renderable *picLoading = new Renderable();
if (picLoading->LoadTexture(theGame.GetResourcePath() + "loading.png")){
int x = theGame.GetWindowWidth() - picLoading->GetWidth();
int y = theGame.GetWindowHeight() - picLoading->GetHeight();
picLoading->SetPos(x, y);
this->AddRenderable(picLoading);
}
else{
delete picLoading;
}
}
int main(int argc, _TCHAR* argv[]){
//获取程序所在路径
char path[MAX_PATH];
memset(path, 0, MAX_PATH);
DWORD pathSize = GetModuleFileNameA(NULL, path, MAX_PATH);
PathRemoveFileSpecA(path);
//设置游戏资源所在路径
string strPath = charToUTF8(path)+"\\Res\\";
theGame.SetResourcePath(strPath);
string title = charToUTF8("中文标题");
theGame.Init(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT);
//加载字体
theGame.LoadGameFont(theGame.GetResourcePath() + "simkai.ttf");
//创建场景
Scene *loadingScene = new LoadingScene();
loadingScene->LoadScene();
theSceneManager.AddScene(0, loadingScene);
theSceneManager.ChangeScene(0);
theGame.Run();
return 0;
}