SDL游戏开发教程10(场景管理器)

    本节将模仿ogre的ScreenManager编写一个SDL的ScreenManager。效果图如下


    这是利用场景管理器创建的一个扫雷游戏界面,为后面的扫雷游戏做准备。

 

    这里的场景管理器主要有四个类,SDLEntity(实体)、SDLSceneNode(节点)、SDLLayer(层)、SDLSceneManager(场景管理器)。

 

    他们的关系是:一个SDLSceneManager管理多个SDLLayer,一个SDLLayer有多个SDLSceneNode,一个SDLSceneNode上面关联一个SDLEntity。

 

    SDLSceneManager管理多个SDLLayer是为了将不同的层分离开来,并且通过索引号来决定哪个层显示在下面,哪个层显示在上面。这样做有利用分开管理,比如将游戏界面中不变的背景和游戏中经常变化的前景分开,设置背景的索引号小于前景索引号,这样就可以保证前景在上面显示,背景在后面显示,同时也可以通过整体平移背景层,来达到整个游戏场景的向前移动的效果。

#ifndef SDLSCENEMANAGER_H_
#define SDLSCENEMANAGER_H_

#include <map>
#include "SDL/SDLSurface.h"
class SDLLayer;
class SDLSceneManager
{
	friend class SDL;
private:
	SDLSceneManager();
public:
	virtual ~SDLSceneManager();
public:
	//获取默认层,默认层为最底层
	SDLLayer* getDefaultLayer();

	//获取索引号对应的层,索引号必须大于0,索引号大的显示在前面
	SDLLayer* getLayer(int index);

	//移除索引号对应的层
	void removeLayer(int index);

	//绘制整个场景
	void	draw(SDLSurfacePtr screen);
private:
	std::map<int, SDLLayer*> layers;
};

#endif /* SDLSCENEMANAGER_H_ */
 

    SDLLayer负责管理层中的SDLSceneNode,每个层都有一个根节点,根节点位于整个层的左上角,通过根节点可以创建子节点,然后子节点可以创建自己的子节点,以此类推,就可以得到一棵节点树。整棵节点树就构成了当前层要显示内容的布局。

#ifndef SDLLAYER_H_
#define SDLLAYER_H_
#include "SDLSceneNode.h"
class SDLSceneManager;
class SDLLayer
{
	friend class SDLSceneManager;
private:
	SDLLayer(SDLSceneManager * sceneManager, int id);
public:
	virtual ~SDLLayer();
public:
	//获得当前层的根节点
	SDLSceneNode* 	getRootSceneNode();

	//获取当前层的索引号
	int				getID();

private:
	//绘制整个层
	void			draw(SDLSurfacePtr screen);
private:
	SDLSceneNode * 		rootNode;
	int					id;
	SDLSceneManager *	sceneManager;
};

#endif /* SDLLAYER_H_ */
 

    SDLSceneNode代表SDLLayer中的一个具体位置,他记录有相对与父节点的相对坐标,通过父节点的绝对坐标,可以获取当前节点的绝对坐标,根节点的绝对坐标为(0,0)。一个节点可以有多个子节点,不同的子节点通过名称来区分;一个节点可以关联一个实体(以后可能会扩充到关联多个),当关联了实体后,场景管理器就会在节点的当前位置绘制实体。

#ifndef SDLSCENENODE_H_
#define SDLSCENENODE_H_
#include "SDL/SDLCore.h"
#include "SDLEntity.h"
class SDLLayer;
class SDLSceneNode
{
	friend class SDLLayer;
private:
	SDLSceneNode();
public:
	SDLSceneNode(SDLSceneNode *parent, std::string name, int x = 0, int y = 0);
public:
	virtual ~SDLSceneNode();
public:
	//获取节点名称
	std::string getName();

	//回去节点相对位置
	SDL_Point getPosition();

	//获取节点绝对位置
	SDL_Point getAbsolutePosition();

	//关联实体
	void attachEntity(SDLEntity *entity);

	//取消与实体的关联
	void detachEntity();

	//创建子节点
	SDLSceneNode* createChildSceneNode(std::string name, int x = 0, int y = 0);

	//创建一个与自己关联的实体
	SDLEntity* createAttachedEntity(const std::string name, SDLSurfacePtr surface);

	//获取已经关联的实体
	SDLEntity* getEntity();
private:

	//绘制本节点和所有的子节点
	void	draw(SDLSurfacePtr screen);
private:
	SDLEntity *entity;
	std::map<std::string, SDLSceneNode*> children;
	std::string name;
	SDLLayer *layer;
	SDLSceneNode *parent;
	SDL_Point	position;
};

