2010年5月 的存档

[心得] 12个Flex常用功能代码

1。 复制内容到系统剪贴板

System.setClipboard(strContent);

2。 复制一个ArrayCollection

//dummy solution( well, it works )
var bar:ArrayCollection = new ArrayCollection();
for each ( var i:Object in ac ){
bar.addItem( i );
}
// fantastic ! //
var bar:ListCollectionView = new ListCollectionView( ListCollectionView( ac ).list );

3。 打开一个新浏览器窗口

navigateToURL(new URLRequest('http://ntt.cc'), '_blank');

4。 刷新浏览器

navigateToURL(new URLRequest("javascript:location.reload();"),"_self")

5。 关闭浏览器

navigateToURL(new URLRequest("javascript:window.close()"),"_self");

6。 设置Alert 窗口的背景为透明

Alert

{

modalTransparency:0.0;

modalTransparencyBlur:0;

}

7。 取随机颜色

  lbl.setStyle('color', 0xffffff*Math.random());

8。 清除子串左侧空格

  public function LTrim(s : String):String

     {
var i : Number = 0;
while(s.charCodeAt(i) == 32 || s.charCodeAt(i) == 13 || s.charCodeAt(i) == 10 || s.charCodeAt(i) == 9)
{
i++;
}
return s.substring(i,s.length);
      }

9。 清除字串右侧空格

  public function RTrim(s : String):String

     {

	var i : Number = s.length - 1;
	while(s.charCodeAt(i) == 32 || s.charCodeAt(i) == 13 || s.charCodeAt(i) == 10 ||s.charCodeAt(i) == 9)
{
	i--;
}
	return s.substring(0,i+1);
      }

10。 清除字串左右的空格

  public function Trim(s : String):String

      {

return LTrim(RTrim(s));
      }

11。 获取数据类型

  getQualifiedClassName(data)

12。 生成随机字符串。

  private function GenerateCheckCode():String

     {

//init
var ran:Number;
var number:Number;
var  code:String;
var checkCode:String ="";
//get 4 radom
for(var i:int=0; i<4; i++)
{
ran=Math.random();
number =Math.round(ran*10000);            //get result like 0.1234
if(number % 2 == 0)
code = String.fromCharCode(48+(number % 10));        //0's ASCII code is 48
else
code = String.fromCharCode(65+(number % 26)) ;        // A's ASCII code is 65
checkCode += code;
}
return checkCode;
}

如何让Chrome浏览器的Flash Debugger生效

因为要换一下游戏的debug环境,换了Chrome浏览器来调试,结果发现debugger无效。

于是在Chrome地址栏中输入:chrome://plugins/以查看Chrome浏览器的插件使用情况,发现Chrome自动启动了自带的Flash Player.

看来这就是原因所在了,如下图:

将其自带的非调试版本Flash停用就OK了。要看自己的Flash版本状态,可以进入Adobe提供的功能网址进行查看:http://kb2.adobe.com/cps/155/tn_15507.html

如何成为一名游戏数值策划

在这之前你首先要知道自己到底想成为哪一种策划。所谓术业有专攻,如今随着游戏本质的不断提升,游戏策划岗位的划分也越来越细。在上第一课前,先问自己一个问题:“我知道游戏策划一共能分几种吗?”

下面看看你的想法是不是也和我一样吧。游戏策划根据工作内容一般分为创意策划、系统策划、文案与剧情、数值设计,但是有些更大型的项目还会细分到关卡设计、道具设计以及角色设计等等。根据不同的游戏类型策划的种类也可能有所不同,更让你吃惊的是一些女性向的育成游戏里还会有专门的服装设计策划!

“创意策划”,这 个名字听起来酷酷的很有趣,相信很有玩家都对这个工作很感兴趣,然而创意策划工作起来一点也不轻松.创意策划的主要工作在游戏的前期,游戏的原型就是靠创 意策划来设计的.如果游戏是一个蛋,创意策划就是下蛋的母鸡了,而其他策划是负责喂食照顾的人.一般来说,鸭子生不出鸡蛋(^-^),所以创意策划的设计 方向会完全决定游戏以后发展成什么样。创意策划有些时候也会兼任主策划或者制作人这个职责,负责统筹全局.

系统、文案策划在创意策划提出了最基本的概念之后就开始运作了。一个游戏该怎么操作?角色的背景?有多少类道具?有没有攻城战?这个时候这些问题就会慢慢 开始浮现出来,并且由这两种策划去解决。当然这些工作不是无序的,也需要经过一个成长的过程,开始阶段甚至要和创意策划互相交流争论以修改一些无法实现的 概念。

很 多朋友对数值策划都很感兴趣,可能是因为名字听起来就比较高端.其实数值策划在游戏策划里也是一个必不可少的位置.尤其在RPG游戏中.但是我要说的是, 数值策划其实一点也不好当,因为数值策划需要考虑的东西太多太多,而且影响当整个游戏的平衡度和可玩度.那么,一个数值策划应该具备的基本知识有哪些呢?

1.数学是基础,尤其关于概率学;

2.逻辑推理能力,有些时候你需要创造一些公式;

3.统筹掌握全局的能力,尤其是大型游戏;

4.细致缜密不烦躁的良好品质;

5.对数据库的掌握和熟悉。

举个简单的例子,下面这个这是最简单的人物伤害模型:
HP – 伤害 = 剩余HP(当剩余HP<0的时候,人物死亡)
但一般的游戏都会引入护甲的概念,比如:
HP – (伤害 – 护甲) = 剩余HP(伤害 – 护甲最小值>=0)
引入护甲的意义是:人要是穿着一件钢板甲,被砍一刀受到的伤害总比裸体被砍要少。
如果再复杂点,人要是除了护甲外,还拿着一块坚硬的盾牌呢?那公式可能就变成:
HP – [伤害 – (护甲+盾牌护甲)]= 剩余HP
…等等

如果再推演下去,还要增加偏斜率,躲闪率,武器与护甲克制,相性克制等等,甚至引入魔法攻击。关于伤害目标的所有复杂的公式最终目的是什么?只有一个:目标什么时候死?
HP / DPS = 死亡时间(DPS = damage per second,每秒伤害。如果是回合制游戏则是DPR,每回合伤害。)

这就应验了前面所说的那句话,很多时候这种公式都是由数值策划创造出来的.这样一个简单公式就可以解决问题,但为什么数值策划们要千方百计的想出许多古怪复杂的公式呢?

“游戏里的公式并非为了解决问题而存在”

所有存在于游戏里的复杂或者简单的公式,都是被创造的。模拟现实,增加策略性,提高或者降低难度,这些都是创造公式的原因。如果我们要创造一个公式,那我们必须产生以下的想法和讨论,具体请看下图.只有被这一系列过程所论证并证实确实可行,才算是成功的例子.

许多规则,公式,使游戏变得复杂而具有策略性,但往往这些变化的根本原因都是满足玩家的需求。作为数值策划并不是一个数学家,必须要明确玩家需求才是数值设计的根本。

========—– —–========

现在放出一个WEB2.0概念的模拟网游的策划概要,首先介绍下游戏的特点:游戏主题是虚拟人生,玩家可以在游戏中工作、赚钱、买房子、家具;可以开公司 当老板,经营自己的事业,最终的成为富翁,成家立室。游戏的特点是玩家在游戏当中可以充当多个角色,就好象在现实世界中,我们是可以工作,是雇员,下了班 之后,我也又可以消费,是消费者,当玩家发展到一定的程度,积累有足够的金钱之后,还可以开公司,成为雇主。
下面是游戏构架图:

========—– —–========

上面WEB2.0概念的策划案先放一放,先给大家看看目前MMORPG类型的一些游戏的策划解决方法,先来讲一下现在基本所有游戏中通用的经验值的设定,然后讲下游戏中的核心,经济系统的设定.

由于目前大多的的游戏采取的方案仍然是以长时间在线来获得更高的级别,因此在经验值方面仍应采取递进的方式让玩家获得经验值。而这个递进的数值既不能太大 也不能太小,太大会造成级别差距太大,使得玩家升下一级越来越过于困难。造成玩家的厌恶心理。而太小则会使总经验缩短,造成游戏寿命缩短。

因此我们可以抛开当前级别的经验进行累加,以级别的增长进行。设计公式如下:

经验值计算公式:

N=当前级别 EXP=30(N*N*N+5N)-80

该 公式完全与当前经验值无关,是以级别为主要值进行的累加。而数字30则是关键值。如果日后测试发现级别增长速度过快或过慢,只需要调整该数字就可以对经验 值进行调整。并且幅度呈三角型。级别越高则经验幅度差别越小。而在低级别时由于升级速度很快变化基本察觉不出来。对于我们进行调整也是非常方便的。

