本节将模仿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);
- }
附件中是工程的完整代码和图片,大家可以通过跟踪代码来了解这节的内容。