我们之前讲了场景图(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存放定义

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);
    //暂时不需要回调函数
};

SecondScene.cpp

首先要include我们之前写好的SecondScene.h,为了方便我们也和HelloWorld一样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);

运行测试:

Cocos2d-x入门之旅[4]场景-LMLPHP

没有问题

场景切换

接下来我们把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两种方法,把一系列场景存储到栈内,按需弹出(释放)和推进新场景

新加入场景时使用pushScene(),新旧场景就会被存入场景栈,新在上旧在下(也就是说旧的场景没有释放),popScene会将新的场景释放,旧的场景就被弹了上来原来的位子,显示旧场景

你可以在场景初始化函数内写一段等待x秒的代码(换个思路,可以写一个计时器),验证pop后显示的旧场景不是新创建的:

bool HelloWorld::init()
{
    ...

    for(int i = 0; i <19999999; i++)// 耗时操作

    ...
}

发挥你的奇思妙想,还有什么方法,自己实验一下

层和场景

这个好理解,想想你玩过的2d游戏,或者Photoshop的图层界面,一个场景里可以有好几个层

场景内创建层很简单:

Layer* layer2 = Layer::create();

当然一个层必须添加到场景内才会生效,层无法离开场景独立存在

之后的学习中我们会进一步体会到层的作用

10-24 07:55