【游客模式】——注册会员,加入11RIA 闪客社区吧!一起见证Flash的再次辉煌……
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
序:这段时间正在整理AS3的教学资料,让我回想起工作和面试中出现的很多问题,这些问题直接或间接的影响了程序员在工作中的编码品质,且在各类教学的资料中也未提及,很多公司的游戏框架和编码规范中也未对代码品质进行严格约束,导致很多程序员工作时一味 序:这段时间正在整理AS3的教学资料,让我回想起工作和面试中出现的很多问题,这些问题直接或间接的影响了程序员在工作中的编码品质,且在各类教学的资料中也未提及,很多公司的游戏框架和编码规范中也未对代码品质进行严格约束,导致很多程序员工作时一味的堆代码,代码臃肿、调用随意、模块混乱、效率质量不可控已是很多游戏公司技术管理人员头疼的问题;
从今天这一篇开始会慢慢收纳和整理关于AS3编程的相关知识点,但不限于高深或稀缺的问题讨论,也会针对初学者收集一些基础的知识点;希望能收到大家的支持和反馈,感兴趣的朋友请关注夜影空间日志AS3知识点分类。
近几年在我手上面试AS3程序员的近百人中,被问到关于Flash Player内部机制在一帧内是如何执行这个问题时,有90%的人答不出来,其中70%左右没有看过此类知识的相关资料,其中50%左右的人从未考虑过每帧时间内除了用户代码外还有其它代码要执行;甚至我问若帧率设置30帧,如果你的某个逻辑函数运行需要30ms时,会不会导致帧率下降?回答是不会下降!我问为什么?回答是因为每帧的运行时间为33.3ms!也就是说根本不知道还有Flash Player内部代码需要处理或是不知道它的处理机制和时间。 在我编写的AS3游戏框架中,封装了动作处理队列管理,所有的处理逻辑都放在动作队列中顺序执行,并且动作分为3个类型:
1.数据通信动作(优先级最高,保证不因为本地逻辑或渲染影响与服务端的同步); 2.逻辑动作(优先级中等,大部分逻辑执行属于此类);
3.后台动作(优先级低,不影响其它执行动作情况下在空闲时执行,通常为不需要立即有结果的异步处理或是预处理动作); 然后在队列管理时进行计算每帧中的动作处理时间,当时间会大于用户代码处理时间(用户代码可处理的时间可测试获得,也可动态在每帧中计算,只要保证每帧间隔即可不掉帧)时,即使队列中还有动作也不会进行执行,让剩余动作在下一帧中处理。不建议在一个动作中编写执行时间过长的代码,我在队列管理中对这类动作进行了警告日志输出,建议将其拆分执行,或是提前放到后台队列中做预处理,甚至可以利用JS或页面上多个swf来做多线程处理。 最终目的是达到平均化执行时间片,不让执行阻塞而导致掉帧,大多数情况下运行是闲置的,在某些交互或处理动作因为业务逻辑求要产生的过大计算或渲染压力,这时让部分逻辑延迟1-2帧处理,也就是33-66ms不会影响体验效果,但是可以保证通信和渲染的流畅; 同样Flash Player内部机制中也做了类似的处理,Flash Player架构师Lee Thomason的演讲原文翻译如下: 理解行模型
执行模型是指Flash Player在每一个帧周期中如何执行相应的指令操作。Flash Player后台事实上运行着n多线程,只是AS并没有给开发人员提供多线程编程模型。这意味着从概念上来讲我们要把Flash Player看做是单线程运行实体,有关这一单线程编程模型的优势/劣势的争论从未休止过,我不想对这一具有争议性的问题做过多评论,但请大家记住这一事实。
可变跑道(Elastic Racetrack)
可变跑道是Flash Player的帧执行模型,这个模型描述了在一帧的处理周期中,代码执行和帧渲染的工作是怎样彼此平衡的。Flash Player 9和AVM2对这一模型进行了一些改进,这一信息是基于对事件机制和渲染模型的研究总结出来的,完整的模型尚未被官方公布。 基本的跑道理论没有发生改变,在Flash Player执行一帧的周期里,前一部分时间用于执行代码,剩余时间用于渲染显示列表中的对象。每个执行阶段都可以根据实际需求增加执行时间来执行更多代码或做更多的渲染工作,而跑道的总长度也将相应增长。 file:///C:\Users\admim\AppData\Local\Temp\ksohtml\wps2967.tmp.png 在前一模型基础上发生改变的是每一阶段在一个微观周期里的样子以及他们怎样形成一帧。 AVM2是由Flash Player中一个叫做Marshal的元帅级组件所操控,Marshal负责将时间切割成Flash Player工作所依的基本时间片,在这里我希望澄清一下Flash Player的时间片跟swf文件运行时的帧速率没有任何关系,我们将最终看到Flash Player是如何将这些时间片合成为一帧。
在Mac OS版Firefox中执行一个由Flex编译得来的swf文件,Marshal通常会将时间切割成19-20毫秒的时间片,时间片大小根据平台和浏览器的不同而存在差异.为方便我们接下来的讨论,我们假定时间片大小为20毫秒,也就是说Marshal每秒钟会产生不超过50个时间片,每个时间片中,五步可能的操作按如下顺序执行:
1、Player事件调度 – 比如Timer事件,鼠标事件,ENTER_FRAME事件,URLLoader事件等等。 2、用户代码执行 – 所有侦听上一步相应事件的代码被执行。 3、RENDER事件调度 – 在用户代码执行期间调用stage.invalidate()会触发这一特殊事件。 4、最后的用户代码执行 – 侦听上述第三步特殊事件的用户代码此时被执行。 5、Player更改显示列表。 file:///C:\Users\admim\AppData\Local\Temp\ksohtml\wps2968.tmp.png
Marshal如此反复的执行20毫秒时间片并在运行中决定下一步操作。一个时间片中执行的所有这些操作最终归纳为上述两段式跑道(代码执行,图像渲染)也就是我们所说的一帧。用户代码和失效操作填充在代码执行区,渲染操作填充在跑道的渲染区段。需要指出的是相关操作只能在Marshal预定的时间内发生,如果你的用户代码很短,那么Marshal仍然会在执行完用户代码后等待一段时间然后进入渲染阶段。
为了更好阐述哪些action被如何执行以及可变跑道如何被创建,请参考如下示例,分别描述了以5fps, 25fps和50fps帧速率工作的swf中时间片是如何被处理的。 file:///C:\Users\admim\AppData\Local\Temp\ksohtml\wps2969.tmp.png
以上示例可以看出,不同的帧速率下,一个帧周期中的可变跑道会执行不同操作,例如对于5fps的swf,每帧处理10个用户action,1个失效action,1个渲染action;帧速率25fps的swf,每帧处理2个用户action,1个失效action,1个渲染action;
对于50fps的swf,每帧只能处理1个用户action,1个失效action,1个渲染action。需要指出的很重要的一点是,某些事件只可能能发生在某些特定的时间片里,比如,Event.ENTER_FRAME事件只能在某一帧的初始时间片中被调度。 一个时间片中代码部分和渲染部分都有可能过长而导致相应时间片大于20毫秒,这就是”可变”的含义,为了保证帧的播放速率仍然接近swf编译时设定的帧率,Marshal会选择丢掉一些时间片。 swf文件的播放速率不可能超过当前Flash Player切割时间片的速率,你可以为swf文件设定120fps的播放速率,但Flash最多可以按照50帧的速度播放(具体数值取决于当前系统的设置)。 代码的执行频率可能比swf的帧率更高,播放一个1fps的swf文件,播放一帧时间为1秒,也就是50个时间片,但在每个时间片里,都会有触发鼠标或计时器事件,尽管只有最后一个时间片才会渲染,另外你可以选择通过调用函数updateAfterEvent() 提前渲染,但只能在鼠标,计时器和键盘事件处理函数中调用。在这种情况下,Marshal会认为当前帧已结束并从下一个时间片起进入下一帧。 最后,如果一个Sprite的外观属性比如width,height等发生变化而将鼠标从该Sprite上方掠过,Flash会进行强制渲染。
如果帧率不是每秒时间片数量的整数因子,那么该平台的渲染时间间隔将变得不固定,比如帧率20fps的swf运行在50个时间片每秒的系统上,Flash Player的行为将是每5个时间片播放两帧,那swf的渲染频率将是2-3-2-3-2-3(时间片)。
|