Weitere ähnliche Inhalte Mehr von Shanda innovation institute (14) [Flash开发者交流][2010.07.24]flash mmorpg游戏引擎及工具开发概述(张明光)3. WEBGAME 核心(引擎)基础架构 1.游戏核心架构设计 核心(引擎)是由多个系统组成的共同体,每个系统都负责管理自己 专属的职能,系统间协调工作最终完成一项或多项功能(效果). 组成: 1.事件心跳机制(系统) 解决交互,互动等问题 2.对象管理机制(系统) 抽象对象,类架构 3.资源管理机制(系统) 各种资源或配置文件管理 4.通信管理机制(系统) 解决核心内外通信问题 特性: 1.有效地提高开发效率 2.有利于团队分工开发 3.有利于2次开发及扩展 6. 指令管理器(职能管理器)----事件,心跳管理中心 事件,心跳管理机制 定义:由心跳指令及事件指令这两种实现了 IOrder接口的指令来作为通信信息载体,并通过指令管代理进行注册分流.到其从属管理器 (心跳管理器,事件管理器)进行对循环或触发类交互的管理中心 特点: 1. 通信载体实现了共同的接口(Iorder) 2. 以回调机制取代事件机制的大部分功能. 组成: 1.事件管理器 AS3事件机制的特征 2.心跳管理器 心跳管理的注意事项 public interface IOrder { function get execHandler():Function; function get callbackHandler():Function; function setUp(execHandler:Function,args:Array, callbackHandler:Function) } 7. 一.命令管理模式 public interface IOrder { functionset id(value:int):void; function get id():int function execHandler():Object; function callbackHandler(args:Array):Object; function setUp(execHandler:Function,args:Array, callbackHandler:Function); } 指令模式与as3的事件机制 的优势与区别: 1.事件机制传递一个事件, 需要放到事件流当中. 要一层接一层的封装事件. 然后通过实现了IEventDispatcher接口的对象将一个事件派发出去. 这个过程可能创建的实例很多.而且因为要安插到事件流当中进行冒泡遍历.当注册的事件越多消耗越大. 2.指令模式则是以预先封装好的方法及回调方式组装成一个指令原型,通信过程不需理会嵌套的层级数,直接对应用对原型的方法进行回调,而且指令原型实例只有相关的2个方法没有其他相关属性.不像事件机制需要整个对象进行引用,再加上生命周期结束后便可方便御载,销毁. 事件,心跳管理机制 9. 心跳管理器: 心跳:以一定的时间间隔为标注,间隔时间内调度一次的事件模式. 目的:更好更合理的对需要进行心跳的对象进行统一管理,且节省不必要的性能浪费. 途径: DisplayObject.ENTER_FRAME: 特征:动作的执行以帧频为频率.每次执行必须最大程度保证当前帧的逻辑及渲染已经完成.(根据执行的情况可能会出现丢帧显现) 适合于偏向渲染的心跳处理.如组件,可视对象涉及渲染的属性修改.等 Timer 特征:能自定义制定的时间间隔进行心跳处理而不依赖于帧频,但帧频的执行对其也有一定影响.执行的间隔的准确度跟当然cpu消耗有直接关系. 适用于偏向逻辑型运算的心跳处理 setInterval 是一个封装好的Timer管理器.但没有实现群组,队列等功能的Timer的工厂模式实现. 于as2时的区别主要在于as2时setInterval只要事件间隔到,就会马上执行下一轮代码块, 而不管之前的代码块是否已经执行完毕. setTimeout 能在执行完制定次数后自动销毁的计数器,是基于setInterval 的一个实现扩展. 功能要求:能实现动态创建timer并对相应注册的指令进行操作. 对于相同心跳的指令进行群组共同注册到统一timer心跳下以减少timer数目增多导致的性能下降. 能进行 增,删,改,查,销毁等操作.根据实际需求还可以实现对同一对象下所有心跳频率的统一停止 开始,销毁等功能. 该部分功能特别适用对于管理场景下不同角色或怪物的速度,逻辑等 事件,心跳管理机制 10. 破解 playerglobal.swc的 SetIntervalTimer部分实现的代码 package flash.utils { import flash.events.*; final class SetIntervalTimer extends Timer { varid:uint; private varrest:Array; private varclosure:Function; private static varintervals:Array = []; function SetIntervalTimer(closure:Function, delay:Number, repeats:Boolean, rest:Array) { super(delay, repeats ? (0) : (1)); this.closure = closure; this.rest = rest; addEventListener(TimerEvent.TIMER, this.onTimer); start(); this.id = intervals.length + 1; intervals.push(this); return; }// end function private function onTimer(event:Event) : void { this.closure.apply(null, this.rest); if (repeatCount == 1) { if (intervals[(this.id - 1)] == this) { delete intervals[(this.id - 1)]; } } return; }// end function static function clearInterval(id_to_clear:uint) : void { id_to_clear = id_to_clear - 1; if (intervals[id_to_clear] is ) { intervals[id_to_clear].stop(); delete intervals[id_to_clear]; } return; }// end function } } 事件,心跳管理机制 心跳管理器: 基于与事件管理器相同统一的管理接口 对核心的所有交互对象的循环间隔调用进行 集中优化管理. 注意: 1.timer实例越少越好,相同间隔的轨到统一timer管理. 2.相同倍数的间隔timer,改用最小公倍数为间隔. 3.逻辑型心跳适用Timer实现,重绘型适宜用EnterFrame 4.实现对象实例的所有心跳命令的统一分组,回收或暂停 12. 通信管理与模块 通信管理机制 作为游戏核心的一个重要系统机制的同时也是沟通不同系统的的桥梁 分类: 通信管理: .系统通信管理 1.全局数据的存储,查询 2.配置数据及常量的管理 3.统一接口形式访问管理 .模块通信管理: 对所有模块都将注册其中并对 其进行管理,模块通过接口方法send将信息发送模块管理器,并由模块管理器发送信件 对内通信:(核心内) 系统间通信 系统内部通信 对外通信:(核心外或2次开发) 1.模块间通信 2.模块内部通信 3.与服务端等外部程序间的通信 信件指令 -----扩展命令模式的通信载体 Message=Head+Body 一个Message(信件)应由两部分组成即Head(信件头,标记信件的通信类型,发信人,收信人等基本信息的载体)及Body(信件主体,通信的行为逻辑,及回调等具体方式的Iorder接口的实现) 13. MODULE—模块 通信管理机制 说明: 核心(引擎)开发的模块部分主要是为了解决 核心外或2次开发时不同功能组成的协调工作,而提供一种模板式开发方式. 并且封装及提供统一的通信模式 模块间通信(ModuleToModule) 模块与服务端通信(ModuleToSevice) 模块内部的通信(SubModule--订阅式通信) 模块化开发 模块-Moudle(MVC) public interface IModule { function get view():IBaseSprite function set view(value:IBaseSprite):void function register(moduleName:String):void function send(message:IMessage):void functionget proxy():IProxy } 31. 解决模块内部的通信问题--------SubModule( 订阅模块) 通信管理机制 模块一般是一个mvc结构 这也意味着一个模块将会有多个界面组成,但功能模块的唯一性. 由此而导致的问题是 1.怎么在模块的某些子界面里调用模块的通信接口进行通信 2.模块在接受到别的模块发送的信息时如果要求该模块下某个组成部分进行操作时,怎么进行通知. SubModule的创建就是为了解决,这两个实际开发中经常遇到的问题而产生的.它的工作原理如下. 这里边我的解决方案是采用订阅的方式,通过模块管理器对模块进行订阅式处理.将一个SubModule挂钩到一个模块后, SubModule会通过模块管理器将其注册到该模块下,并在接受信息的接口方法里遍历其所有注册的SubModule(订阅模块)进行信件广播.而SubModule(订阅模块也可以自身的注册信息通过模块管理器查找到该所属的模块并调用该模块的方法进行信件发送)等操作.不需要使用时只要在该模块移除该订阅模块即可. 34. 游戏中的可视化对象管理 对象管理机制 基于统一基类:IBaseSprite 1.实现可视对象的事件行为,心跳行为的统一注册管理等. 2.所有信息存放于实现统一接口的信息体(IBaseVo)中,用于实现数据与界面分离. 3.能实现实例进行的独立完整御载及销毁. 图形化管理. 1. webgame一般尽量使用位图序列作为呈现对象,具体根据项目要求. 2.灵活使用catchAsBitmap (当可视对象渲染频率较少时,或只移动时,MovieClip类时间轴对象对帧画面进行独立catch而不要对整个进行catch) 3.矢量对象素材能减少加载的内存消耗,但增加cpu负担(一般做法是加载时用矢量,运行后使用catchAsBitmap转换为位图) Ui组件库 1.创建自己的轻量级组件库 2.Fl组件包 第三方组件库AsWing, FlexSpark 组件框架 35. 游戏中的对象优化 鼠标事件的替代方案--动态4叉树的应用 1.解决点击事件中因对象上之上深度的可视对象的存在而阻碍事件的触发. 2.实现高效制定范围的对象搜索 3.减少深度排序的对象列表数 4.用于怪物的简单ai搜索行为 5.用户动态绕行阻碍. 减少cpu的消耗及内存的使用 1.对于非交互类可视对象禁用mouseEnabled及mouseChildren 2.addChild(),addChildAt()等方法消耗巨大(排序类操作最好验证判断后使用) 3.尽量少用滤镜(消耗较大,创建了两份内存) 4.避免大消耗方法使用 如: Array.push() <Ayray.unshift ()<Array. splice() Array.pop()<ArrArray.shift()< Array. splice() (数据添加后排序) 5.少用Alpha与混合模式 6.尽量public替代seter()与geter(), 消耗相当于方法的调用 7.尽量使用函数嵌套. 8.实例数越少越好,脚本越多效率越差(虚拟机的反射遍历) 42. 原理 公式: rectWidth = Width / (4^(n-1)) rectHeight = Height / (4^(n-1)) 逸代过程中:矩形大小变化与建树节点数目的关系: W_1=10000 1 W_2=10000 / 4 = 2500 Sn=4 2 W_3=10000 / 4*4 = 10000 / 16 = 625 Sn=20 3 W_5=10000 / 4*4*4 = 10000 / 64 = 156.25 Sn=84 4 W_6=10000 / 4*4*4*4 = 10000 / 256 = 39.0625 sn=340 5 W_6=10000 / 1024 = 9.765625 sn=1364 6 W_6=10000 / 4096 = 2.44140625 Sn=6460 7 W_7=10000/16384=0.6103515625 sn=21844 : 每次逸代的节点个数为:(4^) 10000像素高宽,逸代6次总节点个数: 4+16+256+1024+4096= (4-4096*4)/(1-4)=6460 效率影响因素:精度及深度 精度:最底层矩形的大小 深度:逸代的次数 43. 45度深度排序 【 Function : getOnlyDepthFunc(target:DisplayObject,source:Array):Object】 target --物品列表中的某一物品。 source --物品列表(包含target) 这个条件方法的最终结果是要返回当前某一物品在一个物品列表中的 唯一确定深度 【itemDepth】以及除去了 target 的剩余物品列表【residualSource】。(可以不需考虑其他物品的深度变化,因为这里只关心target的深度,而事实上因为每次确定一个物品的深度时都有可能导致剩余物品的深度变化所以也是不需要考虑其他物品深度变化的) 好,先不要问这个方法怎么实现,具体实现下边会提到。下边说说我的这个算法的设想 假如现在有一个长度为n的物品列表 【sourceN:Array】,因为假设的已知条件方法【getOnlyDepthFunc】可以返回某一物品在物品列表中的确定深度,我们是否可以 通过逸代这个【getOnlyDepthFunc】方法 并且 参数target 以每次执行完后得到的剩余物品列表【residualSource】的某一个物品,source 则以每次执行完后得到的剩余物品列表【residualSource】去赋值,并且把每次得到的在该片段物品列表中target的唯一确定深度 【itemDepth】储存并整理还原为原物品列表 【sourceN:Array】所对应的物品深度,那么我们就应该可以得到最总我们想要的深度已经排好序的物品新数组了。 上边就是我的设想,这里为了方便大家理解,这里再点一下要注意的一些地方。方法【getOnlyDepthFunc】返回的深度只是参数source列表里的确定深度,但由于每次逸代的 剩余物品列表【residualSource】是总物品列表【sourceN】的剩余片段列表,所以他们存在一个耦合性。 处理上我是这么做的,在排序我前先声明一个跟【sourceN】一样长度的空数组【rebackSource】;然后从第一次开始,以第一次得到的深度为索引 ,将索引对应的物品赋值给【rebackSource】数组对应的位上。然后逸代下一次。因为第2次开始已经是剩余物品列表。所以得出的深度【itemDepth】只是相对于每次返回的剩余物品列表 【residualSource】的对应唯一深度。但正因为是剩余物品列表,所以我们可以通过从开始项到得到的深度索引【itemDepth】项,不为undefiend的项数,来得到在总列表中的位置。也就是【residualSource】列表的具体项。然后把这次的target赋值给该位置。逸代一共执行品列表 【sourceN:Array】长度次。不用考虑效率,因为每次都是剩余列表的长度都在减少,而且不用排序,是一个高效的嵌套循环。 44. C-C B-R B-L C-L B-C T-L [B-L] [B-C] [B-R] [C-L] [C-C] [C-R] [T-L] [T-C] [T-R] 公式: item.sideR=(target.[B].y-item.[L].y) item.sideL=(item.[B].y-target.[L].y) item.sideB=(target.[T].x-item.minZ.[L].x) item.sideT=(item.[T].x-target.[L].x) 当item.sideR<0时,item于target的右边; 当item.sideL<0时,item于target的左边; 当item.sideT<0时,item于target的上边; 当item.sideB<0时,item于target的下边; 当item.sideL<0且item.sideT<0时,item于target的左上边; 当item.sideL<0且item.sideB<0时,item于target的左下边; 当item.sideR<0且item.sideT<0时,item于target的右上边; 当item.sideR<0且item.sideB<0时,item于target的右下边; 当上边4值任意为-1时,target于item的对应边界 012345678 T-C C-R T-R 46. 其他 简单角色编辑器: Bitmapdata. Bytearray File 图形处理工具 (FLJ s-- Flash 文档对象模型 (DOM) 场景操作 Document 对象 Timeline 对象 Layer 对象 库操作 library 对象 Item 对象 混淆器: 加密器: 组件封装: 47. 小结: 三.游戏算法 A.排序算法 –冒泡排 序,快速排序,深度排序 B.寻路算法--广度最优(启发式寻路) C.搜索算法--深度最优(数据结构,2叉树,4叉树,8叉树) D.碰撞算法—图形碰撞,数据碰撞 二.图形处理 A存储 B效果 C渲染 D性能 四.UI组件 A.基本组件 B 功能组件 C.Aswing D.flexFrameWork 一.系统构架 A.通信机制 (模块式通信) C.心跳机制 (集约式管理分派事件机制) C.事件机制 (指令式通信事件模式) D.资源管理 (各自加载统一管理,数据共享) 五.功能工具: A.地图编辑器 B.Avatar编辑器 C.加密/混淆器 D.其他 六.扩展 Alchemy Ant---DailyBuild Jsfl 七.其他 单元测试 ----FlexUnit QTP