Cocos2d-x入門之旅[4]場景
我們之前講了場景圖(Scene Graph) 的概念,繼續之前你先要知道
- 場景圖決定了場景內節點物件的渲染順序
- 渲染時 z-order 值大的節點物件會後繪製,值小的節點物件先繪製
HelloWorld
你還記得HelloWorld場景是如何啟動的麼?回看我們工程裡的AppDelegate.cpp,滾到applicationDidFinishLaunching()
的尾部:
// create a scene. it's an autorelease object auto helloWorldscene = HelloWorld::createScene(); // run director->runWithScene(helloWorldscene);
Ctrl+滑鼠左鍵 點選createScene()
檢視定義,可以看到這個函式在HelloWorldScnen.h內宣告,在HelloWorldScnen.cpp內定義
// HelloWorldScnen.h
static cocos2d::Scene* createScene();
// HelloWorldScnen.cpp
Scene* HelloWorld::createScene()
{
return HelloWorld::create();
}
我們可以通過該函式獲取一個HelloWorld場景物件
之後就是場景的初始化,選單,精靈等物件的Set都在這裡進行
bool HelloWorld::init() { ... }
最後我們看到一個回撥函式
void HelloWorld::menuCloseCallback(Ref* pSender)
{
//Close the cocos2d-x game scene and quit the application
Director::getInstance()->end();
}
C++基礎差的同學可能還不理解回撥的概念,但你只需要知道,這個函式實現了:HelloWorld場景內點選關閉按可鈕關閉視窗 的功能,就行了、
SecondScene
現在我們對著HelloWorld的程式碼來建立一個SecondScene
和HelloWorld一樣,首先我們需要一份SecondScene.h存放宣告,然後是一份SecondScene.cpp存放定義
注意VS內新建檔案時,一定要儲存到Class資料夾內,不然你是不能直接include“xxxxxx”的(萬惡的VS預設儲存路徑不是Class)
修改到專案目錄下的Class
SecondScene.h
首先是套一層巨集保護到頭尾
#ifndef __SECOND_SCENE_H__
#define __SECOND_SCENE_H__
#endif // __SECOND_SCENE_H__
然後
#include "cocos2d.h"
現在我開始我們要做一點點小變化,我們讓SecondScene繼承Layer而不是Scene
class SecondScene : public cocos2d::Layer {
public:
static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(SecondScene);
//暫時不需要回調函式
};
實際上之前的cocos例項工程裡HelloWorld都是繼承自Layer的,不知為何現在改了,但是無妨,我們藉此機會介紹Layer和Scene的區別
SecondScene.cpp
首先要include我們之前寫好的SecondScene.h,為了方便我們也和HelloWorld一樣using namespace cocos2d
你可能看到了HelloWorld.cpp裡寫的是
USING_NS_CC
而不是using namespace cocos2d
,但其實效果是一樣的
接下來我們定義creatSceen()函式,也和HelloWorld::createScene()略有不同
Scene* SecondScene::createScene() {
//建立一個Scene類的物件scene
auto scene = Scene::create();
//建立一個SecondScene類的物件layer
auto SecondScene = SecondScene::create();
//把layer新增到scene裡
scene->addChild(layer);
//返回scene
return scene;
}
理解起來很簡單:首先我們建立了一個Scene類的物件scene,然後建立了一個SecondScene類的物件layer(別忘了我們的SecondScene繼承自layer),再把layer新增到了scene裡,最後返回我們的scene
之後就可以開始寫初始化函數了
bool SecondScene::init() {
auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
Label* label = Label::create("Second Test", "fonts/Marker Felt.ttf", 24);
label->setPosition(
Vec2(
origin.x + visibleSize.width / 2,
origin.y + visibleSize.height - label->getContentSize().height
)
);
this->addChild(label); // 預設z-order=0
return true;
}
我們不用加太多東西,加入一個label讓自己知道這是SecondScene場景就行
現在我們去程式的入口,AppDelegate.cpp裡看看,還記得啟動HelloWorld場景的那兩行程式碼麼,我們改成啟動SecondScene場景:
(記得先要加入#include "SecondScene.h"
到AppDelegate.cpp裡)
// create a scene. it's an autorelease object
auto helloWorldscene = HelloWorld::createScene();
// run
director->runWithScene(helloWorldscene);
改成
auto secondScene = SecondScene::createScene();
director->runWithScene(secondScene);
執行測試:
沒有問題
場景切換
接下來我們把AppDelegate.cpp還原回去(Crtl+Z),讓程式執行時還是從HelloWorld場景開始,然後嘗試使用回撥函式切換到SecondScene
由於我們要在HelloWorld切換到Second,所以我們需要在HelloWorld場景裡新增一個回撥事件,剛好HelloWolrd裡就有一個關閉按鈕的回撥,我們改改程式碼就行(這裡重點是演示如何切換,真的不是我懶)
追蹤到HelloWorld::menuCloseCallback()
:
把Director::getInstance()->END
改成
Director::getInstance()->replaceScene(
SecondScene::createScene()
);
執行測試,不出意外再點選HelloWorld右下角的關閉按鈕,就會一瞬間切換到Second
切換動畫
Cocos內建了很多切換場景的動畫,比如
Director::getInstance()->replaceScene(
TransitionSlideInT::create(
3.0f, SecondScene::createScene()
)
);
執行,Second會在3秒內從上平滑切換掉HelloWorld
別的切換動畫就不贅述了,和Actions又異曲同工之妙,各自己試試吧
場景棧
我們之前都是在使用replaceScene進行場景切換,replaceScene會使前一個場景被釋放(簡單來說就是刪掉了,不再佔用記憶體,想找回來只能重新建立一個),節省了記憶體資源,但有時我們不希望場景被釋放怎麼辦呢
Cocos還提供了 推進pushScene()
和彈出popScene
兩種方法,把一系列場景儲存到棧內,按需彈出(釋放)和推進新場景
棧是一種資料結構,你可以簡單理解為一個彈匣,對棧有兩種操作:
- 入棧(push),裝入子彈
- 出棧(pop),射出子彈
子彈總是最上面的先被射出(pop時棧頂的元素最先出棧),也就是“先進後出”
新加入場景時使用pushScene()
,新舊場景就會被存入場景棧,新在上舊在下(也就是說舊的場景沒有釋放),popScene
會將新的場景釋放,舊的場景就被彈了上來原來的位子,顯示舊場景
你可以在場景初始化函式內寫一段等待x秒的程式碼(換個思路,可以寫一個計時器),驗證pop後顯示的舊場景不是新建立的:
bool HelloWorld::init()
{
...
for(int i = 0; i <19999999; i++)// 耗時操作
...
}
發揮你的奇思妙想,還有什麼方法,自己實驗一下
層和場景
這個好理解,想想你玩過的2d遊戲,或者Photoshop的圖層介面,一個場景裡可以有好幾個層
場景內建立層很簡單:
Layer* layer2 = Layer::create();
當然一個層必須新增到場景內才會生效,層無法離開場景獨立存在
之後的學習中我們會進一步體會到層的作