Remove scene before placing another

Hi,
I have 2 scenes that occupy like 20 mb each. When I replace one of them with another one, I get memory allocation peak of 40 mb, that it goes back to 20. As far as I understood it happens because firstly the new scene is created and after that the previous one is removed.

How can I remove the scene first and only then allocate a new one?

Thanks.

1 Like

so you are using replaceScene()?

You could use popScene() and pushScene()

You can read about them here: http://www.cocos2d-x.org/reference/native-cpp/V3.2/d7/df3/classcocos2d_1_1_director.html

@slackmoehrle: popScene() and pushScene() would make it worser, because the memory of the scene won’t be freed. So he will have always 40 MB when scene B is pushed (scene A will remain in the memory)

@mmvlad: I am afraid but (i think) that’s not possible. You need a running scene to create a new scene. If you only want to avoid the 40 MB peak than you could try it with a workaround. Create a new scene which is between your 20 MB scenes. The new scene has no content at all, so it shouldn’t use a lot of memory. The only purpose of the new scene is to create your 20 MB scene and replaces it. The peak should than be 21 or 22 MB but not 40.

use a transition scene that consumes very little memory.

what i do is replace scene with a very light weight splash page, which displays and then first the second, heavy scene.

this has proven useful as a place to unload the TextureCache, purge sound effects, etc.

that gives your autorelease pool a chance to flush the first scene.

we created a simple Spine animation that shows a progress bar to make the user happy.

If you don’t want a different splash screen in between two scenes, you could use a RenderTexture to create a screenshot of the outgoing scene to initialize the transition splash screen. This way, the user doesn’t even know there is an intermediate scene between the outgoing and incoming.

Thank you all for the replies.

I was thinking about a small scene in between. Guess this is the most simple solution.

@toojuice good idea, I’ll try this. Thanks.

I was also thinking of showing a splash image in the new scene and than run a new std::thread that will init the main part of the scene and when its done just remove the splash.
But I lack multithreading experience and not sure how thread-safe it is to add children to my scene in a new thread. Does anyone know?

Thanks.

@toojuice. That is an interesting idea!!

I did some testing with my application, two scenes use about 18 megs each.

The best performance came from using replaceScene() onto an empty scene with no contents.

I implemented a basic RenderTexture screenshot but the end result wasn’t very attractive – however, that could because of the visuals in my game, not the overall concept.

With some optimized loading code the interim empty scene is only on the screen for about 0.1s

In my case I use a splash screen and pre-load the sound effects and sprites for the next scene, so the player isn’t waiting after the transitions are done.

@toojuice your idea sounds interesting
but could you tell me in detail how do you make it happen…??

as i am new… so i need some coding idea…
i would appreciate your help. :smile:

and @corytrese i would also like to know about your empty scene and optimized code, preload effects that worked for you… and also… don’t the player get a blank screen when you are replacing to an empty scene… !!

Any help is really appreciated. I need to learn this…
it is important stuff… :smile:

@pabitrapadhy - This is kind of rough and untested (just typed it into the forum directly). The Classes are as follows:

Outgoing - class for the outgoing scene
TransitionSplash - class for the transition scene
Incoming - class for the incoming scene

Outgoing.cpp

void Outgoing::nextScene()
{
    Size visibleSize = Director::getInstance()->getVisibleSize();

    RenderTexture *rt = RenderTexture::create(visibleSize.width, visibleSize.height);

    rt->begin();
    this->visit()
    rt->end();

    Director::getInstance()->replaceScene(TransitionSplash::createScene(rt->getSprite()));
}

TransitionSplash.cpp

Scene *TransitionSplash::createScene(Sprite *transitionSprite)
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();

    // 'layer' is an autorelease object
    auto layer = TransitionSplash::create(transitionSprite);    

    // add layer to scene
    scene->addChild(layer);
    
    // return the scene
    return scene;
}

TransitionSplash *TransitionSplash::create(Sprite *transitionSprite)
{
    TransitionSplash *splash = new TransitionSplash();
    if (splash && splash->init(transitionSprite))
    {
        splash->autorelease();
        return splash;
    }
    CC_SAFE_DELETE(splash);
    return NULL;
}

bool TransitionSplash::init(Sprite *transitionSprite)
{
    // call super init
    if (!Layer::init())
    {
        return false;
    }

    this->addChild(transitionSprite);

    return true;
}

void TransitionSplash::onEnterTransitionDidFinish()
{
    Layer::onEnterTransitionDidFinish();

    Director::getInstance()->replaceScene(Incoming::createScene());
}

NOTE: there may be things you have to do with the RenderTexture like positioning it, anchoring it, and potentially flipping it (for some reason, I have a comment in some of my code that says the sprite returned by RenderTexture was flipped, but not sure why). This will get you started, though.

I use a pair of transitions

 CCDirector::sharedDirector()->popSceneWithTransition<CCTransitionFade>(0.5f);

and then

 CCDirector::sharedDirector()->replaceScene(CCTransitionFade::create(0.5,scene));

So the screen fades out to a black screen (the transition scene) and then fades back in.

The original poster is talking about heavy-weight game scenes, which are similar to my scenario.

In this case, the replaceScene call was taking too long – loading that new game play scene was slow.

So I run code like this:

 SimpleAudioEngine::sharedEngine()->preloadEffect(SFX_FILE_32);
SimpleAudioEngine::sharedEngine()->preloadEffect(SFX_FILE_33);
SimpleAudioEngine::sharedEngine()->preloadEffect(SFX_FILE_62);
SimpleAudioEngine::sharedEngine()->preloadEffect(SFX_FILE_63);

and this

 CCTextureCache::sharedTextureCache()->removeTextureForKey("images/main_menu.jpg");
 CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("sheets/ui_buttons.plist");

Because all this loading takes time, my interim scene is not blank, it has images and a progress bar to keep the user happy while the memory work is done.

The original poster is describing a peak memory usage of 40, which for many devices (on Android) is too high.

I ran into this same problem. Because I cannot control the runtime speed of the devices, I accepted that I needed a responsive interface for the user.

If I used a static image, or a screenshot of the previous screen, the user (depending on their device) would not know if something had gone wrong or what not functional.

The first releases of our early CC2d-x games did not feature progress-bars and loading screens and our analytics package told us we were having users quit / abandon the game during this “freeze” (their words, not mine) between scenes.

In this case, to both achieve an acceptable peak memory usage (AKA, not running two 20M scenes at the same time, as the OP said) and a responsive interface I adopted an animated loading screen.

Examples of the result can be found for free in any of my games, for example, Heroes.

https://play.google.com/store/apps/details?id=com.tresebrothers.games.heroesofsteel

That’s a good point. If your incoming scene take a long time to load, my suggestion would make it look like it froze briefly. In the end, though, it can be modified to add a progress bar or something, so if you still want to show the last image of the outgoing scene while loading, you can.