Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you load a scene while animating a sprite in cocos2d-x?

I have a "stage selection" scene and a "game" scene. However when the user presses the button to start the game scene there is a delay between the pressing and the scene showing (about 2 seconds or more on older devices). So what i thought is i should create a loading scene.

So what i am doing right now is passing to my "Loading" scene a std::function which gets called 0.1 seconds after the loading scene appears. This function has the code to start the "game" scene like this:

For creating the loading scene.

auto loading_scene = LoadingScene::createLoadingScene([stage_course]() {

    Director::getInstance()->replaceScene(Game::createScene(stage_course->course_id));

});

Director::getInstance()->replaceScene(loading_scene);

To load the game scene.

void LoadingScene::onEnter()
{
    Node::onEnter();

    call_after(0.1, callback_start);  
}

The result of this is the loading scene showing with a simple animated sprite of a character running. At first i tried with a delay of 1.0 seconds before the callback to check that the sprite is working correctly (it does, the character runs). But it stops moving when the callback is executed (the one that loads the new scene) and remains like this for about 1-2 seconds until the scene has loaded and then its presented..

Does anyone know how to keep the sprite animated while the scene is getting load so that it never stops running until the "game" scene is shown?


Edit:

I am using cocos2d-x-3.8.

My loading scene has the following code in its init function to create an animation which is used to animate a spire:

// Create the sprite animation
Animation *animation = Animation::create();
for (int i = 0; i < INT16_MAX; i++)
{
    string frame_sprite_name = StringUtils::format("Interface/loading/0_%d.png",i);

    auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(frame_sprite_name);

    if (frame) {
        animation->addSpriteFrame(frame);
    } else {
        break;
    }
}

animation->setDelayPerUnit(0.15f);


Animate *animate = Animate::create(animation);


// Create a temporal sprite to run the animation

auto temp_sprite = Sprite::create();

temp_sprite->setAnchorPoint(Vec2(0.5,0.5));
temp_sprite->setPosition(Vec2(DISPLAY_WIDTH/2.0f,DISPLAY_HEIGHT/2.0f));


this->addChild(temp_sprite);

temp_sprite->runAction(RepeatForever::create(animate));

Edit 2:

The reason why my game scene takes too much time to load is because i am loading all the spritemaps my stage needs like this:

// Shared

if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(STUDENTS_SPRITE_MAP)) {
    SpriteFrameCache::getInstance()->addSpriteFramesWithFile(STUDENTS_SPRITE_MAP);
}

if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(OTHERS_SPRITE_MAP)) {
    SpriteFrameCache::getInstance()->addSpriteFramesWithFile(OTHERS_SPRITE_MAP);
}

if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(INTERFACE_SPRITE_MAP)) {
    SpriteFrameCache::getInstance()->addSpriteFramesWithFile(INTERFACE_SPRITE_MAP);
}

if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(ZOMBIES_SPRITE_MAP)) {
    SpriteFrameCache::getInstance()->addSpriteFramesWithFile(ZOMBIES_SPRITE_MAP);
}

if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(PORTRAITS_SPRITE_MAP)) {
    SpriteFrameCache::getInstance()->addSpriteFramesWithFile(PORTRAITS_SPRITE_MAP);
}

if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(CUTS_SPRITE_MAP)) {
    SpriteFrameCache::getInstance()->addSpriteFramesWithFile(CUTS_SPRITE_MAP);
}

// Exclusive

if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(TEACHERS_SPRITE_MAP)) {
    SpriteFrameCache::getInstance()->addSpriteFramesWithFile(TEACHERS_SPRITE_MAP);
}
like image 854
Pochi Avatar asked Sep 14 '15 04:09

Pochi


3 Answers

Try this:

void HelloWorld::loadTextures1(){
    Director::getInstance()->getTextureCache()->addImageAsync("sprites1.png", CC_CALLBACK_1(HelloWorld::loadTextures2, this));
}

void HelloWorld::loadTextures2(Texture2D* texture1){
    this->texture1 = texture1;
    CCLOG("Texture 1 loaded!");
    Director::getInstance()->getTextureCache()->addImageAsync("sprites2.png", CC_CALLBACK_1(HelloWorld::loadTextures3, this));
}


void HelloWorld::loadTextures3(Texture2D* texture2){
    this->texture2 = texture2;
    CCLOG("Texture 2 loaded!");
    Director::getInstance()->getTextureCache()->addImageAsync("sprites3.png", CC_CALLBACK_1(HelloWorld::allTexturesLoaded, this));
}

void HelloWorld::allTexturesLoaded(Texture2D* texture3){
    this->texture3 = texture3;
    CCLOG("Texture 3 loaded!");

    auto cache = SpriteFrameCache::getInstance();

    cache->addSpriteFramesWithFile("sprites1.plist", texture1);
    cache->addSpriteFramesWithFile("sprites2.plist", texture2);
    cache->addSpriteFramesWithFile("sprites3.plist", texture3);

    auto scene = GameScene::createScene();
    Director::getInstance()->replaceScene(TransitionShrinkGrow::create(.5, scene));
}

It asynchronously loads these 3 textures and then synchronously load plist files with preloaded textures. There's a bit of freeze on the end (synchronous part), but it's not bad I think. You may also dig deeper into SpriteFrameCache and try to optimise it.

like image 100
Makalele Avatar answered Nov 05 '22 03:11

Makalele


I am not aware that cocos2d-x supports this particularly well:

CSLoader loads the scene on the current thread so it blocks the animation loop until its finished.

To achieve smooth animations wile CSLoader is working it would need to be refactored either * to run in a worker thread - Which is going to be hard as (last time I looked) any Ref derived object is going to panic if its not created on the main cocos thread. * to frequently callout to the Dispatcher / runloop to allow a frame to animate. I havn't looked at the cocostudio::CSLoader code to see how amenable it would be to this... it certainly doesn't seem to support this out the box.

The alternative that might be hard to achieve is to simply break you scene up into chunks - each of which can be quickly loaded so there is no discernible load delay.

like image 1
Chris Becke Avatar answered Nov 05 '22 02:11

Chris Becke


I agree with what Chris is saying, specifically off-loading work or breaking things into chunks.

True, Cocos2d-x is not designed for multi-threading, so that's why you need to implement an update callback that does only a small amount of work, then waits till the next runloop to allow the engine to render.

I wish I can help you solve the loading issues, but the code you are showing is only for loading the animations, which isn't really the problem. We need to see the code that is causing the slow down to provide some practical solutions.

like image 1
Mazyod Avatar answered Nov 05 '22 04:11

Mazyod