对象池

On 2010年09月3日, in work, by ppzhu

对象池的工作原理:
对象池的工作原理的核心有两点:使用和缓存,即对于那些被频繁使用的对象,在使用完后,不立即将它们释放,而是将它们缓存起来,以供后续的应用程序反复使用,从而减少创建对象和释放对象的次数,进而改善应用程序的性能。

优点:能快速取出对象节省了new对象所产生的cpu,时间的消耗。能很好的控制内存的占用,使用时从对象池取出,使用完毕放回。中间不涉及到对象销毁创建,所以内存占用是定量的。同时假如对象池提前

缺点:对象池同样消耗new同样对象所消耗的时间,对象池从创建到结束消耗定量的内存。对象池只适合大量的对象需要被反复创建使用销毁创建使用销毁的情况。非提前初始化式内存池,缓存as值类型的对象如:point反而产生更大的消耗。

要实现一个对象池,一般会涉及到以下的几个类:

目标对象(someobject)类
该类就是程序中频繁使用的对象。

对象池(objectpool)类
该类主要用于管理对象的借出和归还,并通知对象池完成相应的工作。它至少包含两个方法:
  borrowobject():用于从池中借出对象;
  returnobject():将对象归还到池中;

package wingox.pool {
    import flash.utils.dictionary;

    /**
     * @version 20100315
     * @author brightli
     */
    public class objectpool {

        private static var _pool : dictionary = new dictionary(true);

        private var _template : class;

        private var _list : array;

        public function objectpool(value : class) {
            _template = value;
            _list = new array();
        }

        public function borrowobject() : object {
            if(_list.length > 0) {
                return _list.shift();
            }
            return new _template();
        }

        public function returnobject(value : object) : void {
            _list.push(value);
        }

        public static function getpool(value : class) : objectpool {
            if(!_pool[value]) {
                _pool[value] = new objectpool(value);
            }
            return _pool[value];
        }
    }
}
 

AS3效率优化:使用Vector数据类型

On 2010年08月6日, in work, by ppzhu

自FlashPlayer10伴随着FlashCS4发布时,AS3中就多了一种新的数据类型:Vector
这里先给不大熟悉Vector的同学们做一下简介(节选并改编自官方文档):

Vector是一种特殊的数组
首先,Vector的定义和C语言中“数组”更为接近,即数组的每一个元素都有相同的数据类型。(这就意味着对Vector中元素的访问要经过类型检查)
其次,Vector是一种密集数组,即每一个索引中必须包含一个值。(可以是null)
最后,Vector可以随时指定为固定长度的数组。

由于这些特殊性,Vector便拥有以下几点优势:
性能:使用 Vector 实例时的组元素的访问和迭代速度比使用 Array 时的要快很多。
类型安全性:在严格模式下,编译器可以识别数据类型错误。(但当使用 push() 方法或 unshift() 方法向 Vector 添加值时,编译器不会检查参数的数据类型,而是在运行时检查)
可靠性:相对于 Array,运行时范围检查(或定长检查)极大地提高了可靠性。

今天我们主要关注的是使用Vector数据类型来优化程序效率。
对于追求效率的RIA应用,使用Vector来提升效率是一种简单却行之有效的办法,
然而很多开发者对Vector的了解不够深入,以致未能充分发挥Vector在速度的优势。
接下来我们便来剖析一下Vector的最佳使用方法。

注:下文中所有代码的运行环境:FlashPlayer 10.1 & Mac OS X 10.6.4,所有耗时均经过多次统计平均并取整。一、Vector数组的长度应当固定
Vector类型在AVM中的实现使用的是定长数组,虽然在AS3中其fixed属性默认为false,但仍不建议对Vector数组进行能够更改长度的操作。
请看以下测试:?

var a:Vector.=new Vector.(1000000);
var b:Vector.=new Vector.(10000000);
var t:Number=getTimer();
for(var k:uint=0;k<9;k+=1){
a.concat(new Vector.(1000000));
}
for(var i:uint=0;i<10000000;i+=1){
a[i]=i;
i=a[i];
}
trace(getTimer()-t);
//////////////////////////////////
t=getTimer();
for(var j:uint=0;j<10000000;j+=1){
b[j]=j;
j=b[j];
}
trace(getTimer()-t);