我们可以通过经验值划分表来查看这种明显差距。

以等级90级为例:
========—– —–========

通 过上表我们可以看出,当关键值为30时,总经验已达到近5亿。在初期的级别里,由于经验值获得速度快,所以即便3级是2级经验值差距的3倍多,对于刚进入 的游戏者,以及简单得怪物来说,获得的速度依然很快,一天升数级也没问题,因此游戏者根本感觉不出来。因为游戏者在对比时通常以上一级别进行对比的。

而在最后的90级经验值却只比89级经验值多了30分之1,差距非常小。这样的增长使得游戏者也很难察觉。因此采取该公式即可以增加游戏的寿命(经验值的总值),也不会让游戏者感觉到一级比一级的难升(各级别之间的差距)。

下面将关键值减少到20,在看一下1~90级经验的划分。这样可以更清晰的与之前的经验划分表进行对比,看看关键值缩小后的改变。

N=当前级别 EXP=20(N*N*N+5N)-80

========—– —–========

通 过上表可以看出,总经验降为3亿,升3级的所需经验达到了升2级所需经验的7倍,而90级所需经验依然比89级多30分之1.因此证明该公式对于经验值的 平衡性调整是非常有效的.无论游戏中设计总级别是60级还是200级,无论需要的总经验是1万还是10亿,均可以通过该公式调整游戏者的升级速度.可以说 该公式适合任何需要升级的游戏.

关于该公式最后的减80.这是一个微调数值.为什么要设置此数值呢,我们依然通过公式来看.

将公式的关键值改为10

N=当前级别 EXP=10(N*N*N+5N)-80

玩 家为1级,需要升到2级时 公式为 10(1*1*1+5*1)-80=-20 我们可以看到如果关键值过低,那么会导致经验值变为负数,这时就需要调整最后的微调数值,将它调低。同时,通过修改微调数值也会对各个级别的经验产生影 响。例如将初期各级别之间经验值的数据缩小等等。

关键值进行宏观上的调节,微调值调节微观部分,这就是该公式在等级与经验提升上的作用。

有人说我的策划还是摆脱不了泡菜,我只想说2点,1、目前我刚刚写了一点点数值,数值与是否泡菜无关,传统的MMO升级还是需要的,即便EQ WOW 也都还是需要升级,只是升级的方式与难度降低罢了。怎么升,如何升才是关键。2、有人拿WOW来说,在我看来WOW的副本RAID和传奇的枯燥打怪没什么 区别。和数值就更没什么关系了,数值应比较的还是数值、更加合理的数值。而不是拿游戏性来比较。

杀怪获得的经验值:

公式:获得的经验值=怪物经验*怪物等级/玩家等级

这个公式非常的简单,目前大多游戏也都使用这个公式,好处就是根据玩家的等级来判定杀怪获得的经验,级别越低,杀死高级别的怪物获得的经验越高,级别越高,杀死低级别怪物获得的经验就越低。

举个例子:60级的怪物,假设怪物经验值为3000 分别被40级和80级的玩家杀死。

3000*60/40=4500经验 3000*60/80=2250经验

注: 如果服务器条件允许,可以在最后获得经验中随机乘0.80~1.20之间的任意一个数字,这样就可以设置经验不是固定不变的,而是每次都不相同。甚至可以 与技能相关联,如果最后一击为暴击杀死,则经验翻倍。 或者设置成带有刺激和挑战的模式吸引玩家,比如一小时内连续杀怪时第10、50、100……个怪的时候这个怪的经验也是10、50、100倍。总之这是一 个简单的设置,想怎么设随心所欲。

关于怪物的经验。通常来说怪物经验就是游戏者与怪物同级别时杀死怪物所获得的经验值。一般都是预先设置 好的,可以简单的设置成1,2,3级怪物经验为5,10,15。也可以像前一章一样设置一个怪物的等级经验值提升。或者完全没有关联的给每个等级的怪物一 个经验值。只是这样进行后续的开发时略微麻烦一些。

BOSS级怪物设置特殊的、更高的经验值,这个就看各个游戏的不同设置了,无须在讲。

组队经验值分配数值

不 考虑自己个人做任务的经验,只考虑组队杀怪时的经验值分配,关于这点我们需要考虑很多,现在很多游戏的组队经验值分配都是平均分配或者根据伤害不同来分 配。所以很多游戏中玩家都喜欢单练。级别越高越是如此,这样造成的结果就是游戏中的交流减少。新人没人带。换来的后果就是新人都喜欢去新区,老区没有新鲜 血液的加入。最后不断的合区,在开新服。

我们可以通过人数的增加来增加经验获得的百分比解决这个问题。

例如游戏中最高可6人组队。

人数对应的经验百分比:

2人打怪获得的经验值为%120

3人打怪获得的经验值为%140

4人打怪获得的经验值为%160

5人打怪获得的经验值为%180

6人打怪获得的经验值为%200

个人获得的经验值=(杀死怪物获得的经验值/小队等级总和)*个人等级

例:5人组队,级别分别为:30、33、45、50、60。杀死等级为60级怪物(假设60级怪物经验为3000)。

结果为:{3000*60/[(30+33+45+50+60)/5]*1.8}/(30+33+45+50+60)*个人等级=

30级玩家获得经验:1023

33级玩家获得经验:1125

45级玩家获得经验:1534

50级玩家获得经验:1704

60级玩家获得经验:2045

这样根据等级获得的经验值非常公平。组队就变的更有意义,而玩家之间也乐于快速提高等级而去组队。高级的玩家则更乐于带新手玩家练级,因为带越多级别越低的玩家,则自己的经验值就会越高。

依然通过2个直观的例子来看看。

例1:60级的角色与一个10级的角色组队。在60级左右的区域杀怪。(假设60级怪物经验为3000)

{3000*60/[(10+60)/2]*1.2}/(10+60)*个人等级=
10级玩家获得经验:882

60级玩家获得经验:5290

例2:60级的角色与4个10级的角色组队。在60级左右的区域杀怪。(假设60级怪物经验为3000)

{3000*60/[(10+10+10+10+60)/5]*1.8}/(10+10+10+10+60)*个人等级=

10级玩家每个获得经验:1620

60级玩家获得经验:9720

通 过以上2个例子我们可以看出,60级角色杀死60级怪物只有3000的经验,而与一个10级的角色组队就达到5290的经验,与4个10级的角色组队就达 到9720的经验。对于这样优厚的经验补偿,我想是每一个高级的玩家都乐于去组队的。并且该公式所达到的目的是让经验呈曲线上升,根据每个角色的等级来判 定应该获得的经验。使得无论哪个级别的角色都能获得最大的利益。

同时,公式中的百分比也可以根据游戏需要而修改,可以是%200也可以是%300.但注意一定要根据组队的人数按比例修改就可以了.

FAQ:

1、如果玩家通过自己建数个小号与自己大号组队给自己升级,怎么办?

答:可以通过限制组队等级来解决,比如等级差距30级以上不能组队。这样使得玩家之间的组队划分更加明晰。中级玩家会去带低级玩家(25~30级带1~5级),高级玩家会去带中级玩家(55~60级带25~30级)。

2、那玩家把自己的小号都练到和自己相差30级左右的等级,怎么办?

答:不可能,因为根据经验值计算公式和组队经验分配公式的设计,组队练级不可能达到同步的速度,级别越低的玩家组队升级的速度越快,而级别越高的玩家则越慢。

3、玩家如果将小号通过死亡的方式保留在和自己相差30级左右的等级,怎么办?

答:可通过限制死亡方式来进行,例如设计成死亡只掉当前经验,不会掉级。另外如果游戏采取正常的月卡、点卡模式收费,这种方式反而会使玩家得不偿失。玩家如果采取这种方式必然是自愿的,而运营商的利润则会增加。

4、假设6人组队,也就是在1个高级别的玩家带5个所能带的最低级别玩家时经验的获得最高。一个服务器中很难同时找到这么多适合的级别。玩家是否会因此不习惯于组队?

答:不会,根据组队经验分配公式的设计,只要带比自己级别低的玩家,就是有经验补偿的,只是多少不同。例子中的60级带5个10级的玩家的组队情况只是展示在获得经验最大化的状态下。随便什么状态下组队都是有利的。即使同级别的组队,也会因为人多加快了打怪速度而组队。

5、升级速度这么快会减少游戏寿命,玩家很快都达到当前版本的最高等级怎么办?

答:不会,因为经验值计算公式的关键值和微调值对经验&等级部分的处理,使得总经验完全可以配合怪物经验值和组队经验值公式来进行最适合的调整。