#endif /* SDLSCENENODE_H_ */

 

    SDLEntity负责管理要显示在屏幕上的图像,一个SDLEntity只能包含一个surface,只能关联到一个节点。这里实体名称的作用主要是为了标示一个实体,便于跟踪调试,以后可能会有其他用途(在ogre里面能在场景管理器中通过实体的名称来获取实体,我们这里不行,因为暂时没有看出这样做有什么大的用途)。

#ifndef SDLENTITY_H_
#define SDLENTITY_H_
#include "SDL/SDLSurface.h"
#include <string>
class	SDLSceneNode;
class SDLEntity
{
private:
	SDLEntity();
public:
	SDLEntity(std::string name, SDLSurfacePtr surface, SDLSceneNode *node = NULL);
	virtual ~SDLEntity();
public:
	//获取关联的节点
	SDLSceneNode * getSceneNode();

	//获取名称
	std::string getName();

	//获取包含的Surface
	SDLSurfacePtr getSurface();

	//设置包含的Surface
	void setSurface(SDLSurfacePtr surface);

	//取消关联节点
	void detachSceneNode();

	//关联节点
	void attachSceneNode(SDLSceneNode *	node);
private:
	std::string name;
	SDLSurfacePtr surface;
	SDLSceneNode *	node;
};

#endif /* SDLENTITY_H_ */

 

    这里的逻辑比较简单,以后随着应用的深入,我们再来强化他的功能。

    有了场景管理器后,我们不需要在onRender函数中写任何代码了。

    在SDLFrame::open的消息循环中,去掉调用onRender的代码,加入场景绘制的代码,同时在场景绘制前和绘制后各加一个函数,便于在显示之前和之后做一些事情。

		beforeRender();

		SDL::sceneManager()->draw(screen);
		
		afterRender();
 

 

    下面是应用的代码

