通过onExit()实现游戏场景的暂停

  游戏暂停是一个简单的功能,但只有在实际项目中才会用到。因此学了这么久,才在这里遇到了困难。书本中并未讲解,在网上查阅资料,得到了三点解决方案:

  1.使用Director的pause方法;

  2.先保存屏幕截图,然后push另外一个场景,该场景背景就是此截图。使用RenderTexture动态纹理类保存截图;

  3.遍历节点,调用其onExit()接口。

  先说下前两个解决方案。第一个不知道为什么,启用的时候会出现卡顿,不是我想要的暂停效果。第二个方案,要多写一个暂停场景类,对于游戏体验需求比较大的游戏,自然是必不可少了,但是不适合普通的按钮暂停方式。

  这里我总结第三个方案,那就是使用onExit。根据官方文档,一个Node调用onExit接口,就是在它“退出舞台”时候调用。这个解释实在是很不具体,退出舞台时到底是做了什么?只好查看源代码了。在Node的onExit方法里面,有两个很重要的代码:

this->pause();
for( const auto &child: _children)
        child->onExit();

  可以看到,一个节点onExit后,其子节点也全部onExit,而且它还调用了一个pause方法,那么pause方法又做了什么呢?看代码:

void Node::pause()
{
    _scheduler->pauseTarget(this);
    _actionManager->pauseTarget(this);
    _eventDispatcher->pauseEventListenersForTarget(this);
}

  1.暂停了该节点的schedule;2.暂停了该节点的动作;3.暂停了该节点的事件监听。

  也就是说,除了渲染,其余的所有动态功能几乎全都暂停了。现在就很清楚了:onExit的本质是一个回调函数,是节点在退出舞台时候调用的,功能是让它的节点“收工”。虽然它是一个回调函数,但完全可以手动调用。

  回到问题:如何让游戏场景暂停?

  场景中的主Layer是一个Node,只要主Layer调用了onExit,其所有子节点也会暂停不动了。但是,我们总有一些节点需要“加班”,比如按钮,如果把暂停按钮也onExit了,那么就没办法重新开启游戏了。解决这个办法也很简单,那就是通过遍历子节点,一个一个地onExit暂停它们,同时排除掉不需要暂停的子节点。这样,主Layer虽然没有暂停,但是其所有的该暂停的子节点已经全部暂停了。

  见代码:

auto vec = this->getChildren();
for (auto &child : vec)
{
    if (child->getTag() != PAUSE_MENU)
        child->onExit();
}

  这样,基本就完成了暂停功能。但是还有两个非常重要的点,会对暂停产生致命影响,那就是:

  1.主Layer的事件监听还没有关闭,仍然在接收事件,如触摸;

  2.如果场景带有物理世界,那么物理世界还没有暂停,物理模拟还在继续。

  因此,还需要多调用两行代码才能完成真正的暂停:

_eventDispatcher->pauseEventListenersForTarget(this);

((Scene*)this->getParent())->getPhysicsWorld()->setAutoStep(false);

  这样才算完成了一个完整的暂停效果。而且看上去效率挺快,实际操作上也没出现任何卡顿。最后就是重启游戏了,很简单,反过来调用onEnter即可:节点们,开工了!

原文地址:https://www.cnblogs.com/demon90s/p/4495512.html