6、玩家不愿意组队有着很多的原因,比如打钱,打装备,打材料,不仅仅是因为升级的问题。

答:的确是这样,因此我们需要把每个数值都做好。至少按以上数值制作时,玩家不会因为升级问题而不肯组队。

多人&多队攻击同一怪物经验分配.

多人多队攻击同一怪物是指没有组队的玩家打同一个怪物的情况。部分游戏采取了按最先攻击的人分得经验、按最后一击的人分得经验,或者干脆就是被攻击后的怪物 若干时间内他人无法攻击等等模式。这些都是不可取的。按最先攻击的人或最后一击的人分得经验,容易造成恶意抢怪的发生。而被攻击后怪物若干时间内他人无法 攻击的模式虽然可以避免抢怪,但对于掉宝的BOSS等怪,却又形成了抢怪,只是抢夺的对象从经验变成了物品。
所以针对抢怪似乎没有最好的解决办法。采取分配才是最公平的办法。

公式:单人&单队获得经验=怪物经验/(怪物总血量/单人&单队造成伤害)

这个公式只能说对于能者多得的方式来说最公平,但假设高级玩家去低级区域恶意抢怪,那依然是不公平的。

如果服务器条件允许,可设置最后一击奖励,最后一击杀死怪物者额外获得怪物总经验的%5。

死亡造成经验损失:

这 一点每个游戏都有不同设置,有的游戏借此限制玩家升级速度,也有的游戏完全不设置死亡惩罚。这里就不多说了。我的观点是在服务器稳定的情况下,被玩家杀死 不造成经验损失,被怪杀死损失经验当前级别至下一级别所需经验的X%。经验值减少的极限是到当前等级所需经验值。即永不低于当前等级所需要的经验值。(不 会掉级)。

这样的好处是不会被恶意PK杀死后掉经验,而在服务器稳定(不会卡)的情况下一般不会被怪杀死,如果被怪杀死说明玩家的技巧不 够或者挑战和自己级别装备差距太大的怪了。而设置了不会掉级的处理,则是为了玩家去击杀BOSS级怪或挑战自身极限的设置,一般这种情况死亡频率较高,玩 家会在刚升级时去进行。即便不在刚升级时去进行,也不会因死亡次数太多导致的级别降低而丧失对游戏的信心。

Flash社交游戏开发攻略(转载)

引言:
如今SocailGame火爆的如日中天,AS也是热得滚烫,农场、餐厅以及花园类游戏的火爆使得很多人都打起了开发Flash游戏的算盘。作为公司项目主管,我就负责了两个项目的开发,一个是餐厅,另一个是花园。
这里是游戏的链接(非广告,大家可以只看截图不点连接)
接下来我会以这两个游戏开发的过程为范例,给大家分享一些在游戏开发方面的经验以及一些比较不错的技术解决方案
花园游戏:
餐厅游戏:
基础控制流1:
本来有很多想说的,可一旦真的写了,突然发现无从下手,不过这个感觉就和我第一次开发游戏一样,因此,我就从新手最容易碰到的问题上来说吧。
很多游戏开发新手在这个问题上都会很迷茫,究竟如何去控制整个FLASH游戏?
比如,我们要做一个横版飞行游戏,那么我们会设想敌机在空中飞,那么这个飞行肯定是有速度的,新手一般会想到使用Timer类来实现每隔几秒就让某个元件按照一个数学函数来移动一下位置。这的确是最直观的方法。
不过这么做有两个坏处:
1 每个Timer如果都是独立的,那么则无法从全局上控制流程,比如让所有飞机都停止。如果是使用一个Timer来处理,那么对很多速度不相同的飞机就无法很好的控制。
2 我们不能保证每次Timer过后,显示出来的结果都是我们预期的,因为FLASH有相对固定的帧数,比如帧数是每秒1帧,如果你Timer设定10毫秒就移动一个像素,那么你看到的仍然是1秒移动了100像素,但是系统却运算了100次。
因此我们使用在Flash中更为实用的控制流方式,ENTER_FRAME事件作为控制流的入口。这样做的好处如下:
1 只有在进入下一帧的时候才会做出相应运算,也就是说,所有内容的运算次数是最为优化的,因为你的眼睛只能看到每一帧的内容,因此我们只要在每帧的时候进行相应的改变就可以了
2 使用恰当的方式,我们可以由上而下的让所有的类都相对独立,但又可以受到上一级的控制,可以随时暂停任何一个你想暂停的内容。
那么接下来我们就举一个例子看一下实际的代码:
在文档类的构造函数中书写如下代码:
stage.addEventListener(Event.ENTER_FRAME, tick);
lastTickTime = getTimer();
lastTickTime 是个私有属性。
新增加一个私有函数tick
private function tick(e:Event) : void
{
var timer:int = getTimer();
var timespan:int = timer – lastTickTime;
lastTickTime = timer;
tickBase(lastTickTime);
}
这个tickBase函数的作用是,对需要被控制的对象执行相应的控制代码,具体内容大家可以根据需要自己写。
我们需要知道的就是,tickBase可以传2帧的时间间隔给你所需要控制的对象。因为虽然FLASH中我们设定了固定的帧数,但实际帧数会根据运算情况而有所改变,所以我们要使用这样的方式计算两帧的间隔时间。
好了,大家可以尝试一下,或者休息一会,之后我会写如何去更好的实现这个tickBase中的内容,以达到对你想要控制的内容进行控制

基础控制流2:

上一次讲了如何使用Enterframe做基础控制流的入口,我们使用了Timer配合,使得我们可以在每次进入下一帧的时候获得两帧之间的时间。
现在我们讲讲如何通过tickBase方法进而去控制程序中需要控制的每一个类。
这次我们用《梦想花园》中的花的成长控制作为例子。
如果每一朵花都需要自动的生长,我们应该怎么做呢?没错,我们要使用tick,在每次进入下一帧的时候调用tick函数,传入时间间隔timeSpan,累加这些时间,并且判断花朵是否应该长大,从而更换花朵的状态。
为此,我们写了一个新的类BaseObject类,该类继承Sprite类,该类有个方法addObject代替addChild方法(但是我们不重写addChild,因为也有用到的时候),addObject只能用于添加另一个BaseObject类,该方法的作用是,将另一个BaseObject类的实例化对象加入该类中的一个数组里,作为他的“子”,并且在tick方法中调用它所有“子”的tick,而他所有“子”还将调用“子”中“子”数组的tick方法(如果有这么多“子”)的话。
可以看一下这个类的构造函数和addObject方法以及tick方法
public function BaseObject(mc:MoviClip) : void
{
baseObjects = new Array();   //  baseObjects 是私有属性
content = mc; //  content 是私有属性
}
public function addObject(baseObj:BaseObject)
{
objectsToAdd.push(obj); //objectsToAdd 是私有属性    用于存储需要添加的对象(这是一个临时的数组,当执行了addAndRemoveObjects()方法以后,objectsToAdd才会真正被添加到baseObjects数组中)
}
public function tickBase(timeSpan:uint) : void
{
for each(var i in baseObjects)
{
i.tickBase(timeSpan);
}
tick(timeSpan);
addAndRemoveObjects();//这个方法下次会讲到,很重要,功能是在tick的时候处理把对象添加到显示列表中
return;
}
public function tick(timeSpan:uint)
{
//该方法没有实现是个抽象方法,子类继承该抽象方法以完成tick中的内容
}
虽然这个类没有完整的书写(还不能用),但是现在已经可以对其功能进行了解。任何继承这个类的子类都会有tick方法,一旦使用了addObject方法添加其他继承该类的对象,只要在上一讲中的ENTERFRAME中,调用一个“父”级的BaseObject中的tickBase,就可以自动的传入所有该对象中的所有“子”级对象中的tick。
一旦这么做,我们可以先建立一个BaseObject对象land,然后使用land.addObject(flowe)方法,不过flowe对象也得是BaseObject的实例,然后在EnterFrame事件中执行land.tickBase()方法,在每次进入下一帧的时候,flower就会执行自己的tick方法,于是在tick中我们可以执行计算时间并选择播放花朵的那段动画的过程函数了。
这样的好处是,即便对象之间的关系很复杂(比如land中有grass(草地),grass里有pot(花盆),pot中有5个flower),我们仍然能自由的去写每一个类的tick方法,他们可以有自己的处理方式,然后只要通过addObject方法来添加他的子级就可以达到预期的效果了。
好了,下次我们再说BaseObject中的具体实现方式,现在只要了解我们是如何控制我们的对象的就可以了。

