Tip) implement popScene with Transition

refer:

http://www.cocos2d-iphone.org/forum/topic/1076

implement:

CCDirector.h

    template
    void popSceneWithTransition(float t) {
        CCAssert(m_pRunningScene != NULL, "running scene should not null");
        m_pobScenesStack->removeLastObject();
        unsigned int c = m_pobScenesStack->count();
        if (c == 0) {
            end();
        }
        else {
            m_bSendCleanupToScene = true;
            m_pNextScene = (CCScene*)m_pobScenesStack->objectAtIndex(c - 1);
            CCScene* trans = T::transitionWithDuration(t, m_pNextScene);
            m_pobScenesStack->replaceObjectAtIndex(c-1, trans);
            m_pNextScene = trans;
        }
    }

usage:

CCDirector::sharedDirector()->popSceneWithTransition(0.4);

goodluck!
:slight_smile:

Many, many thanks to you :slight_smile:

Hey guys, this do not work anymore in 2.1…

Any hints? (yeah I have changes the calls to the ones in 2.1… like create and everything, but without success)

Hi, Pedro

This little bit changed code must work:

template
    void popSceneWithTransition(float t) {
        CCAssert(m_pRunningScene != NULL, "running scene should not null");
        m_pobScenesStack->removeLastObject();
        unsigned int c = m_pobScenesStack->count();
        if (c == 0) {
            end();
        }
        else {
            m_bSendCleanupToScene = true;
            m_pNextScene = (CCScene*)m_pobScenesStack->objectAtIndex(c - 1);
            CCScene* trans = T::create(t, m_pNextScene);
            m_pobScenesStack->replaceObjectAtIndex(c-1, (CCObject *)trans);
            m_pNextScene = trans;
        }
    }

Good luck! Happy coding and great profits to you! :slight_smile:

In fact I have done this, my error was that my CCDirector had called PAUSE beforte the transition, what simply breaks it.

I also removed the replaceObjectAtIndex, since it was given an unknow error when compiling to Android, and in fact it doesn’t make any difference :wink:

template
        void popSceneWithTransition(float t) {
            CCAssert(m_pRunningScene != NULL, "running scene should not null");

            m_pobScenesStack->removeLastObject();
            unsigned int c = m_pobScenesStack->count();

            if (c == 0) {
                end();
            }
            else {
                m_bSendCleanupToScene = true;
                m_pNextScene = (CCScene*)m_pobScenesStack->objectAtIndex(c - 1);
                CCScene* trans = T::create(t, m_pNextScene);
                //m_pobScenesStack->replaceObjectAtIndex(c-1, (CCScene*)trans);
                m_pNextScene = trans;
            }
        }

Here is the code as I am using! Thank you for your help!

Very handy for most transitions, but some like CCTransitionPageTurn requires a second param. I added another function to handle that one:

    template 
    void popSceneWithTransition(float t, bool flag) {
        CCAssert(m_pRunningScene != NULL, "running scene should not null");

        m_pobScenesStack->removeLastObject();
        unsigned int c = m_pobScenesStack->count();

        if (c == 0) {
            end();
        }
        else {
            m_bSendCleanupToScene = true;
            m_pNextScene = (CCScene*)m_pobScenesStack->objectAtIndex(c - 1);
            CCScene* trans = T::create(t, m_pNextScene, flag);
            m_pNextScene = trans;
        }
    }

But there are more special cases like CCTransitionFade, and orientations on CCTransitionFlipX/Y/Angular. Thoughts on how to combine all together, or are individual functions going to have to suffice?

UPDATE:

After running into a few compiler errors with the previous method, I came up with a different (better?) way instead.

In Director.h:

        CCScene *previousScene(void) {
            unsigned int c = m_pobScenesStack->count();
            if (c <= 1) return NULL;
            return (CCScene*)m_pobScenesStack->objectAtIndex(c - 2);
        }

        void popScene(CCScene *trans) {
            CCAssert(m_pRunningScene != NULL, "running scene should not null");
            m_pobScenesStack->removeLastObject();
            unsigned int c = m_pobScenesStack->count();

            if (c == 0) {
                end();
            }
            else {
                m_bSendCleanupToScene = true;
                m_pNextScene = trans; // (CCScene*)m_pobScenesStack->objectAtIndex(c - 1);
            }
        }

Usage:

    CCScene *prevScene = CCDirector::sharedDirector()->previousScene();
    CCTransitionScene* transition = CCTransitionPageTurn::create(0.66, prevScene, false);
    CCDirector::sharedDirector()->popScene(transition);

It’s a little more work, but highly more flexible as you can use all the scene transition options.

As I am somewhat new to Cocos2d-x and C++, please scrutinize and tell me what will go wrong with it.

1 Like

Great work Justin Hawkwood, works like a charm. Is there a reason why this code isn’t integrated in cocos2d-x?

Why this code isn’t integrated in cocos2d-x?

+1

Alternatively, one could make popScene() take a lambda that does the wrapping of the previous scene. So calling it would look something like:

Director::getInstance()->popScene([](Scene *prevScene) {
    return TransitionMoveInR::create(0.5, prevScene);
});

This avoids exposing the previous scene directly.

Just a thought, in case anyone is considering adding this to cocos2d-x. I’d love to see that happen, whatever approach is used.

e.

Actually, that turns out to be a tiny change. I’m working with cocos2d-x-3.0beta here.