同样是对长度为1千万的数组进行操作,前者的耗时在850ms,后者则保持在350ms。
如果将上述代码中的Vector a使用1千万次push操作来达到1千万的长度,则耗时变为1800ms。
(事实上,用上述代码对Array进行测试,有同样的耗时比率,所以无论使用哪种数组,都不建议频繁改变其长度)
至于splice操作的耗时,请参见gskinner的Quick As A Flash: Optimization Strategies for AS3 and Flash可以看到,Vector的splice操作要比Array慢出10倍。二、正向/逆向遍历的速度无差异
对于Array类型,正向/逆向遍历其元素的速度大约是3:2,即逆向遍历要慢一些,比如以下代码:?

var a:Array=new Array(10000000);
var b:Array=new Array(10000000);
var t:Number=getTimer();
for(var i:uint=0;i<10000000;i+=1){ a[i]=i; i=a[i]; } trace(getTimer()-t); ////////////////////////////////// t=getTimer(); for(var j:uint=9999999;j>0;j-=1){
b[j]=j;
j=b[j];
}
trace(getTimer()-t);

前者的耗时约是1000ms,后者则是1500ms。
而对于Vector,这种正向/逆向间的速度差异不存在。(有兴趣可以自行修改上面代码进行测试,此处作为结论给出)三、使用Vector储存基元类型的数据时,才能充分发挥Vector的效率优势
AS3内的数据类型分为基元类型和引用类型两种,对于这两种不同类型的数据,使用Vector的效率也是不同的。(需要指出的是,这两种类型在AS3中本质都是引用类型,只不过前者是不变对象,后者是可变对象)
首先是储存基元类型数据时,Vector与Array的读写效率对比:?

var a:Vector.=new Vector.(10000000);
var b:Array=new Array(10000000);
var t:Number=getTimer();
for(var i:uint=0;i<10000000;i+=1){
a[i]=i;
i=a[i];
}
trace(getTimer()-t);
//////////////////////////////////
t=getTimer();
for(var j:uint=0;j<10000000;j+=1){
b[j]=j;
j=b[j];
}
trace(getTimer()-t);

使用Vector耗时在400ms左右,而使用Array则是1200ms。
可见在使用基元类型时,Vector的访问速度相比Array有三倍(以上)的优势。
接下来看看储存引用类型数据时的效率对比:
二者的耗时基本一样,经过多次平均,Array耗时要比Vector多3%~8%,速度上仍然不及Vector,如非对效率要求极高,基本可以忽略。
最后对使用String类型的情况进行了单独测试,与引用类型下的表现结果一致。
四、注意索引的数据类型
很多开发者会忽略一个细节,那就是在AS3中“看起来是整数的”不一定就是整数类型。
请看以下的代码段:

var a:Vector.=new Vector.(10000000);
var t:Number=getTimer();
for(var i:uint=0;i<10000000;i+=1){
a[i*2/2]=i;
i=a[i*2/2];
}
trace(getTimer()-t);
//////////////////////////////////
t=getTimer();
for(var j:uint=0;j<10000000;j+=1){
a[uint(j*2/2)]=j;
j=a[uint(j*2/2)];
}
trace(getTimer()-t);
//////////////////////////////////
t=getTimer();
for(var k:uint=0;k<10000000;k+=1){ a[(k*2/2)>>0]=k;
k=a[(k*2/2)>>0];
}
trace(getTimer()-t);
//////////////////////////////////
t=getTimer();
var n:uint;
for(var q:uint=0;q<10000000;q+=1){
n=(q*2/2);
a[n]=q;
q=a[n];
}
trace(getTimer()-t);

四种方式的耗时分别是1100ms,750ms,650ms,550ms,当然,最后一种由于只计算了一次,不具可比性。(如果同样计算两次,则是750ms)
这里测试用的是乘法和除法,另经测试,+1/-1的结果也是一样。
所以这里特地提一下,如果一定要把算式直接写在索引的位置,要使用位运算将其转化为整数。
关于AS3效率优化中Vector的使用,目前我能想到的就是这四点。(不排除以后想起来了再更新)
希望能对各位有所帮助。