上一次讲了如何使用Enterframe做基础控制流的入口,我们使用了Timer配合,使得我们可以在每次进入下一帧的时候获得两帧之间的时间。现在我们讲讲如何通过tickBase方法进而去控制程序中需要控制的每一个类。这次我们用《梦想花园》中的花的成长控制作为例子。如果每一朵花都需要自动的生长,我们应该怎么做呢?没错,我们要使用tick,在每次进入下一帧的时候调用tick函数,传入时间间隔timeSpan,累加这些时间,并且判断花朵是否应该长大,从而更换花朵的状态。为此,我们写了一个新的类BaseObject类,该类继承Sprite类,该类有个方法addObject代替addChild方法(但是我们不重写addChild,因为也有用到的时候),addObject只能用于添加另一个BaseObject类,该方法的作用是,将另一个BaseObject类的实例化对象加入该类中的一个数组里,作为他的“子”,并且在tick方法中调用它所有“子”的tick,而他所有“子”还将调用“子”中“子”数组的tick方法(如果有这么多“子”)的话。可以看一下这个类的构造函数和addObject方法以及tick方法 public function BaseObject(mc:MoviClip) : void {        baseObjects = new Array();   //  baseObjects 是私有属性   content = mc; //  content 是私有属性   }
public function addObject(baseObj:BaseObject){        objectsToAdd.push(obj); //objectsToAdd 是私有属性    用于存储需要添加的对象(这是一个临时的数组,当执行了addAndRemoveObjects()方法以后,objectsToAdd才会真正被添加到baseObjects数组中)        }
public function tickBase(timeSpan:uint) : void {         for each(var i in baseObjects) {                    i.tickBase(timeSpan); } tick(timeSpan); addAndRemoveObjects();//这个方法下次会讲到,很重要,功能是在tick的时候处理把对象添加到显示列表中        return; }
public function tick(timeSpan:uint){ //该方法没有实现是个抽象方法,子类继承该抽象方法以完成tick中的内容}
虽然这个类没有完整的书写(还不能用),但是现在已经可以对其功能进行了解。任何继承这个类的子类都会有tick方法,一旦使用了addObject方法添加其他继承该类的对象,只要在上一讲中的ENTERFRAME中,调用一个“父”级的BaseObject中的tickBase,就可以自动的传入所有该对象中的所有“子”级对象中的tick。
一旦这么做,我们可以先建立一个BaseObject对象land,然后使用land.addObject(flowe)方法,不过flowe对象也得是BaseObject的实例,然后在EnterFrame事件中执行land.tickBase()方法,在每次进入下一帧的时候,flower就会执行自己的tick方法,于是在tick中我们可以执行计算时间并选择播放花朵的那段动画的过程函数了。
这样的好处是,即便对象之间的关系很复杂(比如land中有grass(草地),grass里有pot(花盆),pot中有5个flower),我们仍然能自由的去写每一个类的tick方法,他们可以有自己的处理方式,然后只要通过addObject方法来添加他的子级就可以达到预期的效果了。
好了,下次我们再说BaseObject中的具体实现方式,现在只要了解我们是如何控制我们的对象的就可以了。

基础控制流3:

前段时间比较忙,所以一直没有更新,不好意思。废话不多说,继续我们的控制流第三讲

这次主要讲两个内容:1、完善插入对象以及移出对象功能

2、在合适的时候更新显示列表,并更新层深

1插入以及移出对象

我们设想,因为程序需要,我们要在场景中插入一个对象,但又因为其他因素,这个对象需要立刻被移出,而这之间的间隔非常小,小到在两帧之间,这个时候我们就没有必要将该对象真正插入到显示列表中(这种情况虽然不多,但是也有可能出现)。

对应public var objectsToAdd:Array(待添加对象数组) ,我们在建一个数组  public var objectsToRemove:Array(待移出对象数组)

然后我们在addObject方法中判断该对象是否也在objectsToRemove出现,如果有,则从objectsToRemove数组中移出该对象,否则,添加到objectsToAdd数组中,removeObject方法也如法炮制。代码如下:

public function addObject(obj:BaseObject) : void

{

var removeIndex:int;

if (obj)

{

removeIndex = objectsToRemove.indexOf(obj);

if (removeIndex != -1)

{

objectsToRemove.splice(removeIndex, 1);

}

else if (objectsToAdd.indexOf(obj) == -1 && baseObjects.indexOf(obj) == -1)

{

objectsToAdd.push(obj);

if (obj.parent != null && obj.parent is BaseObject)

{

BaseObject(obj.parent).removeObject(obj);

}

}

}

return;

}

移出代码就不写了,也是一样的。这么一来,最终我们得到的肯定是一组真正要被添加到显示列表中和要从显示列表中移除的对象数组。注意,无论怎么添加和移出,尚未进入下一帧之前,显示列表中的内容都是没有变的,不会对显示性能造成影响,同时baseObjects中的内容也不会发生变化,通常baseObjects中的内容总是与现实列表中的内容保持一致。

以上两个方法为我们将要在tickBase中的方法addAndRemoveObjects()做了铺垫,那么接下来我就讨论一下addAndRemoveObjects()方法的作用于特点。

一旦大家开始做AS3游戏了,很快就会发现一个大问题,那就是层深的控制。as2中层深控制看起来可能比较简单,设置层深的深度就可以了,但这在AS3中被ADOBE取消了,可能是因为性能的低下。取而代之的是使用addChildAt方法来替代,这样一来,我们可能不能很直观的去控制我们想要控制的层深。因此我们使用addAndRemoveObjects()方法结合我们自己新建的属性drawPriority(绘画优先级)来控制层次之间的关系。这样的好处就是:无论一个场景内有多少东西的层次要发生变化,我们只会在进入下一帧的时候对这些对象排序一次。

public function addAndRemoveObjects() : void

{

var obj:BaseObject;

var index:int;

var isPrio:Boolean;

if (objectsToRemove.length > 0)

{

for each(var i in objectsToRemove)

{

obj = i;

index = baseObjects.indexOf(obj);

if (index != -1)

{

baseObjects.splice(index, 1);

}

if (obj.parent != null && obj.parent == this)

{

removeChild(obj);

}

}

objectsToRemove.splice(0, objectsToRemove.length);

}

if (objectsToAdd.length > 0)

{

for each(var k in objectsToAdd)

{

isPrio = false;

for (var j = 0; j < baseObjects.length; j++ )

{

if (k.drawPriority < baseObjects[j].drawPriority)

{

addChildAt(k, getChildIndex(baseObjects[j]));

baseObjects.splice(j, 0, k);

isPrio = true;

break;

}

}

if (!isPrio)

{

baseObjects.push(k);

addChild(k);

}

}

objectsToAdd.splice(0, objectsToAdd.length);

}

return;

}

简单的解释一下代码,一共做了两件事:

1 把放在objectsToRemove数组中的对象从baseObjects中移出,同时从显示列表中移出,清空objectsToRemove

2 根据objectsToAdd中的每个对象的drawPriority属性,将对象按顺序插入显示列表以及baseObjects数组中,清空objectsToAdd。

这样,我们就完成对该对象内的所有“子”的排序与显示层深的重置。并且每帧最多只会排序一次。

好了,现在我们的BaseObject类已经完成了一大半。拥有了添加删除对象的两个方法,并且会将帧间隔时间传入该对象的子对象以及该对象的子对象的子对象以及……(太多了不写了)中,并且拥有属性drawPriority,只要改变该属性,在进入下一帧的时候就会改变显示列表中的层次关系。

接下来,我们只要继承这个类,就可以轻松的控制每一个对象的行为(只要在类中重写tick的方法即可),以及轻松的控制他们层次关系(由于只在帧间隔的时候做,即不会在肉眼上有延迟,且执行效率也会比较高)。

大家可以动手写一个这样的类,然后去用用看,记得在文档类中调用最外层的BaseObject对象的tickBase函数,然后尝试着做添加,删除,修改层深以及覆盖tick方法的操作,这样一来,我们的基本控制流的结构就差不多了。

接下来我们会讲到如何用反射(Reflection)来简化以及模块化开发。

反射的应用:

反射(Reflection),是众多面向对象语言都有的功能,不过我在论坛上常常看到有人不理解反射的用途,甚至有人说反射没有任何用处。今天我不讲反射究竟有多少可挖掘的地方,我们就着我们的游戏内容,来讲一下反射的现实应用,很多东西,不用你是不知道的。

我们来到花园游戏,花园游戏中有许多的花,很多很多,估计有108种,并且一直在不断的增加。大家都知道,花肯定是flash做好的元件,因此我们要去库里面取,从库里面取也有很多种方法。最简单的就是在库里面元件中的属性点导出,然后在程序里就可以去实例化这个类了。比如你导出类的名字是Flower1, 那么在程序里你就可以这么写:var flower=new Flower1();然后就可以将这个类的实例添加到屏幕了。那么,如果真的这么去做游戏,你就会发现很多问题:1、每次新增加一朵花,我就不得不修改程序,重新新增加一个类的实例化过程。2、如果你想要把主程序和这些资源文件分离的话,你甚至都无法正确编译你的程序,因为在主程序中,并没有Flower1这个类。

所以我们使用了反射,在这种情况下,反射的作用相当于用字符串来取代类名,正因为他是字符串,你可以不顾及这个类当前存不存在。

好了我们开始做了,我很简单的讲一下我们的做法,我们把花的元件全部放到一个单独的fla中,每个花都有单独的导出,从Flower1到Flower108,然后生成flower_asset.swf,我们在主程序中加载这个swf文件,与此同时我们加载一个XML配置文件(具体如何加载我之后会详细说明),总之我就是加载,并且读取到了每个id的花所对应到的导出类名(比如id为20001的红玫瑰花对应的元件的类是Flower1),现在我要显示出某个用户的所有的花,比如这个用户有个花朵的数组,数组中有如下数据[20001,20002,20001,20003],那么我们就做一个循环,实例化所有的FlowerXXX的类,然后添加到舞台上。

我们先在文档类中添加一个静态方法:

public static function getMovieClip(mcname:String) : MovieClip

{

try

{

var mcClass:* = Class(getDefinitionByName(mcname));

if (mcClass == null)

{

return null;

}

return new mcClass;

}

catch (e)

{

return null;

}

return null;

}

注意我使用了try,这是为了系统的稳定性,即便因为一些特殊原因,我们取到了一些错误或者过期的数据(比如有一朵花现在已经在我们的库中删除了,但是因为策划疏忽而没有删除),我们也可以良性地处理这个问题而不至于flash报错。

然后我们在程序中就可以调用这个方法,根据从配置文件中取到的类名的字符串,调用getMovieClip方法去库中取到相应的元件。几乎在我们所有涉及到显示的类中,最后都有调用到该方法。

反射的好处在于,动态的决定了主程序将要调用的类,比如一个玩家的场景中只有5种花,我们就没必要让程序导入108个花的类(从性能上来说,反射其实是有待考究的,因为动态的东西通常比静态的慢,但是这其中的性能的得失很复杂,大家可以再讨论。)就从开发以及维护的角度来说,尽量减少编译以及版本发布的次数,让程序更容易被“定制”总是比将代码写死在程序中要好得多。

5月30日 第3次 FLASH开发者交流会火热报名中!



经过紧张的筹办,第三次FLASH开发者交流会活动开始报名咯!请大家访问:www.swfsh.com

在这次的活动中,我们邀请到AsWing开源UI框架的作者iiley同学跟大家介绍他在FLASH独立游戏开发方面的经验和感受。

同时我们请来积木网的主程谢敏同学,谢敏采用FLEX框架一手开发了积木网网上电脑,谢敏将为我们带来FLEX框架的完整的开发经验谈(超多干货!!)。

在1月活动中,向大家介绍FLASH播放器性能优化经验的主持人谈熠同学将在这次的活动中给我们介绍如何在FLASH中运用元编程方法来提高代码效率和优化开发过程。

“开心宠物”和“升职记”的主程唐翎同学将告诉大家一种全新的FLASH程序的UI设计方式,使你的程序能够超灵活地支持皮肤设定

……

还有很多新鲜有益的主题,我们将不断地更新在网站上,请关注:www.swfsh.com

基于Flash展现的互动虚拟城市解决方案的设计与实现

发一篇肖刚清华大学工硕毕业论文,初看了一下,写的还不错!暂时只看了前面,其使用技术框架为 Flash + WebService + Socket.

喜欢的话就下载下来看吧:基于Flash展现的互动虚拟城市解决方案的设计与实现 (149)

as3优化总结

1.用乘法来代替除法(当除数可转化为有限数的时候)。比如var n:Number = value * 0.5;要比var n:Number = value / 2;快。但差别并不是很大。只有在需要大量计算情况下,比如3D引擎中差别才比较明显。

2.用位运算代替除2或乘2。比如10>>1要比10*2快,而10<<1要比10*2快。从测试来看位运算几乎比乘除快一倍,但是一般情况下,我们不能选择位运算,比如我们就不能用13>>1来代替13/2,尽管前者比后者运算速度更快,但2者的运算结果却不一样。所以还是要看具体情况。

3.用unit()或int()代替取整运算Math.floor()和Math.ceil()。比如var test:uint = uint(1.5);要比var test:Number = Math.floor(1.5);快;而var test:uint = uint(1.5)+1;要比var test:Number = Math.ceil(1.5);也快。如果是Math.floor(),还可以用位运算(>>0)来代替。比如var test:uint =1.5>>0,比unit()或int()更快。

4.用乘-1来代替Math.abs()方法。比如var nn:Number = -23;var test:Number= nn < 0 ? nn * -1 : nn;要比var nn:Number = -23;var test:Number = Math.abs(nn);快。

当然还有更多的优化计算的方法。一般来说,低级运算要比高级运算速度;内部方法比调用其他方法速度快。另外要注意的是,这些方法有的时候可能并一定适用.

第二章  Actionscript 优化指南
来源 gotoAndStop.it

原著 Marco Lapi,alias Lapo, aw译

在这篇文章中,我们将讨论多种优化 Actionscript 代码的方法.
此外我们也针对一些典型的游戏代码进行了系列测试,来最大限度的发掘、提高Flash播放器的性能。


何时进行优化

对现有程序进行优化的过程,有时十分的冗长与困难,这与原始代码的非优化程度有关,所以在投入大量时间进行代码优化之前,最重要的是要估计出要在什么地方对代码做出修改或替换。

一个游戏代码的最重要的部分就是主循环体,通常情况下该循环体要在flash的每一帧上执行,并控制游戏中的角色属性和重要的数据参数。而对于主循环体以外的部分,也可能是次要循环部分,同样要注意是给其否分配了过多的资源,而没有分配给那些更需要资源的核心部分。
通过积累在各处节约出来的时间(可能每处仅仅是几个毫秒),您会明显发现自己的swf运行得更加稳定,并且游戏感也大大加强。


简洁与高效的代码

书写出十分简洁、可以再次调用的代码(有时可能是面向对象的)是一项精细的工作,但这需要多年的编程经验。对于OOP(object oriented programming,面向对象的程序设计),有些场合根本利用不到它的优势,这使得它显得十分奢侈。在有限的资源条件下(可能是flash播放器的原因),通过更先进的方法,像刚刚提到的OOP,就可能反而导致令人不满意的结果。

我们并不是说OOP对游戏编程不好,只是在某些场合它显得过于奢侈和多余。毕竟有时候“传统的方法”却能得到更好的结果。

大体而言,用OOP是比较好的,因为它让代码维护更加简单。但在后文中,你会看到有时为了充分发挥flashplayer性能,而不采用OOP技术。例如:处理快速滚动或者计算十分复杂的数学问题。


基本的优化

一提及代码优化,我们马上会联想到执行速度的改进,而很少去考虑系统资源的分配。这是因为当今,即使是将被淘汰的计算机,都有足够的内存来运行我们大部分的flash游戏(128M的内存足以满足大多数情况的需要,况且,512M的内存是当今新电脑的基本配置)

变量

在各种重要的代码优化手段中,有这么一条:在定义局部变量的时候,一定要用关键字var来定义,因为在Flash播放器中,局部变量的运行速度更快,而且在他们的作用域外是不耗占系统资源的。

aw附:var变量仅仅在花括号对中才有“生命”,个人认为没有系统学过编程的人容易出错的一个地方:

awMC.onLoad = function(){
var aw = 1;
}
awMC.onEnterFrame = function(){
//不存在aw这个变量
}
一段非优化代码:

function doSomething()
{
mx = 100
my = 100
ar = new Array()

for (y=0; y < my; y++)
{
for (x=0; x < mx; x++)
{
i = (y * mx) + x
arr[i] = i
}
}

return arr
}

这段代码中,并未声明函数体内的那些变量(那些仅仅在函数内使用的变量)为局部变量,这使得这些变量被播放器调用的速度更慢,并且在函数执行完毕的时候仍然耗占系统资源。

下面列出的是经过改进的同样功能的代码:

function doSomething()
{
var mx = 100
var my = 100
var ar = new Array()

for (var y=0; y < my; y++)
{
for (var x=0; x < mx; x++)
{
var i = (y * mx) + x
arr[i] = i
}
}
return arr
}
这样一来所有的变量均被定义为了局部变量,他们能够更快地被播放器调用。这一点在函数大量(10,000次)循环运行时显得尤为重要!当一个函数调用结束的时候,相应的局部变量都会被销毁,并且释放出他们占有的系统资源。

onEnterFrame 事件

onEnterFrame事件对于游戏开发者而言是非常有用的,它使得我们能够快速、反复地按照预设帧频(fps)运行一段程序。回想在Flash5的时代,这(onEnterFrame实时监控)是一种非常流行的技术,用这样的事件来控制机器游戏对手的逻辑,又或者我们可以在每一个子弹上设置这样的事件来监测子弹的碰撞。

实际上,我们并不推荐给过多的MoveClip添加这样的事件,因为这样做会导致“无头绪码(spaghetti code)”的出现,并且容易导致程序效率明显降低。

大多数情况下,用单独一个onEnterFrame事件就可以解决问题了:用这一个主循环来执行你所需要的操作。

另一个简单的办法是设置一个合适的帧频:要知道帧频越高,CPU资源就越紧张。

在帧频为25-35(fps)之间时,onEnterFrame足以很好地执行较复杂代码,哪怕你的计算机配置较低。因此,在没有特殊要求的场合,我们不推荐使用高于60(fps)的帧频。

矢量图与位图

在处理图形前,我们一定要做出正确的选择。Flash能对矢量图和位图进行完美的兼容,然而矢量图和位图在播放器中的表现实质却完全不同。

在用到矢量图的时候,我们要尽可能简化它们的形状,去除多余的端点。这样做将大大降低播放器用于呈现矢量图所要进行的计算量。另一个重要方面在于线条的运用,尽量减少和避免冗陈的线条结构,因为它们会直接影响到flash的播放效率。

当某个实例透明度小于100时,也会对播放速率造成影响,所以如果你发现自己的Flash播放速率过慢,就去挑出这些透明的实例来吧!

那么,如果真的需要呈现比较复杂的场景时,你就最好考虑使用位图实现。虽然Flash在对位图的渲染效率上并不是最优越的(比如和Flash的“兄长”Director比起来),但丰富的视觉内容呈现只能靠位图(与位图同复杂度的矢量图形渲染速率非常低)了,这也是很多基于区块的游戏中广泛采用像素图作为背景的原因。顺便要提到的是,Flash虽然对GIF,JPG和PNG都有所支持,但是渲染速度上PNG还是占有绝对优势,所以我们建议flash中的位图都尽可能采用PNG格式。

影片剪辑(MovieClip)的可视性[下面将MovieClip简称为mc]

您可能会经常碰到这样一种情况:有大量不可见/屏幕外的mc等待出场(比如游戏中屏幕外的地图、人物等等)。
要知道,播放器仍然要消耗一定的资源来处理这些不可见/屏幕外的mc,哪怕他们是单帧,非播放的状态。

最好的解决办法之一是给这些mc一个空白帧,当他们不出现在屏幕上时,你能用gotoAndStop()语句跳转到这一帧,从而减少播放器对资源的需求。

请务必记住,这种情况下,简单的设置可见度属性为不可见( _visible = false )是无效的,播放器将继续按照这些mc所停留或播放的帧的复杂度来分配资源。
数组
数组在各种需要记录数据的应用程序和游戏中都被广泛的使用。

一个典型的例子就是基于区块的Flash游戏,在这样一类的游戏中,地图有时被存放成形如arr[y][x]的二维数组。虽然这是一种很常见的方法,但是如果用一维数组的话,却能提高程序的运行效率。另一个重要的方法来提高数组效率是在数组遍历的时候使用for in 循环来代替传统的 for 或者while循环语法。
例如:

一段代码如下
for (var i in arr)
{
if (arr[i] > 50)
{
// 进行某些操作
}
}

它的执行速度明显高于这一段代码:
for (var i=0; i < 10000; i++)
{
if (arr[i] > 50)
{
// 进行某些操作
}
}
前者的效率比后者提高了30%,这个数字在你的游戏要逐帧执行这一段代码的时候显得更加宝贵!


高级优化:
1) for循环 和 while循环
用while循环将会得到比for循环更好的效率。然而,从数组中读取数据,用for in循环式最好的选择!

所以我们不推荐使用:
for (var i=0; i < 1000; i++)
{
//进行某些操作
}而推荐使用

var i=-1
while (++i < 1000)
{
//进行某些操作
}

2) 从数组中读取数据
我们通过测试发现,for in循环的效率大大高于其他的循环方式。参看:
arr = []
MAX = 5000

//数组赋值
for (i=0; i < MAX; i++)
{
arr[i] = i
}
var item = null

// For 循环

for (var i=0; i < MAX; i++)
{
item = arr[i]
}

// For 循环
for (var i in arr)
{
item = arr[i]
}

// While 循环
i = -1
while(++i < MAX)
{
item = arr[i]
}

3) 向数组中写入数据(while , for)
可以看到while循环稍占优势。

4) _global(全局)变量同Timeline(时间轴)变量
我们猜测采用全局变量能提高变量调用速度,然而效果并不像预计的那样明显。

