Tip) implement popScene with Transition

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);
}

ahlwong, I think your solution is great! You just need to add ); after the lambda expressions when calling.

Here are some preprocessor macros that I’ve found useful in simplifying program readability:

#define TO_SCENE(scene, transition, duration)
Director::getInstance()->replaceScene(transition::create(duration,scene::createScene()))

#define PUSH_SCENE(scene, transition, duration)
Director::getInstance()->pushScene(transition::create(duration,scene::createScene()))

#define POP_SCENE(transition, duration)
Director::getInstance()->popScene([](Scene* scene){return transition::create(duration,scene);})

Hi!

Here is a non-intrusive adapter class I’ve crafted that allows one to pop scene with transition without having to change any of the cocos2d sources: http://bit.ly/cocos-pop-transition-hpp

Instead of calling cocos2d::Director::getInstance()->popScene() just call:

cocos2d::Director::getInstance()->pushScene(
     pop_scene_with<cocos2d::TransitionFlipX>::create(1.0f, cocos2d::TransitionScene::Orientation::LEFT_OVER)
);

Note that there is no typical 2nd argument taking the scene for the transition, one simply passes all the arguments they would pass to the transition’s create method except for the 2nd argument carrying the scene.

2 Likes