今天我们来学习一下Cocos2d-x中的场景切换效果,我们在玩一些2D的RPG游戏时,常常会遇到场景的转换,这时候当前场景的画面会以百叶窗翻页或者是缩放消失的动画效果切换到新的场景。Cocos2d-x提供了大量可以直接使用的场景切换效果,本节我们将深入分析一下这些效果的实现原理。
5.1.3 CCTransitionScene场景切换
当场景在切换过程可以通过带有动画功能的场景CCTransitionScene的子类来包装,我们要切换的场景可以显示动画切换的效果,
CCTransitionScene继承自CCScene,而所有场景过渡效果的类都继承自CCTransitionScene。CCTransitionScene的几个基本方法如下。
1、finish
该方法在过渡效果结束时调用。
2、hideOutShowIn
部分过渡效果会使用该方法来隐藏更外面的场景。
3、initWithDuration(float t, CCScene *s)
该方法初始化一个场景过渡效果,并指定过渡时间和即将过渡的场景。
虽然过渡效果的名称和需要的参数数量很多,但是添加场景间的过渡效果只需要增加一行代码而已。
以一个最简单的淡入淡出过渡效果为例,场景在1秒内过渡到白色。
- // 初始化一个过渡场景
- CCTransitionFade* transitionScene =
- CCTransitionFade::transitionWithDuration(1,gamescene);
- // 使用过渡场景对象替代HelloWorld场景
- CCDirector::sharedDirector()->replaceScene(transitionScene);
关于在Cocos2d-x中所能支持的动画效果读者可以查看TestCpp的源码中,Classes目录下,TransitionsTest目录下的TransitionsTest.h和TransitionsTest.cpp。
该案例对应于在testCpp运行后的第二个菜单项。
概述一下切景切换的核心原理:场景的切换,是将当前正在运行的场景运行一个动画序列,这个动画序列一般由两部分构成,第一部分为一个精灵动画或者进度动画,第二部分为一个回调函数,场景在切换过程中,本身会进行精灵动画或进度动画的过程,比如移动或缩放动画造成场景的移出,在动画结束后,执行回调函数将原场景隐藏释放,新场景设置为显示。
为了进一步的把这个原理说明给大家,咱们来进行源码的分析。在Cocos2d-x 2.0中,提供了大量封装好的场景类来实现相应的切换动画。我们打开CCTransition.h来看一下。首先, Cocos2d-x定义了一个用于场景切换的基类CCTransitionScene,这个类是由CCScene派生的,它本质上只是一个具有场景属性的控制器,控制在进行场景切换时的新老场景的切换处理。
class CC_DLL CCTransitionScene : public CCScene
{
protected:
//要切入的新场景
CCScene * m_pInScene;
//要切出的旧场景
CCScene * m_pOutScene;
//切换动画效果的时长
float m_fDuration;
//是否对场景进行渲染排序,是否保证新场景在最上面。
bool m_bIsInSceneOnTop;
//在切换完成后是否清空旧场景。
bool m_bIsSendCleanupToScene;
public:
//构造
CCTransitionScene();
//析构
virtual ~CCTransitionScene();
//重载场景的绘制函数,修改其绘制内容。
virtual void draw();
//重载场景在被载入时调用的函数,做一些初始化的操作。
virtual void onEnter();
//重载场景在被卸载时调用的函数,做一些清理的操作。
virtual void onExit();
//重载场景在被释放时调用的函数,做一些释放的操作。
virtual void cleanup();
//静态函数,创建一个场景切换动画,这里的参数一为动画的时长,参数二为要切换到的新场景,其内部调用create来实现。
CC_DEPRECATED_ATTRIBUTE static CCTransitionScene * transitionWithDuration(float t, CCScene *scene);
//同上,参数一为动画的时长,参数二为要切换到的新场景
static CCTransitionScene * create(float t, CCScene *scene);
//初始化场景切换动画。
virtual bool initWithDuration(float t,CCScene* scene);
//在场景切换动画结束后调用。
void finish(void);
//用来隐藏老场景。
void hideOutShowIn(void);
protected:
//设置对场景进行排序。
virtual void sceneOrder();
private:
//私有函数,用于在场景切换动画完成后,设置新场景为当前游戏场景。
void setNewScene(float dt);
};
代码很清楚的交待了场景切换的新场景创建,初始化,排序,以及结束的一些基本函数,其CPP中对应代码我也粘贴在这里进行解释:
//构造函数
CCTransitionScene::CCTransitionScene()
{
}
//析构函数
CCTransitionScene::~CCTransitionScene()
{
//因为在初始化时开始占用新老场景,所以对其引用计数器做了加一操作,这里自然要减一操作。
m_pInScene->release();
m_pOutScene->release();
}
//静态函数,创建一个场景切换动画,这里的参数一为动画的时长,参数二为要切换到的新场景,其内部调用create来实现。
CCTransitionScene * CCTransitionScene::transitionWithDuration(float t, CCScene *scene)
{
return CCTransitionScene::create(t,scene);
}
//同上
CCTransitionScene * CCTransitionScene::create(float t, CCScene *scene)
{
//使用new创建一个用来切换到的新场景。
CCTransitionScene * pScene = new CCTransitionScene();
//如果有效,则对其进行初始化。
if(pScene && pScene->initWithDuration(t,scene))
{
//如果初始化成功,交由内存管理器进行管理。
pScene->autorelease();
//返回这个新的场景。
return pScene;
}
//如果无效或初始化失败,释放后返回NULL。
CC_SAFE_DELETE(pScene);
return NULL;
}
//初始化场景。
bool CCTransitionScene::initWithDuration(float t, CCScene *scene)
{
//参数有效性判断
CCAssert( scene != NULL, "Argument scene must be non-nil");
//先调用基类的init函数进行基类成员变量初始化。
if (CCScene::init())
{
//将动画时长保存到变量m_fDuration中。
m_fDuration = t;
//保存要切换到的场景。
m_pInScene = scene;
//动画过程会占用新场景,所以对其引用计数器加1
m_pInScene->retain();
//保存当前的场景返回到m_pOutScene中。
m_pOutScene = CCDirector::sharedDirector()->getRunningScene();
if (m_pOutScene == NULL)
{ //如果当前没有任何场景,就创建一个场景返回到m_pOutScene中并初始化。
m_pOutScene = CCScene::create();
m_pOutScene->init();
}
//动画过程会占用当前场景,所以对其引用计数器也加1
m_pOutScene->retain();
//确保切换的两个场景不相同。
CCAssert( m_pInScene != m_pOutScene, "Incoming scene must be different from the outgoing scene" );
// 取得设备管理器。
CCDirector* pDirector = CCDirector::sharedDirector();
//在这个过程中将场景的触屏响应关闭,以免切换动画被中断。
pDirector->getTouchDispatcher()->setDispatchEvents(false);
//进行场景的排序。
this->sceneOrder();
//返回成功。
return true;
}
else
{ //如果初始化不成功,返回false。
return false;
}
}
//设置场景进行排序。
void CCTransitionScene::sceneOrder()
{
//设置将新场景放在最前面。
m_bIsInSceneOnTop = true;
}
//场景的绘制函数。
void CCTransitionScene::draw()
{
//先调用基类的场景绘制函数。
CCScene::draw();
//如果场景有排序,则先绘制旧的场景,再绘制新的场景,新场景最后绘制,就能保证在最前面。
if( m_bIsInSceneOnTop ) {
m_pOutScene->visit();
m_pInScene->visit();
} else {//否则则相反。
m_pInScene->visit();
m_pOutScene->visit();
}
}
//场景切换动画结束后调用的函数。
void CCTransitionScene::finish()
{
//先将新场景设为显示,并设置位置,缩放,旋转参数,摄像机重置。
m_pInScene->setVisible(true);
m_pInScene->setPosition(ccp(0,0));
m_pInScene->setScale(1.0f);
m_pInScene->setRotation(0.0f);
m_pInScene->getCamera()->restore();
//再将旧场景设为不显示,也设置位置,缩放,旋转参数,摄像机重置。
m_pOutScene->setVisible(false);
m_pOutScene->setPosition(ccp(0,0));
m_pOutScene->setScale(1.0f);
m_pOutScene->setRotation(0.0f);
m_pOutScene->getCamera()->restore();
//调用函数设置新场景为当前游戏场景的函数。
//[self schedule:@selector(setNewScene:) interval:0];
this->schedule(schedule_selector(CCTransitionScene::setNewScene), 0);
}
//设置新场景为当前游戏场景的函数。
void CCTransitionScene::setNewScene(float dt)
{
CC_UNUSED_PARAM(dt);
//将当前函数中当前类所挂接的回调函数容器的删除。
this->unschedule(schedule_selector(CCTransitionScene::setNewScene));
//取得当前设备。
CCDirector *director = CCDirector::sharedDirector();
//取得当前设备是否设置了被替换的场景将被释放,将其保存到变量m_bIsSendCleanupToScene。
m_bIsSendCleanupToScene = director->isSendCleanupToScene();
//将新场景设置为当前运行的场景。
director->replaceScene(m_pInScene);
//开启响应触屏事件。
director->getTouchDispatcher()->setDispatchEvents(true);
//设置被替换的场景显示,这样在主循环中设备才会对其进行相应的处理而使其被释放。
m_pOutScene->setVisible(true);
}
//用来隐藏老场景。
void CCTransitionScene::hideOutShowIn()
{
//设置显示新场景,隐藏老场景。
m_pInScene->setVisible(true);
m_pOutScene->setVisible(false);
}
//当前场景被加载时响应的函数。
void CCTransitionScene::onEnter()
{
//调用基类的相应函数。
CCScene::onEnter();
//对新场景调用相应函数。
m_pInScene->onEnter();
}
//当前场景被卸载时响应的函数。
void CCTransitionScene::onExit()
{
//调用基类的相应函数。
CCScene::onExit();
//对老场景调用相应函数。
m_pOutScene->onExit();
//新场景响应onEnterTransitionDidFinish函数。
m_pInScene->onEnterTransitionDidFinish();
}
// 清空
void CCTransitionScene::cleanup()
{ //调用基类的相应函数。
CCScene::cleanup();
//如果m_bIsSendCleanupToScene为true,则调用老场景的cleanup函数。
if( m_bIsSendCleanupToScene )
m_pOutScene->cleanup();
}
http://blog.youkuaiyun.com/honghaier/article/details/8475341