What's the proper way to implement pause screen?

Let’s say I have a scene which consists of gameplay layer.
Gameplay layer contains several game objects and accepts touch event. Whenever I touch on a pause button, then the game will show pause screen as another layer on top of gameplay layer.

I can implement it in a way that I need to go deep down into each object (enemy, hero, any game object) to plug in “isGamePause” flag in its update() loop to not process any code. This also applies to gameplay layer as well.
This is by mean to allow pause layer to be the only one layer that accept touch event before going back to the game.

I guess that’s too much work, and need to consider the modifying code inside each object is painful.
Is there any better way to do that?

I did it now. In spite of this thread http://www.cocos2d-x.org/boards/6/topics/6518, I don’t need to do that complex operation (involve with dispatcher directly).

For a layer, whenever I need to pause the game, I need to do the following.

  • pause its schedule and actions ***
  • pause all of its game objects inside the layer**[via pauseSchedulerAndActions()]*
  • disable CCMenu object (if any), this will ignore touch event on CCMenu related object ie.CCMenuItemImage ***
  • disable touch event for layer itself**[via setTouchEnabled(false)]*

This leads to info I got from this effort that CCMenu maintains its own touch event separately from its parent layer, thus we need to do additional work.

1 Like

I’m not sure how others like my method versus event handling everything but the way I like to code is this:
I set up the scheduler for my in-game scene to call the in-game process/update ( this->schedule( schedule_selector(CGameScene::Process) ); ). From there, my in-game process calls all processes for in-game activity. This way I can check inside of the in-game process if the game is paused, if my boolean variable (bIsGamePaused) is true, I then process the paused in-game menu, otherwise I process the in-game objects/processes. For me, it sounds a lot less complicated than having to schedule absolutely everything (game objects, etc) and deal with it the way you are currently; doing it this way also allows you to see exactly when each object process/update is being called, which I have found useful for debugging.
EDIT: I use the name “process” instead of “update”, where I have used the word “process” just think of it as “update”.

For example:

void CGameScene::Process()
{
    if (bIsGamePaused)
    {
        m_pPauseMenuLayer->Process();
    }
    else
    {
        // In-game object processing here
        // For example:
        // m_pPlayer->Process();
        // for (int i = 0; i < m_enemiesArray.count(); ++i)
        // {
        //     m_enemiesArray[i]->Process();
        // }
    }
}

Of course, when it comes to touch handling, when the game is paused you want to set the game layer’s TouchEnabled to false and the pause layer’s TouchEnabled to true.

I’m not sure if you’ll like this approach but I thought I would mention it either way as it gives you an alternate approach.

Thanks for your input Tim!
Yes, that style is what I get used to when working with other frameworks. Anyway, I found that I need to stop animation of each object as well, so when I pause the game. I do need to stop it as well combining with my preferred way to use its own schedule method. Thus having both of them in the single place inside a Utility class to handle this externally but globally access, it serves me a purpose.

I do still think there’s a lot of work to be done for pausing the game as not only object’s update but layer’s touch event, and object’s animation as well.

Maybe they can implement a more simplistic pause feature for a layer to temporarily halt all scheduled tasks and pause all animation, including disabling any touches on the layer while still rendering its content.

For now, you could just implement layer handling functionality into a class derived from scene to use for all of your scenes? It might take a little bit of time to make and to add to work with your code at first but once it is done you can use it for all future scenes/layers. If you want help getting on track with it I’d be happy to contribute.

Thanks for offering.

Yes, but right now I didn’t have a base methods for layer to handle its own stuff but Pause/Resume functionality is inside one main gameplay-layer. In which, gameplay-layer uses Utility class to help it out to do stopping logic, animation, touch event, etc for all layers and objects. I can manage the variety of stopping various stuff inside each layer as for now, but it’s better to have base functionality for each layer to derived from.

Yeah sorry I meant setting up functionality for a class that derives from layer, not scene. I see what you mean now, that you are using a class to help out at the moment and have functionality in the gameplay layer to do so but yes, you would be better with base functionality for each layer.