Tagged with:  

游戏中的碰撞检测

On 2010年07月21日, in work, by ppzhu

游戏中的碰撞检测方式有很多,不同的算法之间主要是在精度和速度之间权衡。以下几种方式按照速度排序说明。以2D为例,3D不过是增加了一维罢了,算法理解上没太大区别。

一、地图格子划分检测

最简单的一种检测,就是把地图(或者称为场景,总之是指碰撞发生的范围)划成一个个格子,类似仙剑奇侠传这样。假设地图有800*600px,20*20个像素为一格。那么可以划为40*30个格子。地图中参与检测的对象都存储着自身所在的格子坐标,判断碰撞就显而易见了,例如可以认为两个物体在相邻格判为碰撞,或者两个物体在同一格。采用这种方式有个要求,就是地图中所有可能参与碰撞的物体都要是20*20像素左右大小或者是其整数倍,例如房子占了3*3个格子,诸如此类。如果不遵守这个规则,有的物体只占了格子的一半,那么在玩家眼里这种检测就显得非常的粗糙。这种检测就像是把地图的像素点放大几十倍一样,与逐像素检测相比,效率提高了几十倍甚至上百倍。这种方式可运用于对检测要求不严格的游戏,例如踩地雷的RPG、推箱子之类的智力游戏。

二、矩形检测

当地图中的物体不能严格按照某个块大小的整数倍来绘制时,那么就需要另想其他的方法。这种方法适用于地图中的物体近似为矩形或者虽然不是矩形,但是碰撞精度要求不高的情况下。每个物体记录一个能够将自己框住的最小矩形的左上角坐标和矩形长宽。碰撞退化为判断矩形与矩形之间是否重叠,而这仅需要4次比较即可得出,速度很快。但为了判断整个场景中的物体,必须取第一个物体,迭代其他所有物体进行判断,再取第二个物体,迭代除第一第二个物体外的所有物体进行判断,以此类推。总计要进行(n-1)!次矩形判断才能准确得出场景中所有的碰撞可能。

三、圆形检测

与上一种方法类似,区别在于用一个能够包含物体的最小圆代替了矩形。主要是考虑到游戏中的物体外形以平滑为主,例如人物角色。而判断两个圆是否碰撞的计算也很简单,就是判断两个圆心之间的距离是否小于两个圆的半径之和。虽然球形检测在某些情况下提高了精度,但却损失了速度,因为点距离的计算需要用到平方和开方。具体相比慢多少我就不太清楚了。另外,为了计算整个地图的所有碰撞可能,也要进行(n-1)!次比较。

四、像素检测

精确到像素级,已经不能比这更精确了,相对的,效率也是最低的。怎样判断两个物体是否碰撞呢?在过去png格式图片还不盛行的时候,游戏中用到的图片中的透明部分是指定用某种颜色来表示的,例如洋红色。就像电影中的绿幕蓝幕,通过处理把这些颜色的像素点当做透明点处理,而为了判断检测,需要准备一张原图像的黑白图,黑色区域表示透明,这张图片中的每个像素值为0或者1,判断检测的时候取两张图片的黑白图,进行与运算,结果为1(有白点重叠),则判为碰撞。但是现在有了PNG和XNA,逐像素检测就相对简单一些。首先仍然需要有一个矩形框包围物体,通过矩形检测得到重叠的矩形区域可以大大减少检测的像素点数量。然后在这个区域内,取两个图片的点逐行逐列迭代,如果遇到某个点两张图片均有颜色存在,即判为碰撞。同理,进行(n-1)!次比较后得到全地图的碰撞可能。

五、四叉树检测

准确的说这事在第三四五种方法的基础上的优化策略,或者说是第一种方法同后三种方法的组合应用。主要是针对那最后的(n-1)!次比较。方法是,像第一种方法一样将地图分为格子,格子的大小应该能够容纳10个左右的地图中最大物体,例如一个800*600的地图可能就划为9个区。同样的,每个物体要记录自己所在的区坐标以及矩形包围盒。如果该物体完全位于该区内,则只要将其与该区内的其他物体判断碰撞。如果该物体虽然位于某个区,但是小部分位于隔壁区,则额外的需要迭代隔壁区的物体,这点效率损失是可以容忍的,相比于迭代全地图的物体。

