Automatically Push Scene

Hey, I have a tutorial scene that I’d like to automatically push when my game scene starts for the first time. The scene shows up and looks great when I use my in-game tutorial button, but when I try to start it automatically it just shows a black screen.

    void GameScene::onEnterTransitionDidFinish()
    {
        #pragma mark - Tutorial
        if (!UserDefault::getInstance()->getBoolForKey(USER_DEFAULT_KEY_TUTORIAL))
        {
            showTutorial();
        }
    }

void GameScene::showTutorial()
{
    auto tutorialGame = unique_ptr<TriptychGame>(new TriptychGame(GameType::NORMAL, 7, 7));
    tutorialGame->getBoard()(1, 3) = BoardCell(2, 1);
    tutorialGame->getBoard()(1, 5) = BoardCell(2, 1);
    tutorialGame->getBoard().setCurrentNumber(BoardCell(2, 1));
    tutorialGame->setTutorial(true);
    
    auto tutorialScene = GameScene::createScene(std::move(tutorialGame));
    Director::getInstance()->pushScene(TransitionFlipAngular::create(0.5f, tutorialScene));
}

Does replaceScene show the same behaviour?

Just out of curiosity:
Why not using auto tutorialGame = make_unique<TriptychGame>(GameType::NORMAL, 7, 7); here?

You don’t need auto in your code anyway. Just use the unique_ptr constructor:

unique_ptr<TriptychGame> tutorialGame(new TriptychGame(GameType::NORMAL, 7, 7));

I actually don’t have any reason for the copy, thanks for pointing that out! I was doing shared_ptr assignment in another class recently, so I think I just automatically wrote that. :smile:

I don’t really want to replace scene because I’d like the tutorial to show up on top of the current (paused) game. Replacing the scene would make things more difficult and clumsy.

are you wrapping in a unique_ptr to stop any potential memory leaking?

I’m using a unique_ptr in the GameScene since I want the GameScene to own an instance of my TriptychGame class. When GameScene is destroyed, so will the game instance.

Any thoughts on why pushing a scene from onEnterTransitionDidFinish would cause a black screen?

If thats a bad spot to push a scene, whats a better place to do that?

It wont be a good practice, but you can try to call showTutorial with little delay (0.1 sec or even less)

void GameScene::onEnterTransitionDidFinish()
{
    Layer::onEnterTransitionDidFinish();
    this->runAction(Sequence::create(DelayTime::create(0.1f), CallFunc::create(CC_CALLBACK_0(GameScene::showTutorial, this), NULL));

    // or scheduleOnce(CC_SCHEDULE_SELECTOR(GameScene::showTutorial), 0.1f); 
}

Yes. Wrapping new with a unique_ptr makes the owning pointer to be automatically cleaned up by the smart pointer.

This construct is basically the implementation of make_unique, since they forgot to add that into C++11:

template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args )
{
    return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}

You should always rely on make_unique instead of raw owning pointers(especially for function calls with multiple parameters, as the code evaluation can be interleaved), to produce exception-safe code.

This blog entry explains why: GotW #102: Exception-Safe Function Calls (Difficulty: 7/10) – Sutter’s Mill

1 Like

I tried that earlier but felt very dirty and not proud with myself. It does do the trick though.

If there is a better place to push the scene from, I’d love to know about it!

First thing:

If you override onEnterTransitionDidFinish, you shall call its parent’s one, e.g. Node::onEnterTransitionDidFinish()

Well, your method call is in the perfect callback function and it works as expected, as I just tried it(version 3.4)

I guess the issue is in your code here:

auto tutorialScene = GameScene::createScene(std::move(tutorialGame));

Can you post the code, how you are creating/returning the specific scene?

What version are you using?

static GameScene *create(unique_ptr<TriptychGame> game)
{
    GameScene *scene = new GameScene(std::move(game));
    if (scene)
    {
        scene->init();
        scene->autorelease();
    }
    else
    {
        delete scene;
        scene = nullptr;
    }
    return scene;
}