5) 单行、多行变量赋值
我们发现单行变量赋值效率大大高于多行。比如:
a = 0
b = 0
c = 0
d = 100
e = 100

效率就不如:
a = b = c = 0
d = e = 100

6) 变量名寻址
这个测试反映了变量名的预寻址是非常重要的,尤其是在循环的时候,一定要先给丁一个指向。这样大大节约了寻址时间。

比如:
var num = null
t = getTimer()
for (var i=0; i < MAX; i++)
{
num = Math.floor(MAX) – Math.ceil(MAX)
}
t1.text = “Always lookup: ” + (getTimer() – t)

就不如:
t = getTimer()
var floor = Math.floor
var ceil  = Math.ceil
for (var i=0; i < MAX; i++)
{
num = floor(MAX) – ceil(MAX)
}

7) 短变量名和长变量名
变量名越短,效率越高。考虑到长变量名也有它的好处(比如,便于维护等),因此建议在关键部位(比如大量循环出现的时候)使用短变量名,最好就1-2个字符。

8) 循环前、后声明变量
在测试前,我们认为循环前声明变量会更加节约时间,不料测试结果并不明显,甚至还恰恰相反!

// 内部声明
t = getTimer()
for (var i=0; i < MAX; i++)
{
var test1 = i
}
t1.text = “Inside:” + (getTimer() – t)

// 外部声明
t = getTimer()
var test2
for (var i=0; i < MAX; i++)
{
test2 = i
}

9) 使用嵌套的if结构
当用到复杂的条件表达式时。把他们打散成为嵌套的独立判断结构是最佳方案。下面的代码我们进行了测试,发现这种效果改进明显!
MAX = 20000
a = 1
b = 2
c = -3
d = 4

var i=MAX
while(–i > -1)
{
if (a == 1 && b == 2 && c == 3 && d == 4)
{
var k = d * c * b * a
}
}

//下面的判断更加节省时间
var i=MAX
while(–i > -1)
{
if (a == 1)
{
if (b == 2)
{
if (c == 3)
{
if (d == 4)
{
var k = d * c * b * a
}
}
}
}
}

10) 寻找局部变量(this方法同with方法比较)
局部变量的定位方法很多。我们发现用with比用this更加有优势!
obj = {}
obj.a = 1
obj.b = 2
obj.c = 3
obj.d = 4
obj.e = 5
obj.f = 6
obj.g = 7
obj.h = 8
obj.test1 = useThis
obj.test2 = useWith
MAX = 10000
function useThis()
{
var i = MAX
while(–i > -1)
{
this.a = 1
this.b = 2
this.c = 3
this.d = 4
this.e = 5
this.f = 6
this.g = 7
this.h = 8
}
}
function useWith()
{
var i = MAX
while(–i > -1)
{
with(this)
{
a = 1
b = 2
c = 3
d = 4
e = 5
f = 6
g = 7
h = 8
}
}
}

11) 循环监听键盘事件
同刚才所提到的寻址一样,我们实现给一个指向会得到更好的效率,比如:
keyDown = Key.isDown
keyLeft = Key.LEFT

//我们再用 if (keyDown(keyLeft))
附:我们测试了按键代码和键值常量的效率发现并无太大差别。

12) Math.floor()方法与int()
这个问题曾在Flashkit的论坛被提出讨论过。测试表明,旧的int方法反而效率更高。我们的测试结果也反映了这一点。

