今天我們來學習一下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.csdn.net/honghaier/article/details/8475341