void Lesson04::onInit()
{		

	SDLLayer * background = SDL::sceneManager()->getDefaultLayer();
	SDLSceneNode *root = SDL::sceneManager()->getLayer(1)->getRootSceneNode();

	//加载图片
	SDLSurfacePtr	mine_unknown = SDL::imageManager()->loadImage(MINE_UNKNOWN);
	SDLSurfacePtr	frame_outer_h = SDL::imageManager()->loadImage(FRAME_OUTER);
	SDLSurfacePtr	frame_outer_v = SDL::transform()->Rotate90Degrees(frame_outer_h, 1);
	SDLSurfacePtr	frame_inner_h = SDL::imageManager()->loadImage(FRAME_INNER);
	SDLSurfacePtr	frame_inner_v = SDL::transform()->Rotate90Degrees(frame_inner_h, 1);
	SDLSurfacePtr	frame_background_src = SDL::imageManager()->loadImage(FRAME_BACKGROUND);

	SDL_Rect		mine_unknown_rect = mine_unknown->value()->clip_rect;
	SDL_Rect		frame_outer_rect = frame_outer_h->value()->clip_rect;
	SDL_Rect		frame_inner_rect = frame_inner_h->value()->clip_rect;
	SDL_Rect		frame_background_rect = frame_background_src->value()->clip_rect;


	//计算扫雷区域的大小
	SDL_Rect mine_frame;
	mine_frame.w = MINE_COLS * mine_unknown_rect.w
						+ (MINE_COLS-1) * frame_inner_rect.h
						+ frame_outer_rect.h * 2;
	mine_frame.h = MINE_ROWS * mine_unknown_rect.h
						+ (MINE_ROWS-1) * frame_inner_rect.h
						+ frame_outer_rect.h * 2;
	SDLSurfacePtr frame_background = SDL::transform()->zoom(frame_background_src, mine_frame.w, mine_frame.h);
	frame_background_rect = frame_background->value()->clip_rect;

	//----------------------------------------------------------------------------------------
	//创建背景图
	SDLSurfacePtr imageSrc = SDL::imageManager()->loadImage(BACKGROUND_ROOT);
	SDLSurfacePtr image = SDL::transform()->zoom(imageSrc, screen->value()->clip_rect.w, screen->value()->clip_rect.h) ;
	background->getRootSceneNode()->createChildSceneNode("image", 0, 0)->createAttachedEntity("image",image);
	//----------------------------------------------------------------------------------------
	//创建雷区背景
	int	x = (screen->value()->clip_rect.w - frame_background_rect.w)/2;
	int	y = (screen->value()->clip_rect.h - frame_background_rect.h) - 20;
	SDLSceneNode *frame_background_node = root->createChildSceneNode(FRAME_BACKGROUND, x, y);
	frame_background_node->createAttachedEntity(FRAME_BACKGROUND, frame_background);

	//----------------------------------------------------------------------------------------
	//创建边框
	SDLSurfacePtr frame_outer_h_all = SDL::transform()->flat(frame_outer_h
			, frame_background_rect.w
			, frame_outer_rect.h);
	SDLSurfacePtr frame_outer_v_all = SDL::transform()->flat(frame_outer_v
				, frame_outer_rect.h
				, frame_background_rect.h);
	//上
	std::string FREAM_OUTER_TOP = FRAME_OUTER + "TOP";
	SDLSceneNode * frame_outer_top_node = frame_background_node->createChildSceneNode(FREAM_OUTER_TOP
				, 0
				, 0);
	frame_outer_top_node->createAttachedEntity(FREAM_OUTER_TOP, frame_outer_h_all);
	//下
	std::string FREAM_OUTER_BOTTOM = FRAME_OUTER + "BOTTOM";
	SDLSceneNode *	frame_outer_bottom_node = frame_background_node->createChildSceneNode(FREAM_OUTER_BOTTOM
				, 0
				, frame_background_rect.h - frame_outer_rect.h);
	frame_outer_bottom_node->createAttachedEntity(FREAM_OUTER_BOTTOM, frame_outer_h_all);
	//左
	std::string FREAM_OUTER_LEFT = FRAME_OUTER + "LEFT";
	SDLSceneNode *	frame_outer_left_node = frame_background_node->createChildSceneNode(FREAM_OUTER_LEFT
				, 0
				, 0);
	frame_outer_left_node->createAttachedEntity(FREAM_OUTER_LEFT, frame_outer_v_all);
	//右
	std::string FREAM_OUTER_RIGHT = FRAME_OUTER + "RIGHT";
	SDLSceneNode *frame_outer_right_node = frame_background_node->createChildSceneNode(FREAM_OUTER_RIGHT
			, frame_background_rect.w - frame_outer_rect.h
			, 0);
	frame_outer_right_node->createAttachedEntity(FREAM_OUTER_RIGHT, frame_outer_v_all);

	//-----------------------------------------------------------------------------------------
	//创建初始雷格
	for(int row = 0; row < MINE_ROWS; row++)
	{
		for(int col = 0; col < MINE_COLS; col++)
		{
			std::string name = MINE_UNKNOWN + boost::lexical_cast<std::string>(row*MINE_COLS + col);
			SDLSceneNode *node = frame_background_node->createChildSceneNode(name
					, frame_outer_rect.h + col * (mine_unknown_rect.w + frame_inner_rect.h)
					, frame_outer_rect.h + row * (mine_unknown_rect.h + frame_inner_rect.h));
			node->createAttachedEntity(name, mine_unknown);
			this->mines[row][col] = node;
		}
	}

	//-----------------------------------------------------------------------------------------
	//创建内部网格
	SDLSurfacePtr frame_iner_h_all = SDL::transform()->flat(frame_inner_h
				, frame_background_rect.w - frame_outer_rect.h*2
				, frame_inner_rect.h);
		SDLSurfacePtr frame_iner_v_all = SDL::transform()->flat(frame_inner_v
					, frame_inner_rect.h
					, frame_background_rect.h - frame_outer_rect.h*2);
	for(int col = 1; col < MINE_COLS; col++)
	{
		std::string name = FRAME_INNER + "v" + boost::lexical_cast<std::string>(col);
		SDLSceneNode *node = frame_background_node->createChildSceneNode(name
				, frame_outer_rect.h + col * mine_unknown_rect.w + (col-1)*frame_inner_rect.h
				, frame_outer_rect.h);
		node->createAttachedEntity(name, frame_iner_v_all);
	}
	for(int row = 1; row < MINE_ROWS; row++)
	{
		std::string name = FRAME_INNER + "r" + boost::lexical_cast<std::string>(row);
		SDLSceneNode *node = frame_background_node->createChildSceneNode(name
				, frame_outer_rect.h
				, frame_outer_rect.h + row * mine_unknown_rect.h + (row-1)*frame_inner_rect.h);
		node->createAttachedEntity(name, frame_iner_h_all);
	}
	//-----------------------------------------------------------------------------------------
	//创建上面的显示剩余雷数和所花时间数

	//剩余雷数
	int	xStart = 0, yStart = 0;
	SDLSurfacePtr mineAmountTitleSf = SDL::imageManager()->loadImageWithoutColor(MINE_AMOUNT, 255, 255, 255);
	xStart = frame_background_node->getPosition().x;
	yStart = (frame_background_node->getPosition().y - mineAmountTitleSf->value()->clip_rect.h)/2;
	SDLSceneNode *mineAmountTitelNode = root->createChildSceneNode(MINE_AMOUNT, xStart, yStart);
	mineAmountTitelNode->createAttachedEntity(MINE_AMOUNT, mineAmountTitleSf);

	SDLFontPtr font = SDL::fontManager()->OpenFont(FONT, 24);
	SDLSurfacePtr mineAmountSf = font->RenderUNICODEBlended("99", SDL::assistant()->makeColor(255, 0, 0));
	xStart = mineAmountTitleSf->value()->clip_rect.w + frame_background_node->getPosition().x + 5;
	yStart = (frame_background_node->getPosition().y - mineAmountSf->value()->clip_rect.h)/2;
	this->mineAmountNode = root->createChildSceneNode(MINE_AMOUNT+boost::lexical_cast<std::string>(1), xStart, yStart);
	mineAmountNode->createAttachedEntity(MINE_AMOUNT+boost::lexical_cast<std::string>(1), mineAmountSf);

	//重启图标
	SDLSurfacePtr restartSf = SDL::imageManager()->loadImageWithoutColor(RESTART, 255, 255, 255);
	xStart = (screen->value()->clip_rect.w - restartSf->value()->clip_rect.w)/2;
	yStart = (frame_background_node->getPosition().y - restartSf->value()->clip_rect.h)/2;
	SDLSceneNode *restartNode = root->createChildSceneNode(RESTART, xStart, yStart);
	restartNode->createAttachedEntity(RESTART, restartSf);

	//计时器
	SDLSurfacePtr timeCounterSf = font->RenderUNICODEBlended("000", SDL::assistant()->makeColor(255, 0, 0));
	xStart = frame_background_node->getPosition().x + frame_background_rect.w - timeCounterSf->value()->clip_rect.w;
	yStart = (frame_background_node->getPosition().y - timeCounterSf->value()->clip_rect.h)/2;
	SDLSceneNode *timeCounterNode = root->createChildSceneNode(CLOCK+boost::lexical_cast<std::string>(1), xStart, yStart);
	timeCounterNode->createAttachedEntity(CLOCK+boost::lexical_cast<std::string>(1), timeCounterSf);

	SDLSurfacePtr clockSf = SDL::imageManager()->loadImageWithoutColor(CLOCK, 255, 255, 255);
	xStart = frame_background_node->getPosition().x + frame_background_rect.w
				- timeCounterSf->value()->clip_rect.w - clockSf->value()->clip_rect.w - 5;
	yStart = (frame_background_node->getPosition().y - clockSf->value()->clip_rect.h)/2;
	this->clockNode = root->createChildSceneNode(CLOCK, xStart, yStart);
	clockNode->createAttachedEntity(CLOCK, clockSf);

	//插旗
	SDLSurfacePtr mine_flag = SDL::transform()->zoom(SDL::imageManager()->loadImage(MINE_FLAG), mine_unknown_rect.w, mine_unknown_rect.w);
	this->mines[4][5]->getEntity()->setSurface(mine_flag);
	this->mines[8][10]->getEntity()->setSurface(mine_flag);
	this->mines[10][3]->getEntity()->setSurface(mine_flag);
	//插问号
	SDLSurfacePtr mine_doubt_src = SDL::imageManager()->loadImageWithoutColor(MINE_DOUBT, 255, 255, 255);
	SDLSurfacePtr mine_doubt = SDL::video()->copySurface(mine_unknown);
	SDL::video()->BlitSurface(mine_doubt_src, NULL, mine_doubt, NULL);
	this->mines[8][5]->getEntity()->setSurface(mine_doubt);
	this->mines[4][10]->getEntity()->setSurface(mine_doubt);
	this->mines[5][3]->getEntity()->setSurface(mine_doubt);
	//插0,1,2,3,4,5,6,7,8
	SDLEntity *entity = this->mines[8][3]->getEntity();
	entity->detachSceneNode();
	delete entity;
	SDLSurfacePtr mine_number_1 = SDL::imageManager()->loadImageWithoutColor(MINE_NUMBER_1, 255, 255, 255);
	this->mines[8][6]->getEntity()->setSurface(mine_number_1);
	SDLSurfacePtr mine_number_2 = SDL::imageManager()->loadImageWithoutColor(MINE_NUMBER_2, 255, 255, 255);
	this->mines[9][10]->getEntity()->setSurface(mine_number_2);
	SDLSurfacePtr mine_number_3 = SDL::imageManager()->loadImageWithoutColor(MINE_NUMBER_3, 255, 255, 255);
	this->mines[2][3]->getEntity()->setSurface(mine_number_3);
	SDLSurfacePtr mine_number_4 = SDL::imageManager()->loadImageWithoutColor(MINE_NUMBER_4, 255, 255, 255);
	this->mines[5][6]->getEntity()->setSurface(mine_number_4);
	SDLSurfacePtr mine_number_5 = SDL::imageManager()->loadImageWithoutColor(MINE_NUMBER_5, 255, 255, 255);
	this->mines[12][10]->getEntity()->setSurface(mine_number_5);
	SDLSurfacePtr mine_number_6 = SDL::imageManager()->loadImageWithoutColor(MINE_NUMBER_6, 255, 255, 255);
	this->mines[15][3]->getEntity()->setSurface(mine_number_6);
	SDLSurfacePtr mine_number_7 = SDL::imageManager()->loadImageWithoutColor(MINE_NUMBER_7, 255, 255, 255);
	this->mines[13][12]->getEntity()->setSurface(mine_number_7);
	SDLSurfacePtr mine_number_8 = SDL::imageManager()->loadImageWithoutColor(MINE_NUMBER_8, 255, 255, 255);
	this->mines[11][15]->getEntity()->setSurface(mine_number_8);
}
 

 

    附件中是工程的完整代码和图片,大家可以通过跟踪代码来了解这节的内容。