有个问题,我怎么知道哪些物体是跟该物体位于同一个区呢?那不是还是要迭代一遍所有物体?这时候就是题目发挥的地方的,之所以称为四叉树检测(当然,这名字是我自己取的),就是因为那些区块是以四叉树的方式链接的,即得到一个区块的对象,就可以直接得到其上下左右相邻的区块的对象,而物体可以是存储在所在区的一个列表中。这样就不用遍历所有物体也可以直接取出隔壁区的物体了。当地图很大的时候,四叉树的优势体现得很好。

六、3D中的碰撞检测

以上是我所掌握碰撞方法,可能还有更多吧。那么3D中的检测其实是2D的延伸,例如矩形检测变为立方体检测,圆形检测引申为球形检测,四叉树检测进化为八叉树检测。

当然了,凡是有例外。逐像素检测方法在3D中没有相对应的方法,因为3D中的物体的表示最小单元是三角型而非点。其实也可以说逐三角片检测是逐像素检测的3D版,但毕竟是平面碰撞的检测,需要一定的计算公式,而不是与或一下就OK的。这里就不赘述了。

总结

我的表达能力不是很OK,另外手打得也有些酸。

选择哪种方法,要根据你的游戏需要,例如推箱子游戏,显然只要格子碰撞检测就足够了。而一些以球作为主要物体的游戏例如射击游戏则可以考虑圆形碰撞。

同样的,在有些碰撞精度要求很高的游戏中,还要对这些方法进行一定的变化,例如格斗游戏,作为判断的单元不是整个人而是四肢、身体等部位,需要更多的包围盒来表示一个物体。

原文地址:http://darkey2s.blog.163.com/blog/static/739930262010115105333989/

Tagged with:  

[转]一个AS3 socket解码设计的错误思路

On 2010年07月17日, in work, by ppzhu

作者:闪刀浪子 http://www.iflashigame.com http://hi.baidu.com/mr_ziqiang
转载请保留以上信息。

调试游戏确实是一项磨人的工作。难怪很多业内人都喜欢说“好游戏是改出来的”。奇都(game.qdooo.com)基础平台目前还在测试,陆陆续续收到了很多朋友的反馈(想参与测试的朋友欢迎联系我索取激活码)。所以这段时间的主要工作是在做美化和优化,新的竞技系统也在紧锣密鼓的准备中。

这里主要想说一下socket解码在设计时忽略的一个问题。(感谢Lite3的反馈)。

对于客户端的Socket的数据基本读取方式一般来说可以分为三种:

1、按照数据流的结尾标记截取
2、按照包头记录的包长度截取
3、按照包长度截取并验证结尾标记。

第一种方式:如图,通常做发是每个包结尾发送一个\0标记,表示这个数据包发送完了。socket每次读取1个字节直到遇到结尾符,结束读取将数据包传递到逻辑层。这种方式在xmlSocket时就已经用了很多了。

第二种方式:如图包头用一个整型记录完整包的长度。每次都先读取一个包长度,然后按照包长度读取指定长度的数据作为一个完整数据包传递到到逻辑层。

第三种方式:如图。这种方式把以上两种方式做了结合,读取的时候无需一个一个字节读,直接读取指定长度。结尾符可以用来做校验判定,同时可以作为包长度字节的读取依据。(如果上一个包有问题可以丢弃之后,以结尾符为标记读取下一个包的包长度。)

介绍了一点基本原理,下面说一下这个设计缺陷。

问题:

通讯中我们使用的是第二种方式——包长读取。在通讯类中创建一个临时存储变量_dataArray,提供一个getData():*公用方法,供外部取出数据。每次读完一个完整包后压入_dataArray,同时触发”recievedData”事件。代码如下:

private function socketDataHandler(event:ProgressEvent):void
{
//_readFlag:int;//0表示全部读完了,1表示长度读取完毕 2表示正在读取数据
while (bytesAvailable)
{
if (_readFlag == 0&&bytesAvailable>=4)
{
_length = Number(readInt());
_readFlag=1
}
if (_readFlag == 1 && bytesAvailable >= _length)
{
var temp:Object = readObject();
_dataArray.push(temp);
dispatchEvent(new Event(“recievedData”));//
_length = 0;
_readFlag = 0;
}
}
}

以上代码从结构上看是没有任何问题的,而且在测试前期我们也一直用着没有任何问题。逻辑相当清楚:接收到socket的事件后首先读取一个包长,然后按照包长读取数据。读取完毕发出事件。。。

那么,问题终于来了——网友Lite3出现了。^ _ ^

Lite3发来了一个出错提示:
Error: Error #1502: 脚本的执行时间已经超过了 15 秒的默认超时设置。
at qdooo.net::mySocket/socketDataHandler()

相信大家一看就能知道问题出在了哪里——问题就在while上。

while的循环等待时间因为某种原因超过了15秒。这里所指的某种原因就是网速,那天Lite3那边的网速非常慢。这样一来放大了设计中一处缺陷所照成的影响——必须等到可读数据的长度等于或者大于包长度的时候才开始读取,也就是下面这句:

if (_readFlag == 1 && bytesAvailable >= _length)

正是由于这个判断做了限定,如果数据长度不够,那么他会在while中一直循环等待。直到超过15秒报错。

解决:

分析好了原因那么就很好解决了,想办法读空流里面的数据让while跳出等待就行了。

Tagged with:  

Adobe Flash Catalyst CS5全新交互式设计工具

On 2010年07月14日, in work, by ppzhu
Adobe Flash Catalyst CS5 软件是一款触手可得的全新交互式设计工具。无需编写任何代码,即可将 Adobe Photoshop、Illustrator 和 Fireworks 图稿变换为具有表现力的交互式项目,并充分利用 Adobe Flash Platform 的范围和一致性。
2010年4月12日北京时间23时,Adobe Creative Suite 5设计套装软件正式发布。Adobe CS5总共有15个独立程序和相关技术,这些软件通过五种不同的组合构成了五个不同的组合版本,分别是大师典藏版、设计高级版、设计标准版、网络高级版、产品高级版。
Flash Catalyst之前的开发代号为Thermo。这是一个为FLEX设计师准备的软件,通过Catalyst,设计师与程序员可以更好的搭配之间的工作。 它可以让你通过简单的拖拽就可以完成界面设计,然后可以生成具体的Flex代码,就不需要程序员在Flex中单独布局样式,Catalyst可以跳跃启动任意Flex项目。 Adobe平台业务部门总经理David Wadhwani表示,Catalyst旨在为开发人员和设计人员建立起沟通的桥梁,程序开发人员可以导入设计师在Photoshop、IIIustrator和Fireworks中设计的用户界面,并将它们转化成UI组件而不改变它们原先的“皮肤”、外观和整体风格。设计师仍然用Adobe的各种产品来完成自己的大部分工作,但是能通过Catalyst来定义UI组件了,就像开发人员通过编程来完成这一工作一样。它给开发人员和设计人员提供了一种交流协作的平台,而不用通过电子邮件或是一起坐在电脑前探讨。 Adobe CS5 的 Flash Catalyst 则是为了挑战微软的 Expression Studio,这款新的软件将作为Flash的另一个选择,是专门为设计师和美工量身定做,用户无需编写代码即可创建具有表现力的界面和交互式内容,可将 Adobe Photoshop、Illustrator 和Fireworks 图稿变换为具有表现力的交互式项目,并充分利用 Adobe Flash Platform 的范围和一致性,可以说设计结果触手可得
Flash Catalyst 就像是设计人员与开发人员之间的一座桥梁,它可以让设计者在熟悉的应用程序环境下工作,如Photoshop 、Illustrator,同时能够在后台自动生成开发人员所需要的代码。
Flash Catalyst的工作流程着重强调工具的直觉本质(intuitive nature),在短时间内获得成果。
Tagged with:  