There should be a CCScene::setEnabled* or CCLayer::setEnabled(bool)* method so that a scene stops processing while having other layers still run.

Anyway, I think the best way to do a Pause Screen is to use pushScene() to pause the current scene and run a new scene.

@Lance, Thanks for input Lance.
There’s none. I don’t think I can draw another Scene on top of another, it should be layer so I can’t follow that direction. You can draw another scene on top of other?

You can pause the current scene and have a new one on top of it using *pushScene(). You can then remove the top-most scene usingpopScene()* and the previous scene would then continue processing.

However, what I did was something like this. Basically, just have a flag which determines if the game is paused or not. If it is paused, skip updating, else, update normally.

void MainLayer::setPaused( bool p_Paused ) {
   this -> isPaused = p_Paused;
}

void MainLayer::updateGame( ) {
   if ( this -> isPaused ) {
      // paused, do stuff
   } else {
      // not paused, do stuff
   }
}

// Invoked when pause button is pressed.
void MainLayer::openPauseMenu() {
   // Create pause menu
   PauseMenu * pauseMenu = PauseMenu::createWithGame( this );
   this -> addChild( pauseMenu, INT_TOP, TAG_PAUSE_MENU );
   this -> setPaused( true );
}

// Invoked when resume button is pressed.
void PauseMenu::closePauseMenu() {
   // Resume updating of game.
   this -> game -> setPaused( false );

   // Destroy this object
   this -> removeFromParentAndCleanup( true );
}

@Lance: Hey thanks so much, it also solves the problem cleanly more than my approach.
I really always thought that Scene cannot be draw on top of each other, so pause menu cannot be created with this way, but hey look at that by setting scene (layer)’s alpha and push the scene on stack then it works really well.
Great advice!

It exactly eliminates the need of pause flag for my case as layers are in different scene, it completely pauses out. Its benefit now becomes info.

I would love to know more about your case, I guess it’s within in the same Scene so you still use pausing flag.

Thanks again!

Yes, my method is within the same scene. I did not use *pushScene()* because the pause menu is supposed to be on top of the game itself (the menu is semi-transparent).
Glad I helped.

Hey, that’s my case as well. It works for me. The pause menu is on top of gameplay-scene and its other layers. It’s semi-transparent as well. Did I miss something for your case?

Did you change the pause menu’s alpha to a semi-transparent?
I haven’t tried that, though. I was sure the background would be black. I didn’t know it would work. lol I learned something new today.
Anyway, I’ve already implemented the pause menu using a flag so rewriting it might not be a great idea but I will keep this one on my notes just in case.

Ha ha, yes my pause menu is in semi-transparent. That thought is my first thought so I didn’t use your suggested approach. But it works for me.

It doesn’t work Lance, I repeat it doesn’t work.

For some reasons, my XCode builds and sent the old build to my iPad without any errors (it compiled and successful), I just see the old results.
Now I built for simulator, and it blacks out.

I think his way was similar to the way I was doing it. The pushScene() sounds interesting but I, myself, would prefer keeping all in-game-related content under the same scene.

I changed to basically how you stated you have done in your second post now since I wanted to utilise the scheduler more often in the future and remove the pause flag. I was going to make a base class to derive from for all layers until I realised the only place I can foresee needing the functionality is for my in-game scene class as I won’t be pausing anywhere else (making it pointless to have a base class with this functionality), so I have the game handling it - here is what I have now:

void CGameScene::Process(float _fDeltaTick)
{
    // Game logic here
}
void CGameScene::PauseGame()
{
    // Pause schedules and any running actions and disable touch events on the layer
    this->pauseSchedulerAndActions();
    this->setTouchEnabled(false);

    // Disable touch events for the HUD's menu objects
    m_pHeadsUpDisplayLayer->SetHUDMenuState(false);

    // Show the game menu
    m_pGameMenuLayer->ShowGameMenu();
}
void CGameScene::ResumeGame()
{
    // Resume schedules and any running actions and enable touch events on the layer
    this->resumeSchedulerAndActions();
    this->setTouchEnabled(true);

    // Enable touch events for the HUD's menu objects
    m_pHeadsUpDisplayLayer->SetHUDMenuState(true);

    // Hide the game menu
    m_pGameMenuLayer->HideGameMenu();
}