13)eval表达式与中括号语法
我们并没有发现明显的差别,并不像刚才所述那样,旧的eval表达式比起中括号方法并没有太大的优势
var mc = eval_r(“_root.myMc” + i)
var mc = _root["myMc" + i]
//两者效率差不多16) 涉及MC的循环:ASBroadcaster 同欢同循环的差别


结论
我们从这些测试结果中发现,对于不同的需求,采用不同的代码,我们可以大大提高脚本的执行效率。虽然我们在这里罗列了许多的优化代码的方法,需要大家自己测试、实验的还有很多(考虑到每个人的需求不同).如果你想更加深入地讨论这类问题。可以来我们的论坛。

aw附:
终于翻译完了,自己也学到很多好东西,大家又什么问题可以去gotoAndPlay的官方,也可以来我的Blog提出!

第三章  黑羽AS心得:浅释ActionScript的代码优化
来源:Kingda blog

本文既为浅谈代码优化,那么就不深入到OOP设计层面。仅涉及Flash8帮助里面提到的一些代码编写优化原则,并加以解释。
准则来源于Flash8 帮助,我做了一些解释:

1.避免从一个循环中多次调用一个函数。
在循环中包含小函数的内容,可使效果更佳。小函数生命期短,利于资源释放。尤其是在大的循环中时。

2.尽可能使用本机函数。
本机函数要比用户定义的函数运行速度更快。本机函数即Flash中内有的一些函数(intrinsic),比如hitTest(),你没必要自己写一个类似的。

3.不要过多使用 Object 类型。
数据类型注释应力求精确,这样可以提高性能。只有在没有适当的备选数据类型时,才使用 Object 类型。同时也便于代码管理,时刻知道对象的类型和作用。
同时也有利于编译器编译时优化。

4.避免使用 eval_r() 函数或数据访问运算符。
通常,较为可取且更有效的做法是只设置一次局部引用。不得已时才用eval,比如转换_droptarget为MovieClip时。

5.在开始循环前将 Array.length 赋予变量,尤其是大的循环。
在开始循环前将 Array.length 赋予变量(比如var iLength:Number),将其作为条件使用,而不是使用 myArr.length 本身。
原因,在循环中,iLength是Number变量,会被放入寄存器使用,效率远比访问Array再得到length高。例如,应使用

var fontArr:Array = TextField.getFontList();
var arrayLen:Number = fontArr.length;
for (var i:Number = 0; i < arrayLen; i++) {
trace(fontArr[i]);
}

来代替:
var fontArr:Array = TextField.getFontList();
for (var i:Number = 0; i < fontArr.length; i++) {
trace(fontArr[i]);
}

6.注重优化循环及所有重复动作。
Flash Player 花费许多时间来处理循环(如使用 setInterval() 函数的循环)。

7.在局部变量够用时,不要使用全局变量。类静态变量也要少用。
全局变量是开发者的恶梦。实在需要全局变量的话,我建议使用singleton设计模式来进行管理。

8.声明变量时,添加 var 关键字。
这是为了编译时让编译器知道你的变量类型,优化编译。

黑羽补充一点:对关键字的使用要谨慎。
不赞成使用关键字作为自己的method和属性名,除非你确认后续开发不会用到相同的事件名和属性名。
但你怎么知道flash使用了多少隐藏关键字?太多了!比如说 className, invalidate, refresh, mouseOver等等不常用的关键词。好的方法是使用SEPY编辑器来写代码,那里面加亮了所有公布的和没有公布的关键词。
而且因为很有可能和start,load,等这些常用的事件名重复,带来代码不必要的修改和麻烦。

9.对涉及到调用绘图资源的函数时,尽量先多判断再调用。
所有渐变,位置变化,创建删除MC,组件等函数都涉及到绘图资源的调用。在很多情况下,尽量先用逻辑判断变量或者对象的属性,必要时再调用这些函数。这样可以节省较多的计算资源。

Flex开发中可能出现内存泄漏的地方

收集了不少的会导致内存泄露的情况:
事件监听:
对父级对象加了监听函数,会造成内存泄露,例:
override protected function mouseDownHandler(…):void {
systemManager.addEventListener(“mouseUp”, mouseUpHandler);
……
}
解决:
在销毁对象的时候,remove掉这些监听,虽然弱引用也可以避免这些问题,但自己掌控感觉更好。
但以下几种情况不会造成内存泄露:
弱引用:someObject.addEventListener(MouseClick.CLICK, handlerFunction, false, 0, true);
对自己的引用:this.addEventListener(MouseClick.CLICK, handlerFunction);
子对象的引用:
private var childObject:UIComponent = new UIComponent;
addChild(childObject);
childObject.addEventListener(MouseEvent.CLICK, clickHandler);
总之…有addEventListener,就removeEventListener一下吧,要为自己做的事负责~哈哈
清除引用
remove掉子对象后并不会删除该对象,他还保留在内存中,应该将引用设置为null
removeChildren(obj);
obj = null;
静态成员
Class (或MXML)中有:
public static var _eventService : MyService=new MyService();
解决:在dispose时,需要设置:
_eventService =null;
module (未解决)
moduleLoader unloadModule后
ModuleInfo 并不会被GC.
Garbage Collection in a MultiCore Modular Pipes Application
这篇文章介绍了一种GC策略,感觉对于ModuleInfo 的GC无效。
(未尝试、未遇到)
CSS样式
module 中如果使用了shell的CSS定义或是<mx:Style> 这样的定义,那么这个module将不能GC.
弹出的窗口应该是同样的结果.
解决方法,使用动态CSS文件
module init中
StyleManager.loadStyleDeclarations(“css/myStyle.swf”);
module dispose中
StyleManager.unloadStyleDeclarations(“css/myStyle.swf”);
TextInput/Textarea(未解决)
如果module中有window使用了TextInput/Textarea控件,不点击没有问题,只要点上去,那么很遗憾了,module和所在窗体将不能被GC.
这个BUG非常严重,目前还没有解决方法。
memory leak when using TextInput and TextArea when click the keyboard 这里面附加的解决方法无效。
通过profiler分析,应该和Focusmanager有关,只有一点击就不会释放。
CursorManager.setCursor
使用了
cursorID = CursorManager.setCursor(iconClosed);
dispose时要
CursorManager.removeCursor(cursorID);
Bitmap
如果使用Bitmap,结束时需要调用其dispose方法,否则内存消耗巨大。
另外,BitmapData是可以共享使用的,多个Bitmap可以使用同一BitmapData,节省不少内存。
var bmp:Bitmap  =new Bitmap();
……..
if (bmp.bitmapData!=null) {
bmp.bitmapData.dispose();
}
Image
包含了Image对象时,在removeChildren时会造成不能释放(测试多次,结果不一,建议还是做如下处理)。
解决:
img.source = null;
this.removeChild(img);
img = null;
Loader、SWFLoader、声音、视频、Effect等…
如果是加载SWF文件,先停止播放。
停止声音的播放
停止正在播放的影片剪辑(Movieclip)
关闭任何连接的网络对象,例如Loader正在加载,要先close。
取消对摄像头或者麦克风的引用
取消事件监听器
停止任何正在运行的定时器,clearInterval()
停止任何Timer对象,timer.stop()
停止正在播放的效果(Effect)
其他
binding也疑似有memory leak 问题。
引用以及内存泄露相关博文和资料:
http://blogs.adobe.com/aharui/2007/03/garbage_collection_and_memory.html
http://www.craftymind.com/2008/04/09/kick-starting-the-garbage-collector-in-actionscript-3-with-air/
http://www.cnblogs.com/janyou/archive/2008/11/25/1340753.html
http://www.dreamingwell.com/articles/archives/2008/05/understanding_m.php
总结:由于之前Flash一直是在网页上使用,一般网页都是看完就关掉的,估计Adobe在内存回收这块也没有下太大的功夫,现在AIR的出现使得内存管理也相当重要了,并且,AIR本身对内存的消耗就相当大,一个没有任何内容的初始创建的AIR程序,就得占掉10-20M+的内存…AIR还需改善.