flex embed swf问题

On 2010年07月8日, in work, by ppzhu

1) 如果是通过Embed来嵌入swf的话,Flex3只支持FlashCS2所创建的swf.
2)  如果是通过loader来加载的话,只有AS3的swf才能在加载后被控制

(这和第一点相反,今天才在网上看到这两条,囧,难怪Embed进来swf都无法控制)

 

现代版“诺亚方舟”

On 2010年06月30日, in work, by ppzhu

原公司在长乐的基地模型

蛮夸张的

 

FLASH CacheManager

On 2010年06月29日, in work, by ppzhu

flash CacheManager

为了减少无谓的加载,所以游戏中做一个CacheManager是很有必要

思路:

/**
  * 缓存管理器 配合 loader使用
  *
  * 每次开始加载数据的时候
  * 判断缓存中是否存在文件,没有进行写入,有则直接获取本地数据进行加载。
  * 写入的时候判断是否已经设置无限的存储空间,无,提示设置。默认情况下,Flash Player 最多在本地永久保存 100K 的远程共享对象。
  * 版本更新问题,在每次打开flash的时候会有份数据更新列表(一份fileName数组即可),对旧版本的缓存文件进行删除,接下去就回去加载新的文件
  *
  */

 

Tagged with:  

Intel PC上安装MAC OS X kalyway 10.5.2

On 2010年06月28日, in work, by ppzhu

硬件检测

*********************************************************************************

一)下载CPUZ,检测计算机的CPU的指令集是否在SSE及以上,因为只有SSE2及以上的CPU才适合安装MAC OS。如果CPU不是SSE2及以上,那么在你的本机上就不能安装MAC OS。

二)如果CPU支持SSE2, 可以继续。如果要在虚拟机上安装MAC OS,那么计算机必须支持*硬件虚拟化技术*: 下载小软件securable.exe就可以检测CPU芯片是否支持*硬件虚拟化技术*. 如果不支持那么在虚拟机上安装MacOS肯定不行。

*********************************************************************************

Dell optiplex 755安装示例

一)虚拟机上安装

1)环境:WindowsXP + VMware7

2)下载安装VMware7

4下载MAC OS X kalyway10.5.2

5)安装MAC OS X kalyway10.5.2到VMware里.

i)在VMware里新建一虚拟机, guest OS选择other里的FreeBSD64bit, 然后把ISO镜像导入,确定,power on vmware.

ii)启动的时候会提示,大概是:本机没有开启虚拟技术,问你要不要继续,点yes.然后就开始安装了。

iii)在选择安装磁盘时,因为windows一般安装的时候安的是NTFS, 所以要把虚拟机中用于MAC OS的那部分格式成MAC OS的磁盘格式。在提示你选择安装磁盘的那页上最上面有一个叫”实用工具”的,这个就可以把用安装虚拟MAC OS的空间格式成MAC OS需要的格式。格式好后,关闭”实用工具”,这时你就可以看到一个磁盘在图标,选择它就可以继续安装了。

vi)在region那一步的时候要选中国,因为这样给你提示要填写的栏目你才熟悉。键盘我选择是ASCII 美式类型。

v)我在虚拟机上安装MAC OS上安装的很慢,花了大约三到四个小时,如果真机安装的话只要20分钟左右。

vi)安装完成后,就可以重启了。重启的时候按下“任意键”就可以进入OS启动界面,如果在提示你按“任意键”时你没有来得急按也没有关系,直接回车或者输入-v就可以启动了,只不过这样你会先看到启动的控制台输出再会看到苹果“风火轮”界面。

———–

本想学下oc,发现cpu不支持硬件虚化,只好作罢,以后有米再去买个mac折腾去。

Tagged with:  

Adobe开源框架

On 2010年06月24日, in work, by ppzhu

Cairngorm是adobe为flex工程师准备的mvc开源框架,据说与PureMVC一起号称flex届目前最好用的两个mvc框架。

http://opensource.adobe.com/wiki/display/cairngorm/Downloads

一个中文介绍

http://wangguorui89.javaeye.com/blog/430916

Tagged with: