一、cocos creator相关处理
cocos creator做的H5游戏,切换浏览器标签,发现一切都停止了。在creator论坛上,也看到了相关的讨论:
切换标签页,动作动画都暂停了
我做了一个网游,页面切换或最小化时,所有js暂停,怎么解决?
有个层主提供了使用worker解决办法
//bgWorker.js
function timedCount()
{
postMessage(1);
setTimeout("timedCount()", 1000 / 60);
}
timedCount();
//要后台跑的scene加上这个
onEnterBackground : function()
{
console.log("游戏进入后台");
if(!cc.sys.isNative)
{
this.bgWorker = new Worker("res/bgWorker.js");
this.bgWorker.onmessage = function(evt)
{
cc.director.mainLoop();
};
}else {
}
},
onEnterForeground : function()
{
console.log("游戏回到前台");
if(!cc.sys.isNative)
{
if(this.bgWorker != null)
{
this.bgWorker.terminate();
this.bgWorker = null;
}
}else{
}
}
不用每个计时器都用一个worker,一个worker在进后台的时候按帧率,每隔一段时间调一次cc.director.mainLoop()就行了。
二、Laya中切换浏览器标签,不会暂停掉
1.Stage.as
// 各种浏览器兼容
var hidden:String = "hidden", state:String = "visibilityState",
visibilityChange:String = "visibilitychange";
var document:* = window.document;
if (typeof document.hidden !== "undefined") {
visibilityChange = "visibilitychange";
state = "visibilityState";
} else if (typeof document.mozHidden !== "undefined") {
visibilityChange = "mozvisibilitychange";
state = "mozVisibilityState";
} else if (typeof document.msHidden !== "undefined") {
visibilityChange = "msvisibilitychange";
state = "msVisibilityState";
} else if (typeof document.webkitHidden !== "undefined") {
visibilityChange = "webkitvisibilitychange";
state = "webkitVisibilityState";
}
window.document.addEventListener(visibilityChange, visibleChangeFun);
function visibleChangeFun():void {
if (Browser.document[state] == "hidden") {
_isVisibility = false;
if (_me._isInputting()) Input["inputElement"].target.focus = false;
} else {
_isVisibility = true;
}
renderingEnabled = _isVisibility;
_me.event(Event.VISIBILITY_CHANGE);
}
2.Render.as
function Render(width,height){
/**@private */
this._timeId=0;
var style=Render._mainCanvas.source.style;
style.position='absolute';
style.top=style.left="0px";
style.background="#000000";
Render._mainCanvas.source.id="layaCanvas";
var isWebGl=laya.renders.Render.isWebGL;
Render._mainCanvas.source.width=width;
Render._mainCanvas.source.height=height;
isWebGl && Render.WebGL.init(Render._mainCanvas,width,height);
Browser.container.appendChild(Render._mainCanvas.source);
Render._context=new RenderContext(width,height,isWebGl ? null :Render._mainCanvas);
Render._context.ctx.setIsMainContext();
Browser.window.requestAnimationFrame(loop);
function loop (){
Laya.stage._loop();
Browser.window.requestAnimationFrame(loop);
}
Laya.stage.on("visibilitychange",this,this._onVisibilitychange);
}
/**@private */
private var _timeId:int = 0;
/**@private */
private function _onVisibilitychange():void {
if (!Laya.stage.isVisibility) {
_timeId = Browser.window.setInterval(this._enterFrame, 1000);
} else if (_timeId != 0) {
Browser.window.clearInterval(_timeId);
}
}
/**@private */
private function _enterFrame(e:* = null):void {
Laya.stage._loop();
}
可以看到,切换标签进入后台后,Laya会使用浏览器的setInterval方法,每秒一次,继续处理_loop。这个频率已经很低了,因为在正常状态下,是使用Browser.window.requestAnimationFrame来处理_loop的。
在https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame看到如下解释:
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
.
注意:若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()
.
当你准备更新动画时你应该调用此方法。这将使浏览器在下一次重绘之前调用你传入给该方法的动画函数(即你的回调函数)。回调函数执行次数通常是每秒60次,但在大多数遵循W3C建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。为了提高性能和电池寿命,因此在大多数浏览器里,当requestAnimationFrame() 运行在后台标签页或者隐藏的
3.Stage.renderingEnabled
在上面的代码中,出现了renderingEnabled = _isVisibility;
这个属性的注释是这样写的:
设置是否渲染,设置为false,可以停止渲染,画面会停留到最后一次渲染上,减少cpu消耗,此设置不影响时钟。比如非激活状态,可以设置renderingEnabled=false以节省消耗。
另外,还有对帧率的调整:
/**全速模式,以60的帧率运行。*/
public static const FRAME_FAST:String = "fast";
/**慢速模式,以30的帧率运行。*/
public static const FRAME_SLOW:String = "slow";
/**自动模式,以30的帧率运行,但鼠标活动后会自动加速到60,鼠标不动2秒后降低为30帧,以节省消耗。*/
public static const FRAME_MOUSE:String = "mouse";
/**休眠模式,以1的帧率运行*/
public static const FRAME_SLEEP:String = "sleep";
三、cocos中关于requestAnimationFrame
1.CCGame.js
翻看引擎源码,在CCGame.js中找到如下部分:
// @Time ticker section
_setAnimFrame: function () {
this._lastTime = performance.now();
var frameRate = game.config.frameRate;
this._frameTime = 1000 / frameRate;
if (CC_JSB || CC_RUNTIME) {
jsb.setPreferredFramesPerSecond(frameRate);
window.requestAnimFrame = window.requestAnimationFrame;
window.cancelAnimFrame = window.cancelAnimationFrame;
}
...
看一下变化,名字少了几个字母,变成requestAnimFrame。然后是hidden事件:
_initEvents: function () {
var win = window, hiddenPropName;
// register system events
if (this.config.registerSystemEvent)
inputManager.registerSystemEvent(this.canvas);
if (typeof document.hidden !== 'undefined') {
hiddenPropName = "hidden";
} else if (typeof document.mozHidden !== 'undefined') {
hiddenPropName = "mozHidden";
} else if (typeof document.msHidden !== 'undefined') {
hiddenPropName = "msHidden";
} else if (typeof document.webkitHidden !== 'undefined') {
hiddenPropName = "webkitHidden";
}
var hidden = false;
function onHidden () {
if (!hidden) {
hidden = true;
game.emit(game.EVENT_HIDE);
}
}
// In order to adapt the most of platforms the onshow API.
function onShown (arg0, arg1, arg2, arg3, arg4) {
if (hidden) {
hidden = false;
game.emit(game.EVENT_SHOW, arg0, arg1, arg2, arg3, arg4);
}
}
没有做什么特殊处理。如果参考Laya那样,用浏览器的setInterval去每秒调用cc.director.mainLoop(),能不能行呢?就是这样改引擎源码,有点费劲了……