diff --git a/cocos2d/cocos/2d/CCDirector.cpp b/cocos2d/cocos/2d/CCDirector.cpp
index 2778596..02ce2a9 100644
--- a/cocos2d/cocos/2d/CCDirector.cpp
+++ b/cocos2d/cocos/2d/CCDirector.cpp
@@ -676,6 +676,13 @@ void Director::popScene(void)
     }
 }
 
+void Director::popScene(std::function&lt;Scene*(Scene*)> wrappingFunc) {
+    popScene();
+    if (_nextScene) {
+        _nextScene = wrappingFunc(_nextScene);
+    }
+}
+
 void Director::popToRootScene(void)
 {
     popToSceneStackLevel(1);
diff --git a/cocos2d/cocos/2d/CCDirector.h b/cocos2d/cocos/2d/CCDirector.h
index 10f46bb..2c9a08b 100644
--- a/cocos2d/cocos/2d/CCDirector.h
+++ b/cocos2d/cocos/2d/CCDirector.h
@@ -257,6 +257,7 @@ public:
      * ONLY call it if there is a running scene.
      */
     void popScene();
+    void popScene(std::function&lt;Scene*(Scene*)> wrappingFunc);
 
     /** Pops out all scenes from the stack until the root scene in the queue.
      * This scene will replace the running one.

And my calling code looks like:

auto f = [](Scene* scene) {
    return TransitionMoveInL::create(0.1, scene);
};
Director::getInstance()->popScene(f);

I’m sure I broke every cocos2d-x coding convention there is. But you get the idea, I hope. It’s working for me.

Thanks, @frognozzle :slight_smile:

I ‘improved’ on it slightly by removing a single line of your code. _nextScene is used when changing between scenes in all cases, so by removing popScene() from your code, it can be used for ALL pop*() methods.

Eg

void Director::wrapNextScene(std::function<Scene*(Scene*)> wrappingFunc) 
{
    if (_nextScene) {
        _nextScene = wrappingFunc(_nextScene);
    }
}

Example usage

Director::getInstance()->popToRootScene();
Director::getInstance()->wrapNextScene([](Scene* scene) {
	return TransitionZoomFlipAngular::create(0.5f, scene, TransitionScene::Orientation::LEFT_OVER);
});

The code is a strange middleground between beautiful and disgusting, isn’t it? In any case, it works well and it’s simpler than changing creating three new pop*() methods. I’ll leave it to the devs to do a more complete job. :slight_smile:

1 Like

Worked perfectly! I agree it doesn’t make sense that CCDirector doesn’t have these methods built in :/.

+1

Anyone have any idea why this wouldn’t work on the Linux platform?

I get a Segmentation Fault using the code in

With 2.2 on Linux.

Here is the code for Cocos2d-X v3.2
Add in Director.h after void popScene(void);

Scene *previousScene(void);
void popScene(Scene *trans);

Add in Director.cpp after void Director::popScene(void)

Scene* Director::previousScene(void) {
unsigned int c = _scenesStack.size();

if (c <= 1) return NULL;
return (Scene *) _scenesStack.at(c - 2);
}


void Director::popScene(Scene *trans) {
CCAssert(_runningScene != NULL, "running scene should not null");
_scenesStack.popBack();
unsigned int c = _scenesStack.size();

if (c == 0) {
    end();
}
else {
    
    _sendCleanupToScene = true;
    _nextScene = trans;
}
}

Usage:

Scene *prevScene = CCDirector::getInstance()->previousScene();
TransitionScene* transition = TransitionFade::create(1.0, prevScene);

Director::getInstance()->popScene(transition);
2 Likes

Have you tried to implement it similar to runscene/push scene ?

@catch_up
You may want to use:

Director::getInstance()->runWithScene(TransitionFade::create(1.0f, newSceneToTransition));

Director::getInstance()->pushScene(TransitionFade::create(1.0f, newSceneToTransition));

No… I just wanted to ask that they way you implemented transitions for pop scene. I mean the code that you changed under Director.h/cpp . Is it similar to cocos2d-x implementation of push/runWithScene ?

@saffronstate thanks!! I am using cocos2dx 3.3 final and it just worked with charm :slight_smile:

The above implementations work up to 3.14 however, I think this solution is more elegant and requires less changes:

Replace the method declarations in CCDirector.h with these that take a function that wraps the new scene:

void popScene(std::function<Scene*(Scene* scene)> transition = [](Scene* scene){ return scene; });
void popToRootScene(std::function<Scene*(Scene* scene)> transition = [](Scene* scene){ return scene; });
void popToSceneStackLevel(int level, std::function<Scene*(Scene* scene)> transition = [](Scene* scene){ return scene; });

And replace the declarations in CCDirector.cpp with these:

void Director::popScene(std::function<Scene*(Scene* scene)> transition)
void Director::popToRootScene(std::function<Scene*(Scene* scene)> transition)
void Director::popToSceneStackLevel(int level, std::function<Scene*(Scene* scene)> transition)

Wrap your _nextScene = lines in the above methods like so:

_nextScene = transition(_scenesStack.at(c - 1)); // in popScene

popToSceneStackLevel(1, transition); // in popToRootScene

_nextScene = transition(_scenesStack.back()); / /in popToSceneStackLevel

Notice the wrapping function has a default function supplied, so you can continue to use these methods without supplying a transition function.

You can now call these functions like so:

popScene([](Scene* scene) {
    return TransitionMoveInL::create(0.3f, scene);
}

popToRootScene([](Scene* scene) {
    return TransitionMoveInL::create(0.3f, scene);
}

popToSceneStackLevel(5, [](Scene* scene) {
    return TransitionMoveInL::create(0.3f, scene);
}