The SetHUDMenuState function is just this:

void CHeadsUpDisplay::SetHUDMenuState(bool _bIsEnabled)
{
    m_pHUDMenu->setEnabled(_bIsEnabled);
}

The hide and show functions look like this:

void CGameMenu::ShowGameMenu()
{
    // Make the game menu visible and allow touch events for the menu
    m_pMenu->setEnabled(true);
    m_pMenu->setVisible(true);
    this->setTouchEnabled(true);
    this->setVisible(true);
}
void CGameMenu::HideGameMenu()
{
    // Hide the game menu and disable touch events for the menu
    m_pMenu->setEnabled(false);
    m_pMenu->setVisible(false);
    this->setTouchEnabled(false);
    this->setVisible(false);
}

I only hide the menu as I don’t believe in loading in-game menus every time they are opened unless the memory footprint isn’t small and makes constructing and de-constructing or pushing and popping each time worthwhile.

If you know of a more efficient method or if this method is inefficient, let me know. It seems to perform well for me at the moment and I thought I’d post it just for some feedback and for anyone’s reference.

That’s similar to me Tim.

I better share mine as well as it may benefit others who read this stuff.

Note: My structure is that I have a single scene that consists of multiple layers. I have gameplay-layer, hud-layer, hudcontrol-layer, and pause-layer. In gameplay-layer, it’s just like a container that hold other game objects in game session ie. enemies, hero, etc. Hud-layer consists of anything just to show for info but with no interaction. HudControl-layer is the main interaction that users will play with, it accepts touch events to control hero character, it has CCMenu* related class as well. Pause-layer is what I want to show when touch on pause button in hudcontrol-layer.

— Structure —

Root
|__ Scene
      |__ Game Layer
      |__ Hud Layer
      |__ HudControl Layer
      |__ Pause Layer

Utility class
The following is the implementation code inside Utility class. It’s a singleton class that accommodate me in notable things to easily code stuff.
Please note that

getGamePlayScene()

as seen below will return gameplay scene but the scene itself consists of gameplay-layer.

void ZHUtil::pauseScheduleAndActionsForAllLayers()
{
    ZHUtil::sharedInstance()->getGameplayScene()->pauseSchedulerAndActions();
    ZHUtil::sharedInstance()->getGameplayScene()->getStageLayer()->pauseSchedulerAndActions();
    ZHUtil::sharedInstance()->getGameplayScene()->getHUDLayer()->pauseSchedulerAndActions();
    ZHUtil::sharedInstance()->getGameplayScene()->getHUDControlLayer()->pauseSchedulerAndActions();

    CCMenu* menu = (CCMenu*)ZHUtil::sharedInstance()->getGameplayScene()->getHUDControlLayer()->getChildByTag(MENU);
    if(menu != NULL)
    {
        menu->setEnabled(false);
    }
}

void ZHUtil::resumeScheduleAndActionsForAllLayers()
{
    ZHUtil::sharedInstance()->getGameplayScene()->resumeSchedulerAndActions();
    ZHUtil::sharedInstance()->getGameplayScene()->getStageLayer()->resumeSchedulerAndActions();
    ZHUtil::sharedInstance()->getGameplayScene()->getHUDLayer()->resumeSchedulerAndActions();
    ZHUtil::sharedInstance()->getGameplayScene()->getHUDControlLayer()->resumeSchedulerAndActions();

    CCMenu* menu = (CCMenu*)ZHUtil::sharedInstance()->getGameplayScene()->getHUDControlLayer()->getChildByTag(MENU);
    if(menu != NULL)
    {
        menu->setEnabled(true);
    }
}

