链表使用中隐藏的杀机(1)
以as3为例
链表是很有用且方便的数据结构,能方便的解决很多问题。
对于游戏开发来讲,很有用途。
例如游戏中的子弹(及其附加的效果)在游戏运行过程中会大量产生,如果用数组来存储这些子弹,那么每一帧中需要遍历的数组长度中包含着大量的冗余。这是得不偿失的。而且如果子弹生命周期结束就将其移除并再不停地产生新的对象也很耗cpu, 对于用flash player做3D来讲,能节省哪怕一点cpu留给图像渲染器,也是值得的。
因此我采用的做法是用两个链表来存放使用中和已经使用结束准备再利用的子弹。
存放使用中的子弹的链表: bulletLink
存放已经使用结束准备再利用的子弹 bulletSLink(作为对象池)
这两个链表都属于同一个链表类 DollLink 的实例(双链表),
每一个子弹对象的对象暂定为 bullet是Bullet类的实例
Bullet类的实例都能放入 DollLink链表中。
Bullet类中有一个公用方法 update() 对于bullet 这个方法实现了他几乎所有的AI。
这个方法大体结构如下:
public function update():void{
if(_isSleep){
if(lifetime < 0){
…//其他代码
// 当子弹自己生命结束,就将自己从使用链表中删除
// 并加入存储链表中以备再次使用
bulletLink.remove( this );
bulletSLink.append( this );
…//其他代码
}else{
…//其他代码
}
lifetime --;
}
}
当这个方法写好的时候,我自己认为不会有问题。但是下面的使用方式,直接将我引入泥潭。
在游戏运行的每一帧的遍历中,对于子弹的操作我是这样做的:
var b:Bullet = bulletLink.getBegin();
while(b != null){
b.update();
b = b.next;
}
我愿意为,子弹自身处理自己生命结束(lifetime < 0)的动作就会完美的展现其行为,结果,我错了。子弹射出后其轨迹半天才消失。我以为是链表自身的问题,我折腾了半天,不但没有结果还弄出了循环超时的蠢问题。
经过静下心来认真分析, 我想先测一下链表长度在运行中的变化。我输出了链表在运行中的长度值,法相我不停的发射火炮的时候,长度竟然飙到了2000多, 老天! 这个信号,直接将我引入了错误的根源:
在上面的while循环中,调用update的时候,如果lifeTime<0成立, 那么b.next就等于null了因此while循环无法正常遍历完所有子弹就提前结束了
庆幸哦。
我将上面的循环改为:
var b:Bullet = bulletLink.getBegin();
var nextB:Bullet = null;
while(b != null){
nextB = b.next;
b.update();
b = nextB;
}
然后在测试,所有奇怪的现象都消失了。
在测试链表长度,也是正常的。
链表很有用,但是因为操作引用,所以要小心使用,我的大意(惯性思维和不熟练)造成了这个耗时的错误。
将这个东西列出来,大家一起分享。