Scene* GameScene::createScene(unique_ptr<TriptychGame> game)
{
    auto scene = Scene::create();
    auto layer = GameScene::create(std::move(game));

    scene->addChild(layer);

    return scene;
}

I’m on version 3.3.
The original tutorial I followed derived its Scene from Layer, which is why my scenes are created this way.
I have a separate discussion open regarding that here, would love to know why Scenes don’t derive from Scenes.
http://discuss.cocos2d-x.org/t/scene-is-a-layer/

Also, the game scene is also the tutorial scene, with a few flags that allow it to behave in a “tutorial” mode, so the creation process indeed works as expected.

I cant test it, but probably GameScene::onEnterTransitionDidFinish called 2nd time after pushScene

But its being called on a different instance of GameScene, why would that matter?

Well, you don’t derive a scene from a layer. Every layer hast a scene object, to where you are adding the layer as a child.

Because it’s not necessary, as the scene base object has the layer as a child and is added/used by the director.
You only derive a scene from the scene base, to get specific behavior.

Yes. You are just passing it to the constructor. But of which type is TriptychGame?

I guess you nailed it. Seems, that it creates an infinite loop.

You return another GameScene, which overrides onEnterTransitionDidFinish, and is called, which pushes the scene and onEnterTransitionDidFinish is called again, which pushes the scene…

That’s why you should make the tutorial a distinct scene and not a GameScene. This creates an infinite loop, as both scenes have implement the same onEnterTransitionDidFinish callback.

If you really want to use your code, you have to add e.g. a boolean in onEnterTransitionDidFinish, to decide on firing the pushScene call or not. But this is all dirty and hackish, shipping around the real problem: the tutorial not being a distinct scene.

My scenes are based on the HelloWorldScene that is generated with the cocos new -l cpp command. If this is incorrect, then maybe we should look at fixing the cocos utility. Create a new project and take a look at the sample scene, it derives from Layer.

if (!UserDefault::getInstance()->getBoolForKey(USER_DEFAULT_KEY_TUTORIAL) && !game->isTutorial())

My original code didn’t have the !game->isTutorial() check, I removed the code yesterday (because of the black screen) and forgot to re-add that condition when posting here. The result is the same regardless. It works when I add DelayTime, so I’m guessing the issue is related to the transition not being fully finished? I really don’t want to rely on delay time to get this working though as its probably not guaranteed on all devices.

Scenes are directly derived from Node, so are Layers. The scene base object doesn’t have a a layer, as a layer and a scene are two different classes. I’m not quite sure what you mean here.

class CC_DLL Layer : public Node
class CC_DLL Scene : public Node

All of my game logic is separated from cocos2d-x, as I don’t like coupling my game logic with cocos2d-x. TriptychGame is just a class that manages the Player and Board objects. Completely unrelated to the cocos2d-x SDK.

Scenes should encompass Layers etc, not the other way around. The notion of a scene is to encapsulate a series of UI elements such as Layers, Sprites etc. Why should every layer have a scene object that it adds itself to? (I think this is what you just said).

You mean this?

class HelloWorld : public cocos2d::Layer

This is what the cocos utility create. It derives from Layer. Of course, Layer also derives from Node like Scene, but both derive from Node. Layer itself is a Node like Scene is, but they don’t derive from each other.

Well, the creation of the new layer is finished before playing the transition, so should not be the case. But as I posted, I’m doing the same and it works perfect; calling the scene transition to B from A in onEnterTransitionDidFinish from A.

The base type is Node, to make sure everything from Scenes, Layers, Sprites and so on can be animated the same way.

What I mean is, that you could derive your “AwesomeScene” from Scene and use that in your code, because you want behavior, the scene base does not implement. In general, the provided functionality of Scene is sufficient and you can just use that, instead of deriving from it and getting the same functionality.

