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

在这里插入图片描述

发布了9 篇原创文章 · 获赞 0 · 访问量 1031

猜你喜欢

转载自blog.csdn.net/drizzlemao/article/details/103419909