void ZHUtil::pauseScheduleAndActionsForAllEnemies()
{
    CCArray* enemies = ObjectSpawner::sharedInstance()->getEnemies();

    for(int i=0; icount(); i++)
    {
        CharacterAI* ai = (CharacterAI*)enemies->objectAtIndex(i);
        ai->pauseSchedulerAndActions();
    }
}

void ZHUtil::resumeScheduleAndActionsForAllEnemies()
{
    CCArray* enemies = ObjectSpawner::sharedInstance()->getEnemies();

    for(int i=0; icount(); i++)
    {
        CharacterAI* ai = (CharacterAI*)enemies->objectAtIndex(i);
        ai->resumeSchedulerAndActions();
    }
}

void ZHUtil::disableTouchForAllLayers()
{
    ZHUtil::sharedInstance()->getGameplayScene()->setTouchEnabled(false);
    ZHUtil::sharedInstance()->getGameplayScene()->getStageLayer()->setTouchEnabled(false);
    ZHUtil::sharedInstance()->getGameplayScene()->getHUDLayer()->setTouchEnabled(false);
    ZHUtil::sharedInstance()->getGameplayScene()->getHUDControlLayer()->setTouchEnabled(false);
}

void ZHUtil::enableTouchForAllLayers()
{
    ZHUtil::sharedInstance()->getGameplayScene()->setTouchEnabled(true);
    ZHUtil::sharedInstance()->getGameplayScene()->getStageLayer()->setTouchEnabled(true);
    ZHUtil::sharedInstance()->getGameplayScene()->getHUDLayer()->setTouchEnabled(true);
    ZHUtil::sharedInstance()->getGameplayScene()->getHUDControlLayer()->setTouchEnabled(true);
}

GameplayScene
Look at the following Pause/Resume method

void GameplayScene::pauseGame()
{
    // pause schedule and actions
    ZHUtil::sharedInstance()->pauseScheduleAndActionsForAllLayers();
    ZHUtil::sharedInstance()->pauseScheduleAndActionsForAllEnemies();
    m_kiki->pauseSchedulerAndActions();
    // disable touch
    ZHUtil::sharedInstance()->disableTouchForAllLayers();
}

void GameplayScene::resumeGame()
{
    // resume schedule and actions
    ZHUtil::sharedInstance()->resumeScheduleAndActionsForAllLayers();
    ZHUtil::sharedInstance()->resumeScheduleAndActionsForAllEnemies();
    m_kiki->resumeSchedulerAndActions();
    // enable touch
    ZHUtil::sharedInstance()->enableTouchForAllLayers();
}

As usual, I also set the flag appropriately and then use utility method from ZHUtil class to do things for it. In this case I need to do operations as I noted out in the 2nd comment above.
It may look a little bit confuse to you as I want GameplayScene to control Pause/Resume flow, the logic is inside this class. But the actual touch event comes when we touch pause button lie inside hudcontrol-layer.

HudControl Layer

void HUDControlLayer::_menuPauseCallback(CCObject* pSender)
{
    Core::sharedCore()->log("Pause button is clicked.");
    // pause the game
    ZHUtil::sharedInstance()->getGameplayScene()->pauseGame();
    // add pause layer to the root of GameplayScene
    PauseLayer* pauseLayer = PauseLayer::create();
    ZHUtil::sharedInstance()->getGameplayScene()->getParent()->addChild(pauseLayer, TOPMOST_LAYER, PAUSE_LAYER);
}

When the button is touched, then I pause the game (according to logic) and pop up a pause-layer. Thus pause-layer is responsible for future going back to normal game session.

I involved much of ZHUtil there, but it’s my current code-base and style and also works fine.
Let me know if you have any thought or would like to discuss.

EDIT: I removed out the setting of pause-flag as we don’t really need it because we already pause the schedule so the code won’t come in.

Yes that is very similar. I used to use singletons but I avoid using singletons in most cases which is why I don’t have any, my structure looks like this:

Root
|__ MainMenuScene              // The main menu scene
    |__ Main Menu Layer        // Contains everything main menu related
