上一篇中我们讲到如何切换场景,这一篇我们来讲讲另外一种场景的切换。
我们知道,切换场景都是由导演类负责,上一篇中使用了replaceScene()这个方法,而当使用这个方法从第一个场景跳转到第二个场景的时候,那么,第一个场景便会被释放掉! 试想,如果我们在玩游戏的时候,如果想要在设置中关掉音乐,假设需要返回到设置场景,那么,使用了这个方法就会把你当前的游戏进度给清理掉了,就是说要重新开始了,那得多不幸。所以,现在有了另外的方法:pushScene()和popScene()。怎么说呢,使用这个两个方法其实就是进栈和出栈的过程。栈(stack),如果读者学过数据结构的就会知道,它就是一个可以看做容器的东西,规则是先进后出,大家可以看下图:
如果我们把游戏场景作为场景一先进栈,然后当需要进入设置场景的时候,调用pushScene()这一方法来作为场景二进栈,那么,场景一就不会被释放,而一直保存在那里,当设置完成,就使用popScene()这一方法把设置场景pop出去,就是释放掉。这样一来,不但可以保存原来的游戏场景进度,而且还能达到设置的目的。
好了,既然理解的这个push和pop是怎么样的之后,我们就需要来使用一下了,还是使用上次的两个场景:MyFirstScene和MySecondScene。
然后使用这两个方法也很简单,我们只要在场景跳转函数中修改就可以了,当然,这两个方法还是由导演类来完成的,我就直接贴代码出来了:
MyFIrstScene.cpp:
#include "MyFirstScene.h"
#include"MySecondScene.h"
//MyFirstScene场景创建实现
Scene* MyFirstScene::createScene()
{
auto scene = Scene::create(); //创建一个场景
auto layer = MyFirstScene::create(); //创建一个MyFirstScene层
scene->addChild(layer); //把MyFirstScene层加入刚刚创建的场景中,
return scene; //返回这个场景
}
bool MyFirstScene::init()
{
if ( !Layer::init() ) //先初始化父类的init方法,如何初始化失败,则创建MyFirstScene层失败
{
return false;
}
Size VisibleSize = Director::getInstance()->getVisibleSize(); //获得屏幕大小
auto label = Label::createWithSystemFont("Hello,This is MyFirstScene", "fonts/arial.ttf", 30);//创建一个标签
//label->setPosition(Vec2(VisibleSize.width / 2, VisibleSize.height / 2));//设置位置
//this->addChild(label);//把标签添加到层上
//菜单条目的创建有多种,之前的HelloWorld.cpp中实现的是图片菜单条目(MenuItemImage),即根据图片来创建菜单条目
//这里是根据标签来创建菜单条目,然后设置回调函数
auto menuitem = MenuItemLabel::create(label,CC_CALLBACK_1(MyFirstScene::EnterSecondScene,this));
//创建好了菜单条目,就需要加入菜单中,所以下面就是创建菜单
auto menu = Menu::create(menuitem, NULL);
//把菜单添加到MyFirstScene层中
this->addChild(menu);
return true;
}//菜单回调函数的实现
void MyFirstScene::EnterSecondScene(Ref *pSender)
{
//我们知道,如果需要跳转场景,就会想到一个类,它就是Director类,它就是管理场景的
//这里跳转场景调用到的是导演类的这个接口:replaceScene(Scene *scene),里面传进去的是就是一个场景
// Director::getInstance()->end();
//Director::getInstance()->replaceScene(MySecondScene::createScene());
Director::getInstance()->pushScene(MySecondScene::createScene());
}
其实修改的并不多,就只是修改上面这个菜单回调函数,其他的都不变。所以我就把MyFirstScene.cpp贴出来就行了。
还有就是MySecondScene.cpp:
#include"MySecondScene.h"
#include"MyFirstScene.h"
Scene *MySecondScene::createScene()
{
Scene* scene = Scene::create();
MySecondScene *layer = MySecondScene::create();
scene->addChild(layer);
return scene;
}
bool MySecondScene::init()
{
if (!Layer::init())
{
return false;
}
Size VisibleSize = Director::getInstance()->getVisibleSize();
auto label = Label::createWithSystemFont("Hello,This is MySecondScene", "fonts/arial.ttf", 30);
auto menuitem = MenuItemLabel::create(label, CC_CALLBACK_1(MySecondScene::EnterFirstScene, this));
auto menu = Menu::create(menuitem, NULL);
this->addChild(menu);
return true;
}
void MySecondScene::EnterFirstScene(Ref *pSender)
{
//跳转到第一个场景,记得包含第一个场景的头文件:MyFirstScene.h
//Director::getInstance()->replaceScene(MyFirstScene::createScene());
Director::getInstance()->popScene();
}
这里修改的就是把这个场景给pop掉,就是释放掉。好了,相信大家运行的结果和之前没什么区别对吧~这样好像就不能够知道是不是像上面所说的那样场景一是否被释放掉了。所以,其实我们可以加个测试的。
测试1:在场景一中尽可能的加多点东西,这样如果每次都会重新初始化即可看出效果。然后不要使用pushScene()和popScene()这两个方法,都换回之前的replaceScene()。然后当从场景二跳转回之前曾被释放的场景一,而场景一需要初始化并渲染的时间加长了就会出现卡顿现象!
测试2:同样也是在场景一中加入尽可能多的元素,不过这需要和测试1保持一致的数量。然后使用pushScene()和popScene()这两个方法进行场景切换。
对于测试的元素,我们可以这么来做,在场景一中的init()方法中加一千个标签。
代码如下:
for (int i = 1; i <= 1000; i++)
{
auto label = Label::createWithSystemFont("Hi~", "fonts/arial.ttf", 30);
label->setPosition(Vec2(100, 100));
this->addChild(label);
}
测试1结果:
测试2结果:
很明显,如果我们使用pushScene()和popScene()这两个方法的时候,即便是加多了点东西,那也是在开始运行场景一的时候卡一下,然后就不会卡了~可以试一下哦~
另外补充一下的就是。我们左线角不是有三行数字么,这里说一下它们的意思:
第一行代表渲染该场景是所需要的批次;
第二行代表渲染每一帧所需要的时间;
第三行代表当前的游戏帧率。
好了,这就是切换场景的另一种方式。希望对读者有所帮助~(每次写完,还是不免给自己一个大大的微笑~)