#include #include //用键盘控制精灵移动 int main(int argc, char ** argv) { SDL_Surface * screen; //主表面 SDL_Surface * image; //用来放MM-----的图片信息(像素) SDL_Surface * PlayerImage; //用来测试的图片 SDL_Event event; Uint32 BeginTicks, EndTicks; SDL_Rect PRect, BRect; //PRect对应精灵的移动的小图位置(实现动画),BRect对应精灵在屏幕的位置。 unsigned char PlayerStarts = 0; unsigned char PlayerIndex = 0; bool bAppRun = true; //初始化SDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) { fprintf(stderr, "SDL_Init %s\n", SDL_GetError()); return -1; } //初始化成功设置退出要调用的函数SDL_Quit atexit(SDL_Quit); //创建一个640X480 16-bit 模式的主表面 16位可以让MM的效果好看一点 screen = SDL_SetVideoMode(230, 230, 16, SDL_SWSURFACE); if (screen == NULL) { fprintf(stderr, "Couldn't set 640x480x8 video mode %s\n", SDL_GetError()); return -1; } //读取MM图片信息,并创建一个表面,并把数据填入该表面中。 image = SDL_LoadBMP("./mm.bmp"); //请在终端里运行该程序 if (image == NULL) { fprintf(stderr, "Couldn't load MM, %s\n", SDL_GetError()); //遗憾你不能显示MM了,不过你可以用图片浏览程序看。 return -1; } //读取player.bmp PlayerImage = SDL_LoadBMP("./player.bmp"); //请在终端里运行该程序 if (image == NULL) { fprintf(stderr, "Couldn't load MM, %s\n", SDL_GetError()); //遗憾你不能显示MM了,不过你可以用图片浏览程序看。 return -1; } //读取第一个像素 Uint8 key = *((Uint8 *)PlayerImage->pixels); //设置色键 SDL_SetColorKey(PlayerImage, SDL_SRCCOLORKEY, key); //有了MM的表面了,就可以显示了。 //将MM的表面画在我们的主表面上,用MM来作为背景 if (SDL_BlitSurface(image, NULL, screen, NULL) < 0) { //解释一下NULL,第一个是按照image的尺寸显示,第二个是默认显示。你也可以指定大小,不过要用到SDL_Rect你可以看一看帮助。 fprintf(stderr, "BlitSurface error: %s\n", SDL_GetError()); //看看提示吧 return -1; } PRect.x = 0; //初始化动画显示的图片。 PRect.y = 0; PRect.w = 32; PRect.h = 48; BRect.x = 0; //初始化精灵的位置。 BRect.y = 0; BRect.w = 32; BRect.h = 48; //贴上测试用的表面 if (SDL_BlitSurface(PlayerImage, &PRect, screen, &BRect) w, image->h); BeginT
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值