|__ GameScene                  // The in-game scene
    |__ Game Layer             // Contains everything in-game related
    |__ Heads Up Display Layer // This encapsulates all HUD features
       |__ Controls Class      // This encapsulates the controls for the HUD layer by separating it from all of the other HUD features
    |__ Game Menu Layer        // This is the pause menu for the game

I like to encapsulate my classes in a way that makes sense to me, therefore, in my mind I see it where the game ‘has’ a game menu and ‘has’ a HUD and of course ‘has’ all of the game objects which is why I made them a part of the game layer.
Although, as you say yours does too, my GameScene contains all the functionality for the game layer because of how cocos2d-x is done. So, it does have the functionality inside the game layer. I should probably share how I create the layers for the game scene and game layer for other’s reference as well (I removed everything inside my initialisation other than the layer details):

bool
CGameScene::init()
{
    bool bIsInitialised = false;

    // Initialise the layer's base class
    if( CCLayer::init() )
    { // Initialise the in-game layer
        // Create and initialise the HUD; the HUD is an auto-release object so it needs to
        // be retained and later released on de-construction
        m_pHeadsUpDisplay = CHeadsUpDisplay::create();
        m_pHeadsUpDisplay->retain();

        if (m_pHeadsUpDisplay)
        {
            // Create and initialise the Game Menu; the Game Menu is an auto-release
            // object so it needs to be retained and later released on de-construction
            m_pGameMenu = CGameMenu::create();
            m_pGameMenu->retain();

            if (m_pGameMenu)
            {
                // Hide the game menu and disable touch events for the menu
                m_pGameMenu->HideGameMenu();

                // Initialisation successful
                bIsInitialised = true;
            }
        }
    }

    return (bIsInitialised);
}

cocos2d::CCScene*
CGameScene::CreateGameScene()
{
    // Declare the Game scene; the scene is an auto-release object
    CCScene* pGameScene = 0;

    // Instantiate the Game scene
    pGameScene = CCScene::create();
    if (pGameScene)
    {
        // Declare and instantiate the Game layer; the layer is an auto-release object
        CGameScene* pGameLayer = CGameScene::create();

        if (pGameLayer)
        {
            // add the Game layer to the Game scene
            pGameScene->CCNode::addChild(pGameLayer, 0);

            // add the Game layer's Heads Up Display layer to the scene with a z-order of 1 to
            // ensure that it shows on-top of the Game layer
            pGameScene->CCNode::addChild(pGameLayer->GetHeadsUpDisplay(), 1);

            // add the Game layer's Game Menu layer to the scene with a z-order of 2 to ensure
            // that it shows on-top of the Game layer and the Heads Up Display layer
            pGameScene->CCNode::addChild(pGameLayer->GetGameMenu(), 2);
        }
    }

    // return the Game scene
    return (pGameScene);
}

At the moment I cannot see a better way around doing the pause menu state with cocos2d-x against the way we have done it although I have only been working with the engine for a short time; I am glad to see that we have taken the same approach as it means I don’t have to change anything, haha. If you do come up with a better work around, let me know. Hopefully this thread should serve as a guideline to new people trying to work this out.

The way you have done isn’t confusing, I understand how you are doing it as I also have it basically the same where the pause button lies inside the HUD layer:

void
CHeadsUpDisplay::GameMenuButtonCallback(cocos2d::CCObject* _pSender)
{
    m_pGameLayer->PauseGame();
}

The game layer is passed into the HUD on initialisation to prevent the need for it to be a singleton. Doing it this way allows anything that needs to know about it to know about it while preventing anything unrelated to the game layer from having access to it.
If you are wondering why I choose to avoid singletons even when they can make it easier - the only reason I bother is partly because they are frowned upon but mostly because not using them makes me consider better design rather than having a crutch to avoid thinking about better design; I have found not considering alternate design has made re-designing very time-consuming in comparison to designs where I have not used singletons.

EDIT: (@Wasin) Is there a reason for your pause flag? I was curious as to why you still have a pause flag but forgot to ask why.