收集了不少的会导致内存泄露的情况:
事件监听:
对父级对象加了监听函数,会造成内存泄露,例:
override protected function mouseDownHandler(…):void {systemManager.addEventListener(“mouseUp”, mouseUpHandler);……}解决:在销毁对象的时候,remove掉这些监听,虽然弱引用也可以避免这些问题,但自己掌控感觉更好。
但以下几种情况不会造成内存泄露:
弱引用:someObject.addEventListener(MouseClick.CLICK, handlerFunction, false, 0, true);对自己的引用:this.addEventListener(MouseClick.CLICK, handlerFunction);子对象的引用:private var childObject:UIComponent = new UIComponent;addChild(childObject);childObject.addEventListener(MouseEvent.CLICK, clickHandler);总之…有addEventListener,就removeEventListener一下吧,要为自己做的事负责~哈哈
清除引用
remove掉子对象后并不会删除该对象,他还保留在内存中,应该将引用设置为null
removeChildren(obj);obj = null;
静态成员
Class (或MXML)中有:
public static var _eventService : MyService=new MyService();解决:在dispose时,需要设置:
_eventService =null;module (未解决)
moduleLoader unloadModule后ModuleInfo 并不会被GC.Garbage Collection in a MultiCore Modular Pipes Application这篇文章介绍了一种GC策略,感觉对于ModuleInfo 的GC无效。(未尝试、未遇到)
CSS样式
module 中如果使用了shell的CSS定义或是<mx:Style> 这样的定义,那么这个module将不能GC.弹出的窗口应该是同样的结果.解决方法,使用动态CSS文件module init中
StyleManager.loadStyleDeclarations(“css/myStyle.swf”);module dispose中
StyleManager.unloadStyleDeclarations(“css/myStyle.swf”);TextInput/Textarea(未解决)
如果module中有window使用了TextInput/Textarea控件,不点击没有问题,只要点上去,那么很遗憾了,module和所在窗体将不能被GC.这个BUG非常严重,目前还没有解决方法。memory leak when using TextInput and TextArea when click the keyboard 这里面附加的解决方法无效。通过profiler分析,应该和Focusmanager有关,只有一点击就不会释放。
CursorManager.setCursor
使用了
cursorID = CursorManager.setCursor(iconClosed);dispose时要
CursorManager.removeCursor(cursorID); Bitmap
如果使用Bitmap,结束时需要调用其dispose方法,否则内存消耗巨大。另外,BitmapData是可以共享使用的,多个Bitmap可以使用同一BitmapData,节省不少内存。
var bmp:Bitmap  =new Bitmap();
……..
if (bmp.bitmapData!=null) {    bmp.bitmapData.dispose();}Image
包含了Image对象时,在removeChildren时会造成不能释放(测试多次,结果不一,建议还是做如下处理)。解决:
img.source = null;this.removeChild(img);img = null;Loader、SWFLoader、声音、视频、Effect等…
如果是加载SWF文件,先停止播放。停止声音的播放停止正在播放的影片剪辑(Movieclip)关闭任何连接的网络对象,例如Loader正在加载,要先close。取消对摄像头或者麦克风的引用取消事件监听器停止任何正在运行的定时器,clearInterval()停止任何Timer对象,timer.stop()停止正在播放的效果(Effect)其他
binding也疑似有memory leak 问题。
引用以及内存泄露相关博文和资料:http://blogs.adobe.com/aharui/2007/03/garbage_collection_and_memory.html

http://www.craftymind.com/2008/04/09/kick-starting-the-garbage-collector-in-actionscript-3-with-air/

http://www.cnblogs.com/janyou/archive/2008/11/25/1340753.html

http://www.dreamingwell.com/articles/archives/2008/05/understanding_m.php

总结:由于之前Flash一直是在网页上使用,一般网页都是看完就关掉的,估计Adobe在内存回收这块也没有下太大的功夫,现在AIR的出现使得内存管理也相当重要了,并且,AIR本身对内存的消耗就相当大,一个没有任何内容的初始创建的AIR程序,就得占掉10-20M+的内存…AIR还需改善.

as 动画融合投掷、拖拽、碰撞的例子

先看一下效果:(拖动小球往一个方向抛出,小球可获得加速度)

源码如下:

package {
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	public class Throwing extends Sprite {
		private var ball:Ball;
		private var vx:Number;
		private var vy:Number;
		private var bounce:Number = -0.7;
		private var gravity:Number = .5;
		private var oldX:Number;
		private var oldY:Number; 

		public function Throwing() {
			init();
		}
		private function init():void {
			graphics.beginFill(0xffffff,0.8);
			graphics.drawRoundRect(0,0,2000,1200,0);
			//设置自动尺寸匹配
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align=StageAlign.TOP_LEFT;
			ball = new Ball();
			ball.x = stage.stageWidth / 2;
			ball.y = stage.stageHeight / 2;
			//横纵向加速度
			vx = Math.random() * 10 - 5;
			vy = -10;
			addChild(ball);
			ball.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		private function onEnterFrame(event:Event):void {
		vy += gravity; ball.x += vx;
		ball.y += vy; var left:Number = 0;
		var right:Number = stage.stageWidth;
		var top:Number = 0;
		var bottom:Number = stage.stageHeight;
		if (ball.x + ball.radius > right) {
				ball.x = right - ball.radius;
				vx *= bounce;
			}else if (ball.x - ball.radius < left) {
				ball.x = left + ball.radius; vx *= bounce;
		}
		if (ball.y + ball.radius > bottom) {
				ball.y = bottom - ball.radius; vy *= bounce;
			}else if (ball.y - ball.radius < top) {
				ball.y = top + ball.radius; vy *= bounce;
			}
		}
		private function onMouseDown(event:MouseEvent):void {
			oldX = ball.x; oldY = ball.y;
			stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
			ball.startDrag();
			removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			addEventListener(Event.ENTER_FRAME, trackVelocity);
		}
		private function onMouseUp(event:MouseEvent):void {
			stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
			ball.stopDrag();
			removeEventListener(Event.ENTER_FRAME, trackVelocity);
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		private function trackVelocity(event:Event):void {
			vx = ball.x - oldX;
			vy = ball.y - oldY;
			trace(oldX+"======="+ball.x);
			oldX = ball.x;
			oldY = ball.y;
		}
	}
}

基于FLASH平台的3D RIA应用-网上世博会

2010上海世界博览会之网上世博会

http://www.expo.cn

1、[园区揽胜—园区]
园区揽胜是网上世博会BS平台的核心体验内容。
用户通过[园区揽胜]进入园区,页面出现上海世博园地图,海宝自动讲解园区内容。
页面下方左侧,适时显示网站最新信息;用户在页面下方进行五个片区的选择,点击片区可分别进行体验。
点击 按钮,可以暂停海宝当前讲解,点击暂停,按钮变成 ,点击继续进行讲解。

2、[园区揽胜—片区]
选择片区进入。海宝自动讲解片区内容。
点击 按钮,暂停海宝当前讲解,点击暂停,按钮变成 ,点击继续进行播放。
点击 按钮,切入下一个片区体验。
点击  按钮,返回园区,用户重新对片区进行选择。
以A片区为例,用户到达A片区,页面显示A片区地图。片区展馆以特殊的标识在地图区动态显示。
(1)、双击地图上展馆,快速进入展馆体验。
(2)、点击下方展馆推荐,可进行展馆体验。

3、[园区揽胜—展馆]
以中国馆为例,用户进入中国馆,海宝自动介绍中国馆内容。
点击 按钮,暂停海宝当前讲解,点击暂停,按钮变成 ,点击继续进行播放。
点击 按钮,切入下一个展馆体验。
点击 按钮,返回片区地图,用户重新选择展馆体验。
以A片区为例,用户到达A片区,页面显示A片区地图。片区展馆以特殊的标识在地图区动态显示。
(1)、双击地图上展馆,快速进入展馆体验。
(2)、点击下方展馆推荐,可进行展馆体验。

用户在中国馆内可进行多种功能的体验。

(1) 平视转转看进入平视转转看的环视体验,可上下左右拖动地图。

(2) 俯视转转看 
高空俯视展馆的外观,拖动展馆可进行多角度的浏览。

(3) 加入收藏将展馆加入收藏夹,以便用户登录时能快速进入与浏览。
注:此功能只有注册/登录用户才能使用,普通游客例外。

4、园区地图体验
通过点击页面右上方 功能键,进入上海世博园园区地图。
此体验方式提供园区揽胜、园区活动、深度聚焦的三种不同类别的内容体验。是以分类选择的方式,提供用户不同内容体验,通过类别选择用户能快速选择需要的信息。
鼠标滚动可对地图区进行放大缩小,通过左上角的地图功能,也可对园区进行缩放;

(1)园区揽胜

选择园区揽胜,五大片区分别以不同的颜色覆盖在园区地图;

双击A片区,地图放大片区内容,A片区所有展馆以标注点的形式显示在地图上。
点击中国馆,提供中国馆的三种体验模式。

    全景:到达该展馆外,海宝自动介绍展馆内容,可进行平视转转看、俯视转转看、加入收藏四种功能的选择。(同上园区揽胜中的展馆体验)

    俯视转转看:以高空俯视展馆外观。拖动展馆可进行多角度的浏览。

    平视转转看:进入平视转转看的环视体验,可上下左右拖动地图。