初级执事
阅读权限 2
积分 1865
侠名
UID 106
主题
帖子
精华
好友
银子
金子
贡献
威望
推广
活跃
荣耀
注册时间 2017-11-14
最后登录 1970-1-1
在线时间 小时
个人主页
|
【游客模式】——注册会员,加入11RIA 闪客社区吧!一起见证Flash的再次辉煌……
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
概述:
flash后台运行帧频低是源自flashplayer插件的问题,为了解决flash在后台运行时少占用些CPU的问题,但却为页游带来了一些麻烦,比如挂机战斗时玩家切换到别的页面去看东西了,就会造成如伤害输出不足,基本停止打怪这样的问题(除非你的战斗逻辑全写在服务端)。
解决方案思路:
起初想利用js的时间间隔相关的方式去实现(setTimeout、setInterval、和支持H5的requestAnimationFrame帧渲染),不过目前很多浏览器对于当前页面最小化或切换了标签进行了优化,不再执行js的这些函数。
360 浏览器 - 非激活无法执行定期执行的函数
- 切换标签无法执行定期执行的函数
IE - 非激活无法执行定期执行的函数
- 切换标签无法执行定期执行的函数
chrome/ff/opera - 非激活可以执行定期函数
- 切换标签无法执行定期函数
所以,换了一种思路:
-- 检测flash帧频低:在enterframe 事件里根据距离上一帧的时间间隔来检测(或者连续几帧时间间隔都很长)并切换至【虚拟渲染模式】
-- 检测flash帧频正常:在js里通过监听页面激活来与flash交互,并切换至【正常渲染模式】,同时在虚拟渲染模式时如果发现距离上一帧时间很短,就恢复至【正常渲染模式】(起到双重保险的作用,确保能够恢复正常)
实现流程:
1-写一个JS,监听页面激活状态并通知FLASH,FLASH会切入到【正常渲染模式】-- 普通的entreframe事件。
2-初始化flash,以embed方式嵌入js文件,并以字节流方式解成字符串,然后通过调用js的方法eval来动态解析执行这段js代码。
3-首先进入正常的渲染模式,enterframe事件
4-在onEnterframe里,检测距离上一帧的时间是否超过一定范围(比如我的是设定为700),如果是的话就切换至【虚拟渲染模式】,关闭掉游戏的主循环
同时以while形式来确保将一帧执行N次游戏循环,根据计算得来的正确的帧间隔时间。
5-在【虚拟渲染模式】中利用与上一次while的间隔时间以及JS监控到页面被激活主动调用FLASH通知切换至【正常渲染模式】(优先)的双重保险下确保回归正常的enterframe事件中。
flash-AS3
package
{
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.external.ExternalInterface;
import flash.text.TextField;
import flash.utils.ByteArray;
import flash.utils.clearTimeout;
import flash.utils.getTimer;
import flash.utils.setTimeout;
[SWF(width="1024",height="576",frameRate="60")]
public class testBackRender extends Sprite
{
// 嵌入的JS文件,以字节流方式
[Embed(source = "render.js",mimeType = "application/octet-stream")]
// 绑定的类
private var jsClass:Class;
/**
* 游戏主循环总执行次数
*/
private var f:int = 0;
/**
* 虚拟渲染时的方法
*/
private var vf:Function;
/**
* 虚拟渲染时用到的setTimeout记录值
*/
private var vfi:*;
/**
* 是否真实渲染模式(enterframe),否则的话采用while虚拟执行游戏主循环
*/
private var isRealRender:Boolean = true;
/**
* 每帧的起始时间
*/
private var lastFtime:Number;
/**
* 每30帧的起始时间
*/
private var frameTime30:Number
/**
* 构造函数
*
*/
public function testBackRender()
{
// 当此容器被添加到舞台时事件
addEventListener(Event.ADDED_TO_STAGE,init);
}
private function init(e:Event):void{
// 读取字节转化为文本(由于文件是使用ANSI储存的,即本地字符编码,大陆的一般英文字母+简体汉字的话 GB2312)
var jsBy:ByteArray = new jsClass() as ByteArray;
jsBy.position = 0;
var jsStr:String = jsBy.readMultiByte(jsBy.length,"gb2312");
// 与JS交互并使用EVAL动态编译执行代码
if(ExternalInterface.available)ExternalInterface.call("eval",jsStr);
// 记录当前帧的时间,每一帧会重新记录的
lastFtime = getTimer();
// 记录每30帧的起始时间
frameTime30 = getTimer();
// 启用真实渲染模式:游戏主循环帧事件
addEventListener(Event.ENTER_FRAME,onFrame);
// 监听来自JS的页面激活事件来主动快速切换到真实渲染模式
if(ExternalInterface.available)
{
ExternalInterface.addCallback("jscall_useLoop",function(){
realRenderStart();
});
}
// debug文本
var tf:TextField = new TextField();
tf.background = true;
tf.backgroundColor = 0x000000;
tf.textColor = 0xffffff;
tf.width = 200;
tf.height = 576;
addChild(tf);
}
/**
* 真实渲染开启
*/
private function realRenderStart():void{
// 如果已处于真实渲染状态则忽略
if(isRealRender)return;
// 标识为真实渲染状态
isRealRender = true;
trace("■■■■■■■■■■■■■■■■■■■■■■■■");
// 游戏主循环开启
removeEventListener(Event.ENTER_FRAME,onFrame);
addEventListener(Event.ENTER_FRAME,onFrame);
// 清空可能存在的虚拟渲染模式中的setTimeout
if(vfi!=null){
clearTimeout(vfi);
vfi=null;
}
}
/**
* 虚拟渲染开启
* 原理:由于一帧为1秒左右的后台运行,可以在这一帧内通过while来分摊虚拟执行N次游戏主循环(根据帧间隔时间)
*
*/
private function virtualRenderStart():void{
// 如果已处于虚拟渲染状态则忽略
if(!isRealRender)return;
// 标识为虚拟渲染状态
isRealRender = false;
trace("□□□□□□□□□□□□□□□□□□□□□□□□");
// 清除游戏主循环帧事件
removeEventListener(Event.ENTER_FRAME,onFrame);
// 虚拟执行游戏主循环,通过While来实现
vf = function():void
{
// 内部检测根据下一帧的间隔时间如果很短说明恢复正常帧频了,这时切换回真实渲染模式即可
// 但通常会被外部的JS通过页面激活事件主动让这里变为真实渲染模式,所以实际上这只是起到保险作用
if(lastFtime){
// 此项是因为可能已经被JS激活了
if(isRealRender==true){
return;
}
//
var ft:Number = getTimer()-lastFtime;
trace("虚拟中的间隔时间",ft)
if(ft<100){
// -- 正常帧频
realRenderStart();
return;
}
}
// 记录while循环开始前的时间
var virtualFrameT1:Number = getTimer();
// 计算帧间隔时间
var frameInterval:Number = (1000/stage.frameRate);
// 记录while循环开始前的时间,并且在期间会没执行一次游戏主循环就刷新记录下
var virtualFrameT2:Number = getTimer();
// 测试用的执行次数
var times:int = 0;
while(true){
// 取得当前的时间
var virtualFrameT3:Number = getTimer();
// 如果距离while起始时间已经超过1800ms的话则要退出等待到下一帧来执行了,总要让flash渲染一下否则就卡死-脚本超时了
// 并且你JS主动切换至真实渲染模式也要等待这个while循环结束,所以我们定为1800ms就结束循环,
// 这个数字比较合理,距离flash下一帧的时间间隔较短(测试了60fps和30fps的情况下)
if(virtualFrameT3-virtualFrameT1>=1800){
// 记录当前帧时间
lastFtime = getTimer();
// 下一帧再重新执行此方法进入新的while
vfi = setTimeout(vf,0);
break;
}
// 超过间隔时间说明要执行一次游戏主循环了
if((virtualFrameT3-virtualFrameT2)>frameInterval)
{
// 执行游戏主循环
onFrame(null);
// 多出来的部分要计算为当前时间-多出来的时间
virtualFrameT2 = virtualFrameT3-((virtualFrameT3-virtualFrameT2)-frameInterval);
// for test
times++;
trace(times,(virtualFrameT3-virtualFrameT2),"ms");
}
}
};
// 立即执行虚拟渲染方法
vf();
}
/**
*EnterFrame事件
* @param e
*
*/
private function onFrame(e:Event):void{
// 计算与上一帧的间隔时间
var ft:Number = getTimer()-lastFtime;
// for test
var tf:TextField = getChildAt(0) as TextField;
f++;
if(f%30==0){
tf.appendText((getTimer()-frameTime30)+"ms "+(e?"[正常]":"[后台运行]")+"\n");
frameTime30 = getTimer();
tf.scrollV = tf.bottomScrollV;
}
// -- 如果处于真实渲染模式的话,检测帧频很低的话就使用切换至虚拟渲染模式
if(isRealRender){
// 记录帧时间
lastFtime = getTimer();
if(ft>=700){
// 清除,以免执行虚拟渲染方法时冲突
lastFtime = null;
// 切换至虚拟渲染方法
virtualRenderStart();
}
}
}
}
}
代码:JS
var flashID = "testBackRender";
window.onfocus = function(){
var flashObj = getFlashObj(flashID);
flashObj.jscall_useLoop();
};
function getFlashObj(name){
var obj = document[name]==null?window[name]:document[name];
return obj;
}
---------------------
作者:黑暗之神KDS
来源:CSDN
原文:https://blog.csdn.net/kdsrpg/article/details/52299228
版权声明:本文为博主原创文章,转载请附上博文链接!
|
评分
-
查看全部评分
|