cocos2dx场景切换分析之如何自定义场景切换动画

前言:cocos2dx引擎中自带了几十种场景切换的动画,可以使场景切换的过程中平滑过渡,不至于太生硬。那么问题来了,这些动画是怎么实现的呢?如果觉得系统自带的场景切换动画太丑陋,或者满足不了我们游戏的个性化需求,这时我们就完全可以自定义。

PS: 使用引擎版本3.10


一、引擎中怎么实现场景切换动画的?

随便查看一个场景切换动画类(比如TransitionMoveInR、TransitionSlideInL、TransitionShrinkGrow ...等等)的源码,可以看到他们都是继承自Scene类的,那么就说明他们本身就是一个场景。

下面我们跟踪一遍场景切换代码的流程,就可以清晰的明白其中的原理了:

1、先定义一个我们自己需要切换到的普通场景:auto pScene = HomeScene::createScene();  (这个很简单,没什么好说的)


2、使用任意一个场景切换动画包装一下这个场景:auto pTransitionMove = TransitionMoveInR::create(0.6f, pScene);

这段代码,跟进create源码中可以看到调用了bool TransitionScene::initWithDuration(float t,Scene *scene)方法进行初始化:

bool TransitionScene::initWithDuration(float t, Scene *scene)
{
    CCASSERT( scene != nullptr, "Argument scene must be non-nil");

    if (Scene::init())
    {
        _duration = t;

        // retain
        _inScene = scene;    // 我们需要切换到的场景(下一个场景HomeScene)
        _inScene->retain();
        _outScene = Director::getInstance()->getRunningScene();   // 当前显示的场景
        if (_outScene == nullptr)
        {
            _outScene = Scene::create();
        }
        _outScene->retain();

        CCASSERT( _inScene != _outScene, "Incoming scene must be different from the outgoing scene" );
        
        sceneOrder();  // 设置_isInSceneOnTop变量,控制播放动画时,_inScene和_outScene绘制的层级(这里默认就是设置_inScene在上面)
        return true;
    }
    else
    {
        return false;
    }
}
可以看到,这里并没有立即把场景切换到我们的HomeScene场景中去,而只是在TransitionMoveInR(也是一个场景)中简单的保存了下来。


3、执行场景切换代码:Director::getInstance()->replaceScene(pTransitionMove); 跟进replaceScene方法

void Director::replaceScene(Scene *scene)  
{  
    //CCASSERT(_runningScene, "Use runWithScene: instead to start the director");  
    CCASSERT(scene != nullptr, "the scene should not be null");  
      
    if (_runningScene == nullptr) {  
        runWithScene(scene);  
        return;  
    }  
      
    if (scene == _nextScene)  
        return;  
      
    if (_nextScene)  
    {  
        if (_nextScene->isRunning())  
        {  
            _nextScene->onExit();  
        }  
        _nextScene->cleanup();  
        _nextScene = nullptr;  
    }  
  
    ssize_t index = _scenesStack.size();  
  
    _sendCleanupToScene = true;  
    _scenesStack.replace(index - 1, scene);  // 将场景栈(其实就是一个Vector)中保存的当前场景替换为TransitionMoveInR场景  
  
    _nextScene = scene;// 到这里就大概可以看出来了,本质上是先切换到TransitionMoveInR场景,再切换到HomeScene场景,多么巧妙的过渡!  
  
} 


4、到主循环中,开始切换场景。mainLoop() --> drawScene() --> setNextScene(),跟进到setNextScene()方法中看看:

void Director::setNextScene()
{
    bool runningIsTransition = dynamic_cast<TransitionScene*>(_runningScene) != nullptr;
    bool newIsTransition = dynamic_cast<TransitionScene*>(_nextScene) != nullptr;

    // If it is not a transition, call onExit/cleanup
     if (! newIsTransition)
     {
         if (_runningScene)
         {
             _runningScene->onExitTransitionDidStart();
             _runningScene->onExit();
         }
 
         // issue #709. the root node (scene) should receive the cleanup message too
         // otherwise it might be leaked.
         if (_sendCleanupToScene && _runningScene)
         {
             _runningScene->cleanup();
         }
     }

    if (_runningScene)
    {
        _runningScene->release();
    }
    _runningScene = _nextScene;    // 这是当前运行的场景_runningScene就为TransitionMoveInR场景了
    _nextScene->retain();
    _nextScene = nullptr;

    if ((! runningIsTransition) && _runningScene)   // 执行TransitionMoveInR场景的onEnter()和onEnterTransitionDidFinish()方法
    {
        _runningScene->onEnter();
        _runningScene->onEnterTransitionDidFinish();
    }
}

5、跟进TransitionMoveInR场景的 onEnter() 方法(即父类TransitionMoveInL的 onEnter() 方法):