But that does not change, if your tutorial is a distinct scene, that takes cocos2d-x independent management of the Player and Board objects.
It has also disadvantages. E,g, you have to create an API, to interface your decoupled logic with cocos2d-x entities. Passing textures for the player to the cocos2d-x entities to finally create sprites, instead of letting your Player objects create them on their own.
That’s basically how you are implementing it?

That’s the ancient discussion on various forums and boards :wink:
This is what cocos2d did since the beginning, and as cocos2d-x is a port of that, it sucked that in.

The scene is encompassing the layer, as the layer is added to it as a child. People just think it is wrong to have the layer return it’s scene like a parent widget instead of the scene being the prominent object and returning it’s layer(s).

You have to ask the original developers of cocos2d, why that was their design decision.

As you are working with the layer itself, the scene moves silently into the “background”, so basically your layer IS your scene. If you don’t like it, you don’t have to follow that paradigm. You can do it the way you suggested: having a scene creating layers and manage them.

Back to your blackscreen: can you track the amount, of how often onEnterTransitionDidFinish/the pushing of the scene is called? Maybe you should even create and track time-stamps of creation/init/scene pushes to make sure it is not a timing issue.

I added a boolean to my scene creation function and a check on it inside my tutorial call, which creates the same scene object and pushes it to the scene. This imitates your code. Works totally fine.

We must be overseeing something or it’ could be a bug in 3.3.

Is there a way you can provide the source-code with the basic stuff, which can be compiled?

I’m basically allowing cocos2d-x to make the decisions on what is loaded and drawn based on what the actually underlying game logic is doing. The game logic doesn’t know anything about sprites, pixels and positions, thats all handled within the Scenes. This makes for much more testable code and makes porting easier (i’m assuming).

I was just asking because of your previous response, I didn’t quite understand what you described. Which version of cocos2d-x are you using?

Right, I don’t actually want to derive scene from Layer, and would much rather call a scene a scene. This also lets me construct and manage the scenes easier, but like I said, I learned from HelloWorld, which was using Layer as a base.

Yes, thats exactly what I said. I never actually said I want my scene to device from layer. All I’m saying is what the default HelloWorld is doing, which is what I learned from.

Sure it does, but just porting to another engine. What is the probability of that happening?

I see. I’m using 3.4

I did not meant to derive a scene from Layer. I was just pointing out, that you can create a scene and add the layer to the scene instead of creating the scene inside the layer and add itself to the scene(like in the HelloWorld example.

Yeah, but you don’t have to do it that way if you prefer other ways. Nobody says, that the stuff cocos2d-x does, is the correct way :wink:

Maybe they change that paradigm with v4.

Sorry about the misunderstanding. It’s caused by the naming of the examples.

As you postet the following

I assumed you really mean the type Scene, as you typed it uppercase, and not the scene(the layer).

cocos2d-x names the type scene as Scene, but also names the implementation of the layer a Scene(HelloWorldScene), which is kind of confusing, cause it is in fact a layer with type layer.

We should try and change this in v4. I already don’t do things this way in the Programmers Guide sample code.

1 Like

Yeah. It kind of confuses many many developers. Most of the engines don’t create scenes in a layer, but have the scene as a prominent object, which adds and manages layers.

Maybe it’s a good thing to mention that in the Programmers Guide, so people don’t get confused why those things are implemented different in the code examples/tests.

The naming of the implemented layers as “Scenes” is also not optimal.
Scenes are on one hand really of the type Scene, but on the other hand all the examples treat a layer as a scene as well. That could make beginner’s heads spin :wink:

Is it already decided to drop Layer completely in future versions?

Speaking of changes: it would be nice to switch from callbacks by implementing virtual functions to lambda functions.

1 Like

Yes, it would be nice to have a prominent Scene type that the director deals with. The lambda idea is nice, as long as we still retain callbacks virtual functions. I’m not a huge fan of having lambdas all over the place when I can write a method that looks more clear, and is in a more clear place in the code.