SDL2 遊戲開發日誌(二)

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;
}

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章