void TransitionMoveInL::onEnter()  
{  
    TransitionScene::onEnter();  
    this->initScenes();   // 初始化场景  
  
    ActionInterval *a = this->action();  
    //_inScene在上面第2点中说了保存的就是HomeScene场景。这里就执行了一个Action动作,就是我们看到的场景切换动画!(因此自定义动画只需要自己在这里修改即可)
    _inScene->runAction(Sequence::create(this->easeActionWithAction(a), CallFunc::create(CC_CALLBACK_0(TransitionScene::finish,this)), nullptr)); // 动画执行完了之后调用回调方法TransitionScene::finish()} 
  

6、跟进回调方法TransitionScene::finish() :

void TransitionScene::finish()  
{  
    // clean up  
    _inScene->setVisible(true);  
    _inScene->setPosition(0,0);  
    _inScene->setScale(1.0f);  
    _inScene->setRotation(0.0f);  
    _inScene->setAdditionalTransform(nullptr);  
  
    _outScene->setVisible(false);  
    _outScene->setPosition(0,0);  
    _outScene->setScale(1.0f);  
    _outScene->setRotation(0.0f);  
    _outScene->setAdditionalTransform(nullptr);  
  
    //[self schedule:@selector(setNewScene:) interval:0];  
    this->schedule(CC_SCHEDULE_SELECTOR(TransitionScene::setNewScene), 0);// 处理非常简单,但是这里为什么要用定时器,而不直接调用,有点奇怪!  
}  
  
void TransitionScene::setNewScene(float dt)  
{      
    CC_UNUSED_PARAM(dt);  
  
    this->unschedule(CC_SCHEDULE_SELECTOR(TransitionScene::setNewScene));  
      
    // Before replacing, save the "send cleanup to scene"  
    Director *director = Director::getInstance();  
    _isSendCleanupToScene = director->isSendCleanupToScene();  
      
    director->replaceScene(_inScene); //关键点就在这里! 这里有调用了一遍replaceScene方法,而_inScene就是我们的HomeScene场景,这下就明白了吧!  
    // issue #267  
    _outScene->setVisible(true);  
}


二、如何自定义一个场景切换动画?

明白了原理,就好办了,不多说。下面就直接给一个我的项目中用到的一个动画吧,很简单!就是实现类似iOS里面的一个功能:当我切换场景到下一个场景后(场景动画是从右向左滑入界面),我需要回退到上一个场景(场景动画是当前界面向右滑出),但是cocos2dx中自带没有这个动画!


1、头文件.hpp:

#ifndef XCaseTransitionMoveOutR_hpp  
#define XCaseTransitionMoveOutR_hpp  
  
#include <stdio.h>  
#include "cocos2d.h"  
  
USING_NS_CC;  
class XCaseTransitionMoveOutR : public TransitionScene, public TransitionEaseScene {  
public:  
    XCaseTransitionMoveOutR();  
    ~XCaseTransitionMoveOutR();  
    static XCaseTransitionMoveOutR* create(float t, Scene* scene);  
    virtual bool init(float t, Scene* scene);  
      
    virtual ActionInterval* action(void);  
    virtual ActionInterval* easeActionWithAction(ActionInterval * action) override;  
    virtual void onEnter() override;  
      
protected:  
    /** initializes the scenes */  
    virtual void initScenes();  
};  
  
#endif /* XCaseTransitionMoveOutR_hpp */

2、实现文件.cpp:

#include "XCaseTransitionMoveOutR.hpp"  
  
XCaseTransitionMoveOutR::XCaseTransitionMoveOutR()  
{  
      
}  
  
XCaseTransitionMoveOutR::~XCaseTransitionMoveOutR()  
{  
      
}  
  
XCaseTransitionMoveOutR* XCaseTransitionMoveOutR::create(float t, Scene* scene)  
{  
    auto newScene = new (std::nothrow) XCaseTransitionMoveOutR();  
    if(newScene && newScene->init(t, scene))  
    {  
        newScene->autorelease();  
        return newScene;  
    }  
    CC_SAFE_DELETE(newScene);  
    return nullptr;  
}  
  
bool XCaseTransitionMoveOutR::init(float t, Scene* scene)  
{  
    bool isInit = false;  
    do{  
        CC_BREAK_IF(!TransitionScene::initWithDuration(t, scene));  
        _isInSceneOnTop = false;  
          
        isInit = true;  
    }while (0);  
    return isInit;  
}  
  
ActionInterval* XCaseTransitionMoveOutR::action(void)  
{  
    Size s = Director::getInstance()->getWinSize();  
    return MoveTo::create(_duration, Vec2(s.width,0));  
}  
  
ActionInterval* XCaseTransitionMoveOutR::easeActionWithAction(ActionInterval * action)  
{  
    return EaseOut::create(action, 2.0f);  
}  
  
void XCaseTransitionMoveOutR::onEnter()  
{  
    TransitionScene::onEnter();  
    this->initScenes();  
      
    ActionInterval *a = this->action();  
      
    _outScene->runAction(Sequence::create(this->easeActionWithAction(a), CallFunc::create(CC_CALLBACK_0(TransitionScene::finish, this)), nullptr)  
     );  
}  
  
void XCaseTransitionMoveOutR::initScenes()  
{  
    _outScene->setPosition(0, 0);  
    _inScene->setPosition(0, 0);  
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值