zoukankan      html  css  js  c++  java
  • “AS3.0高级动画编程”学习:第一章高级碰撞检测

    AdvancED ActionScript 3.0 Animation 是Keith Peters大师继"Make Things Move"之后的又一力作,网上已经有中文翻译版本了,打算下一阶段开始啃这本书。

    今天开始学习高级碰撞检测,所用到的预备知识:

    原作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com 

    1、BitmapData的透明与不透明区别

    位图数据(BitmapData)有二种模式,一种支持透明(即每个像素的值采用AARRGGBB这种32位颜色表示);另一种不支持透明度(即传统的RRGGBB这种24位颜色表示,简单点讲就是alpha分量默认为FF,且不能修改),下面这个示例说明了区别:

     1 package {
     2     import flash.display.Bitmap;
     3     import flash.display.BitmapData;
     4     import flash.display.Sprite;
     5     import flash.display.StageAlign;
     6     import flash.display.StageScaleMode;
     7     import flash.geom.Rectangle;
     8      
     9     [SWF(height="400",width="300")]
    10     public class BitmapCompare extends Sprite {
    11         public function BitmapCompare() {
    12             stage.align=StageAlign.TOP_LEFT;
    13             stage.scaleMode=StageScaleMode.NO_SCALE;
    14              
    15             //随机在舞台上划一些线条
    16             graphics.lineStyle(0);
    17             for (var i:int=0; i<100; i++) {
    18                 graphics.lineTo(Math.random()*300,Math.random()*400);
    19             }
    20              
    21             //创建一个不透明的位图
    22             var bmpd1:BitmapData=new BitmapData(300,200,false,0xffff99);
    23             bmpd1.fillRect(new Rectangle(100,50,100,100),0xff0000);//注意:因为不透明的,所以颜色是24位的,没有alpha分量
    24             var bmp1:Bitmap=new Bitmap(bmpd1);
    25             addChild(bmp1);
    26              
    27             //创建一个支持透明的位图
    28             var bmpd2:BitmapData=new BitmapData(300,200,true,0x80ffff99);//注:默认为50%透明的ff9颜色
    29             bmpd2.fillRect(new Rectangle(100,50,100,100),0x80ff0000);//注:此处为32位颜色
    30             var bmp2:Bitmap=new Bitmap(bmpd2);
    31             bmp2.y=200;
    32             addChild(bmp2);         
    33         }
    34     }
    35 }
    View Code

    可以看到,上半部分的位图因为不支持透明,所以将背后的线条全部挡住了。

    2、五角星的画法

    先来看一个beginFill方法的神奇之处

    1 graphics.lineStyle(0);
    2 graphics.beginFill(0xffff99);
    3 graphics.moveTo(10,10);
    4 //注意下面只划了二条边
    5 graphics.lineTo(10,100);
    6 graphics.lineTo(100,100);
    7 //graphics.lineTo(10,10); //注:正是因为上面的graphics.beginFill(0xffff99);所以这条线flash会为我们自动补齐
    View Code

    注意:虽然只画了二条线,但由于应用了begeinFill方法,flash自动生成了第三条线,形成了一个封闭的三角形.

    回到正题,将一个圆周等分为10份,然后交替用不同的半径值结合三角函数,就能画出一个五角星

     1 var angleBase:Number=Math.PI*2/10;
     2 var radius:uint=100;
     3 var r2:uint;
     4 var i:uint;
     5 var px,py:Number;
     6  
     7 var starline:Sprite = new Sprite();
     8 starline.graphics.lineStyle(0);
     9 for (i=0; i<10; i++) {
    10     r2=radius;
    11     if (i%2==0) {
    12         r2=radius/2;
    13     }
    14     starline.graphics.moveTo(0,0);
    15     px=r2*Math.cos(angleBase*i);
    16     py=r2*Math.sin(angleBase*i);
    17     starline.graphics.lineTo(px,py);
    18 }
    19  
    20 addChild(starline);
    21 starline.x=stage.stageWidth/2;
    22 starline.y=stage.stageHeight/2;
    23  
    24 var star:Sprite = new Sprite();
    25 star.graphics.lineStyle(1,0xff0000);
    26 star.graphics.beginFill(0xffff99);
    27 star.graphics.moveTo(radius/2,0);
    28 for (i=0; i<10; i++) {
    29     r2=radius;
    30     if (i%2==0) {
    31         r2=radius/2;
    32     }
    33     px=r2*Math.cos(angleBase*i);
    34     py=r2*Math.sin(angleBase*i);    
    35     star.graphics.lineTo(px,py);
    36      
    37 }
    38 //star.graphics.lineTo(radius/2,0);
    39 addChild(star);
    40 star.x=stage.stageWidth/2;
    41 star.y=stage.stageHeight/2;
    42 star.alpha = 0.5;
    View Code

    当然,封装成一个单独的类会更好,下面是Star.as的完整代码,以后会经常用到这个类

    3、矩阵的运用(将上面的五角星转化为BitmapData)

    可能有人注意到了,上面的五角星图形,其注册中心点是五角星正中心,所以直接用bitmapData的draw把它画出来,将只能显示一部分:

     1 var bmd1:BitmapData = new BitmapData(100,100,false,0xffefefef);
     2 bmd1.draw(star1);
     3 var bmp1:Bitmap = new Bitmap(bmd1);
     4 addChild(bmp1);
     5 bmp1.x = bmp1.y = 10;
     6  
     7 var bmd2:BitmapData = new BitmapData(100,100,false,0xffefefef);
     8 var m:Matrix = new Matrix();
     9 trace(m.a,m.b,m.c,m.d,m.tx,m.ty);//1 0 0 1 0 0
    10 m.tx = 50;
    11 m.ty = 50;
    12 trace(m.a,m.b,m.c,m.d,m.tx,m.ty);//1 0 0 1 50 50
    13 bmd2.draw(star1,m);
    14 //等效于
    15 //bmd2.draw(star1,new Matrix(1,0,0,1,50,50))
    16 var bmp2:Bitmap = new Bitmap(bmd2);
    17 addChild(bmp2);
    18 bmp2.x = bmp1.x + 110;
    19 bmp2.y = bmp1.y;
    View Code

    如上,为了能完整的用位图"画"出五星,需要将星星向左、向下移动一定的位置,即前面提到的矩阵变换

    ok,下面才是真正的开始,先来看下位图之间的碰撞检测:

     1 package {
     2     import flash.display.Bitmap;
     3     import flash.display.BitmapData;
     4     import flash.display.Sprite;
     5     import flash.display.StageAlign;
     6     import flash.display.StageScaleMode;
     7     import flash.events.MouseEvent;
     8     import flash.filters.GlowFilter;
     9     import flash.geom.Matrix;
    10     import flash.geom.Point;
    11     public class BitmapCollision1 extends Sprite {
    12          
    13         private var bmpd1:BitmapData;
    14         private var bmp1:Bitmap;
    15         private var bmpd2:BitmapData;
    16         private var bmp2:Bitmap;
    17          
    18         public function BitmapCollision1() {
    19             stage.align=StageAlign.TOP_LEFT;
    20             stage.scaleMode=StageScaleMode.NO_SCALE;
    21             var matrix:Matrix = new Matrix();
    22             var radius:uint = 50;
    23             matrix.tx = radius;
    24             matrix.ty = radius;         
    25             var star:Star=new Star(radius);
    26              
    27             bmpd1=new BitmapData(100,100,true,0);           
    28             bmpd1.draw(star,matrix);
    29             bmp1=new Bitmap(bmpd1);
    30             bmp1.x=200;
    31             bmp1.y=200;
    32             addChild(bmp1);
    33              
    34             bmpd2=new BitmapData(100,100,true,0);           
    35             bmpd2.draw(star,matrix);
    36             bmp2=new Bitmap(bmpd2);
    37             addChild(bmp2);
    38              
    39             stage.addEventListener(MouseEvent.MOUSE_MOVE,onMouseMoving);
    40         }
    41          
    42         private function onMouseMoving(event:MouseEvent):void {
    43              
    44             bmp2.x=mouseX-50;
    45             bmp2.y=mouseY-50;
    46              
    47             if (bmpd1.hitTest(new Point(bmp1.x,bmp1.y),255,bmpd2,new Point(bmp2.x,bmp2.y),255)) {
    48                 bmp1.filters=[new GlowFilter];
    49                 bmp2.filters=[new GlowFilter];
    50             } else {
    51                 bmp1.filters=[];
    52                 bmp2.filters=[];
    53             }
    54         }
    55     }
    56 }
    View Code

    这里我们用二个BitmapData“画”出二个星星,再进一步得到二个Bitmap,并加入舞台上。然后调用BitmapData的hitTest方法,检测二个星星之间的碰撞。

    注意这里的:if (bmpd1.hitTest(new Point(bmp1.x,bmp1.y),255,bmpd2,new Point(bmp2.x,bmp2.y),255)) {

    对于这二个星星而言,画到的地方便是完整不透明,没画到的空白地方即是完整透明(不存在类似渐变中的过渡情况),这里的二个255,代表检测时的alpha分量依据,通俗点讲:即只有完全不透明的地方碰到了,才返回true。

    为了对histTest方法中“alpha分量参数”有更好的理解,上面的示例可改进为下面这样:

     1 package {
     2     import flash.display.Bitmap;
     3     import flash.display.BitmapData;
     4     import flash.display.GradientType;
     5     import flash.display.Sprite;
     6     import flash.display.StageAlign;
     7     import flash.display.StageScaleMode;
     8     import flash.events.MouseEvent;
     9     import flash.filters.GlowFilter;
    10     import flash.geom.Matrix;
    11     import flash.geom.Point;
    12     import fl.events.SliderEvent;
    13      
    14     public class BitmapCollision2 extends Sprite {
    15         private var bmpd1:BitmapData;
    16         private var bmp1:Bitmap;
    17         private var bmpd2:BitmapData;
    18         private var bmp2:Bitmap;
    19         public function BitmapCollision2() {
    20             stage.align=StageAlign.TOP_LEFT;
    21             stage.scaleMode=StageScaleMode.NO_SCALE;
    22              
    23             var star:Star=new Star(50);
    24              
    25             var matrix:Matrix = new Matrix();
    26             matrix.createGradientBox(100, 100, 0, -50, -50);
    27              
    28             var circle:Sprite = new Sprite();
    29             //画一个渐变填充的圆
    30             circle.graphics.beginGradientFill(GradientType.RADIAL,[0, 0],[1, 0],[0, 255],matrix);
    31             circle.graphics.drawCircle(0, 0, 50);
    32             circle.graphics.endFill();
    33              
    34             bmpd1=new BitmapData(100,100,true,0);
    35             bmpd1.draw(star, new Matrix(1, 0, 0, 1, 50, 50));
    36             bmp1=new Bitmap(bmpd1);
    37             bmp1.x=stage.stageWidth/2 - bmp1.width/2;
    38             bmp1.y=stage.stageHeight/2 - bmp1.height/2;
    39             addChild(bmp1);
    40              
    41             bmpd2=new BitmapData(100,100,true,0);
    42             bmpd2.draw(circle, new Matrix(1, 0, 0, 1, 50, 50));
    43             bmp2=new Bitmap(bmpd2);
    44             addChild(bmp2);
    45             stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMoving);
    46              
    47             //slider1,slider2是舞台上用设计工具拖出来的二个滑动控件
    48             slider2.addEventListener(SliderEvent.THUMB_DRAG,slider2Change);
    49             slider1.addEventListener(SliderEvent.THUMB_DRAG,slider1Change);
    50              
    51         }
    52          
    53         private function slider1Change(e:SliderEvent):void
    54         {
    55             txt1.text = e.value.toString();
    56         }
    57          
    58         private function slider2Change(e:SliderEvent):void
    59         {
    60             txt2.text = e.value.toString();
    61         }
    62          
    63         private function onMouseMoving(event:MouseEvent):void {
    64              
    65              
    66             if (mouseY>320){return;}//防止小球拖到太下面,挡住了滑块
    67              
    68             bmp2.x=mouseX-50;
    69             bmp2.y=mouseY-50;
    70              
    71             if (bmpd1.hitTest(new Point(bmp1.x,bmp1.y),slider1.value,bmpd2,new Point(bmp2.x,bmp2.y),slider2.value)) {
    72                 bmp1.filters = [new GlowFilter()];
    73                 bmp2.filters = [new GlowFilter()];
    74             } else {
    75                 bmp1.filters=[];
    76                 bmp2.filters=[];
    77             }
    78         }
    79     }
    80 }
    View Code

    调整第二个滑块,然后再测试碰撞效果,体会alpha参数在其中的作用,值得一提的是:因为星星没有类似渐变的填充,要么透明,要么不透明,所以第一个滑块在1-255之间的值,对碰撞结果没有影响,除非设置为0才会有变化.(设置为0时,相当于把星星所对应的矩形边界当做整体在检测)

    通常在实际应用中,可能舞台上更多的是movieClip或sprite,而不是bitmap对象,如果您已经看懂了上面的二个示例,相信“对于MovieClip/Sprite之间的精确碰撞检测”也一定有思路了:构造对应的BitmapData,然后将movieclip或sprite,draw到bitmapData中,然后参考上面的代码处理。

    不过,这里有一个小技巧:因为我们最终需要的可能只是碰撞检测的结果,而并不是真的想要在舞台上显示Bitmap,所以在实际操作中,bitmapData甚至都不用加入到显示列表

     1 package {
     2     import flash.display.BitmapData;
     3     import flash.display.Sprite;
     4     import flash.display.StageAlign;
     5     import flash.display.StageScaleMode;
     6     import flash.events.MouseEvent;
     7     import flash.filters.GlowFilter;
     8     import flash.geom.Matrix;
     9     import flash.geom.Point;
    10     public class BitmapCollision3 extends Sprite {
    11         private var bmpd1:BitmapData;
    12         private var bmpd2:BitmapData;
    13         private var star1:Star;
    14         private var star2:Star;
    15         public function BitmapCollision3() {
    16             stage.align=StageAlign.TOP_LEFT;
    17             stage.scaleMode=StageScaleMode.NO_SCALE;            
    18             star1=new Star(50);
    19             addChild(star1);
    20             star2=new Star(50);
    21             star2.x=200;
    22             star2.y=200;
    23             addChild(star2);            
    24             bmpd1=new BitmapData(stage.stageWidth,stage.stageHeight,true,0);
    25             bmpd2=bmpd1.clone();
    26             stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMoving);
    27             //注:这里bmpd1,bmpd2都没被转成bitmap,更没有加入到舞台中.
    28         }
    29         private function onMouseMoving(event:MouseEvent):void {
    30              
    31             star1.x=mouseX;
    32             star1.y=mouseY;
    33             //先清空bitmapData中的数据,准备一个完全透明的黑"底板"。
    34             bmpd1.fillRect(bmpd1.rect, 0);
    35             bmpd2.fillRect(bmpd2.rect, 0);
    36             //再把要检测的(movieclip或sprite)对象,画到里面.
    37             bmpd1.draw(star1, new Matrix(1, 0, 0, 1, star1.x, star1.y));
    38             bmpd2.draw(star2, new Matrix(1, 0, 0, 1, star2.x, star2.y));
    39             //碰撞检测
    40             //注意:因为bmpd1,bmpd2都没被加入到舞台上,所以默认都在同样的0坐标位置,因此下面的坐标,直接用默认的Point对象实例即可.
    41             if (bmpd1.hitTest(new Point(), 255, bmpd2, new Point(), 255)) {
    42                 star1.filters = [new GlowFilter()];
    43                 star2.filters = [new GlowFilter()];
    44             } else {
    45                 star1.filters=[];
    46                 star2.filters=[];
    47             }
    48         }
    49     }
    50 }
    View Code

    最终的运行效果,跟之前的示例没有区别,就不重复贴出了

    继续,考虑更复杂的大量对象的碰撞问题,前一阵我们刚学习过“Flash/Flex学习笔记(41):碰撞检测”,但是没有考虑到大量对象时的性能问题。

    计算一下:10个物体处理碰撞时,每个物体都要与其它物体做碰撞检测,最终需要的处理次数为 10*9/2 = 45次(数学中的组合问题) ;如果100个物体,就要处理 100*99/2 = 4950次!

    这么大的计算量,每一帧都要处理一遍,AS3.0性能再强也是撑不住的!

    但实际上,我们静下心来想想:大量对象随机分布在舞台上,实际上每个对象只有可能与自身附近的对象发生碰撞,对于那边离自己很远,甚至八杆子打不着的对象,根本没必要跟他们做碰撞检测计算。所以,其实真正需要的计算量应该可以减少很多!

    网格碰撞检测

    如上图,首先可以先将舞台看成一个网格(每个单元格的大小,至少要大于舞台上尺寸最大的对象,即至少要能容纳下块头最大的一个对象)

    这样的话,每个对象都会被划分到对应的格子里,而且只有可能与“身处在同一个格子里的其它对象”以及“相临格子里的其它对象”发生碰撞。

    我们用遍历的思路(从左向右,从上到下)来分析一下:

    先从第一行第一列开始(如上图中的第一排第一个示例),黑色的表示当前要考虑的单元格,很明显:在行1列1的位置,可能与之发生碰撞只有相临的浅灰色单元格,其它白色单元格是不可能与它发生碰撞的。

    继续向右走,到了上图中第一排第二个小图的位置,这里能够与它发生单元格的只有其它4个浅灰色单元格(注:左侧的单元格在前面的检测中已经处理过了,所以这里就可以无视左侧相临的单元格!)

    同理,继续向右,直到第一行全部遍历完成。

    再继续向下,考查第二行:

    因为第一行已经全部处理过了,所以在考查第二行时,可以继续无视上面的单元格,同时再忽略左侧的单元格(道理与第一行相同)

    如此这般... 直到最后一行最后一列全部考查完毕。

    总结:从刚才的分析可以知道,不管在哪一行哪一列,最多只需要关注(包含自身的)5个单元格--自身、右侧、下侧、左下、右下。

    为了方便起见,我们还是用小球来做为基本对象,下面是Ball.cs的代码(相对以前的写法而言,更加OO了)

     1 package {
     2     import flash.display.Sprite;
     3     public class Ball extends Sprite {
     4         private var _color:uint;
     5         private var _radius:Number;
     6         private var _vx:Number=0;
     7         private var _vy:Number=0;
     8         public function Ball(radius:Number, color:uint = 0xffffff) {
     9             _radius=radius;
    10             _color=color;
    11             draw();
    12         }
    13         private function draw():void {
    14  
    15             graphics.clear();
    16             graphics.lineStyle(0);
    17             graphics.beginFill(_color, 1);
    18             graphics.drawCircle(0, 0, _radius);
    19             graphics.endFill();
    20             graphics.drawCircle(0, 0, 1);//在中心画一个点
    21         }
    22         public function update():void {
    23             x+=_vx;
    24             y+=_vy;
    25         }
    26         public function set color(value:uint):void {
    27             _color=value;
    28             draw();
    29         }
    30         public function get color():uint {
    31             return _color;
    32         }
    33         public function set radius(value:Number):void {
    34             _radius=value;
    35             draw();
    36         }
    37         public function get radius():Number {
    38             return _radius;
    39         }
    40         public function set vx(value:Number):void {
    41             _vx=value;
    42         }
    43         public function get vx():Number {
    44             return _vx;
    45         }
    46         public function set vy(value:Number):void {
    47             _vy=value;
    48         }
    49         public function get vy():Number {
    50             return _vy;
    51         }
    52     }
    53 }
    View Code

    ok,下面是完整的代码,请大家在仔细阅读/调试后,重点比较一下100个小球处理完毕所用的总次数。

      1 package {
      2     import flash.display.Sprite;
      3     import flash.display.StageAlign;
      4     import flash.display.StageScaleMode;
      5     import flash.events.MouseEvent;
      6     import flash.text.TextField;
      7      
      8     public class GridCollision extends Sprite {
      9          
     10         private const GRID_SIZE:Number=30;//单元格大小(这里设置为小于的直径,即正好容纳一个小球)
     11         private const RADIUS:Number=15;//小球的半径
     12         private var _balls:Array;
     13         private var _grid:Array;
     14         private var _numBalls:int=100;//小球数量
     15         private var _numChecks:int=0;//检测次数
     16         private var _txt:TextField = new TextField();
     17  
     18         public function GridCollision() {
     19             stage.align=StageAlign.TOP_LEFT;
     20             stage.scaleMode=StageScaleMode.NO_SCALE;
     21             makeBalls();//创建一堆小球
     22             makeGrid();//
     23             drawGrid();
     24             assignBallsToGrid();
     25             checkGrid();
     26              
     27             //显示计数器
     28             trace(_numChecks);
     29             addChild(_txt);
     30             _txt.background = true;
     31             _txt.backgroundColor=0xffff99;
     32             _txt.height = 20;
     33             _txt.width = 30;
     34             _txt.alpha = 0.7;
     35              
     36             stage.addEventListener(MouseEvent.MOUSE_DOWN,mouseDownClick);
     37         }
     38  
     39         private function mouseDownClick(e:MouseEvent):void{
     40             for (var i:int=0; i<_numBalls; i++) {
     41                 var ball:Ball=_balls[i];
     42                 ball.x=Math.random()*stage.stageWidth;
     43                 ball.y=Math.random()*stage.stageHeight; 
     44                 ball.color = 0xffffff;
     45             }
     46             _numChecks=0;
     47             makeGrid();//
     48             drawGrid();
     49             assignBallsToGrid();
     50             checkGrid();
     51              
     52         }
     53  
     54         //创建_numBalls个小球实例,并随机摆放到舞台上
     55         private function makeBalls():void {
     56             _balls=new Array  ;
     57             for (var i:int=0; i<_numBalls; i++) {
     58                 var ball:Ball=new Ball(RADIUS);
     59                 ball.x=Math.random()*stage.stageWidth;
     60                 ball.y=Math.random()*stage.stageHeight;
     61                 addChild(ball);
     62                 _balls.push(ball);
     63             }
     64         }
     65  
     66         private function makeGrid():void {
     67             _grid=new Array ;
     68  
     69             for (var i:int=0; i<stage.stageWidth/GRID_SIZE; i++) {//计算网格列数
     70                 _grid[i]=new Array  ;
     71  
     72                 for (var j:int=0; j<stage.stageHeight/GRID_SIZE; j++) {//计算网格行数
     73                     _grid[i][j]=new Array ;//每个单元格对应一个数组(用来存放该单元格中的小球)
     74                 }
     75             }
     76         }
     77  
     78         private function drawGrid():void {
     79             // 画出行列线
     80             graphics.lineStyle(0,.5);
     81             for (var i:int=0; i<=stage.stageWidth; i+=GRID_SIZE) {
     82                 graphics.moveTo(i,0);
     83                 graphics.lineTo(i,stage.stageHeight);
     84             }
     85             for (i=0; i<=stage.stageHeight; i+=GRID_SIZE) {
     86                 graphics.moveTo(0,i);
     87                 graphics.lineTo(stage.stageWidth,i);
     88             }
     89         }
     90  
     91  
     92         private function assignBallsToGrid():void {
     93             for (var i:int=0; i<_numBalls; i++) {
     94                 // 球的位置除以格子大小,得到该球所在网格的行列数
     95                 var ball:Ball=_balls[i] as Ball;
     96                 var xpos:int=Math.floor(ball.x/GRID_SIZE);
     97                 var ypos:int=Math.floor(ball.y/GRID_SIZE);
     98                 _grid[xpos][ypos].push(ball);//将小球推入对应单元格数组
     99             }
    100         }
    101  
    102         private function checkGrid():void {
    103             for (var i:int=0; i<_grid.length; i++) {
    104                 for (var j:int=0; j<_grid[i].length; j++) {
    105                      
    106                     checkOneCell(i,j);//单元格cell_self自身的碰撞检测
    107                     checkTwoCells(i,j,i+1,j);//单元格cell_self与单元格cell_right(右侧)的碰撞检测
    108                     checkTwoCells(i,j,i-1,j+1);//单元格cell_self与单元格cell_left_bottom(左下角)的碰撞检测
    109                     checkTwoCells(i,j,i,j+1);//单元格cell_self与单元格cell_bottom(下侧)的碰撞检测
    110                     checkTwoCells(i,j,i+1,j+1);//单元格cell_self与单元格cell_right_bottom(右下角)的碰撞检测
    111                 }
    112             }
    113         }
    114      
    115         //cellSelf与自身的检测
    116         private function checkOneCell(x:int,y:int):void {
    117             // 检测当前格子内所有的对象
    118             var cell:Array=_grid[x][y] as Array;
    119             for (var i:int=0; i<cell.length-1; i++) {
    120                 var ballA:Ball=cell[i] as Ball;
    121                 for (var j:int=i+1; j<cell.length; j++) {
    122                     var ballB:Ball=cell[j] as Ball;
    123                     checkCollision(ballA,ballB);
    124                 }
    125             }
    126         }
    127  
    128         //cellSelf与其它单元格的检测
    129         private function checkTwoCells(x1:int,y1:int,x2:int,y2:int):void {
    130             //确保要检测的格子存在
    131             if (x2<0) {
    132                 return;
    133             }
    134             if (x2>=_grid.length) {
    135                 return;
    136             }
    137             if (y2>=_grid[x2].length) {
    138                 return;
    139             }
    140             var cell0:Array=_grid[x1][y1] as Array;
    141             var cell1:Array=_grid[x2][y2] as Array;
    142              
    143             // 检测当前格子和邻接格子内所有的对象
    144             for (var i:int=0; i<cell0.length; i++) {
    145                 var ballA:Ball=cell0[i] as Ball;
    146                 for (var j:int=0; j<cell1.length; j++) {
    147                     var ballB:Ball=cell1[j] as Ball;
    148                     checkCollision(ballA,ballB);
    149                 }
    150             }
    151         }
    152  
    153         private function checkCollision(ballA:Ball,ballB:Ball):void {
    154             // 判断距离的碰撞检测
    155             _numChecks++;//计数器累加
    156             _txt.text = _numChecks.toString();
    157             var dx:Number=ballB.x-ballA.x;
    158             var dy:Number=ballB.y-ballA.y;
    159             var dist:Number=Math.sqrt(dx*dx+dy*dy);
    160             if (dist<ballA.radius+ballB.radius) {
    161                 //碰撞的小球变红色
    162                 ballA.color=0xff0000;
    163                 ballB.color=0xff0000;
    164             }
    165  
    166         }
    167     }
    168 }
    View Code


    在线演示

    上面的示例中,左上角的textField显示的是处理总次数(可以看到,大概在100-150次之间,这比优化前的理论值100*99/2 = 4950减少了90%都不止!)

    需要指出的是:计算次数具体能减少多少,取决于网络(单元格)大小、flash舞台(场景)大小、对象个数、对象的大小;改变其中一个或几个参数,上面的测试结果都将改变。

    再来认真的考虑一下性能问题,虽然用网格算法有效减少了计算次数,但是却多出了创建网格,把对象分配进单元格,遍历网络等操作,这些处理也同样要占用CPU资源,那么到底这些多余的操作影响多大?(或者也可能理解为在什么情况下,网络算法相对传统的(基于每两个对象之间的)两两检测更适用)

      1 package {
      2     import flash.display.Sprite;
      3     import flash.display.StageAlign;
      4     import flash.display.StageScaleMode;
      5     import flash.events.MouseEvent;
      6     import flash.text.TextField;
      7     import fl.controls.Slider;
      8     import flash.utils.getTimer;
      9     import fl.events.SliderEvent;
     10  
     11     public class GridCollision extends Sprite {
     12  
     13         private const GRID_SIZE:Number=20;//单元格大小(这里设置为小于的直径,即正好容纳一个小球)
     14         private const RADIUS:Number=10;//小球的半径
     15         private var _balls:Array;
     16         private var _grid:Array;
     17         private var _numBalls:int=50;//小球数量
     18         private var _txtGrid:TextField = new TextField();
     19         private var _txtBasic:TextField = new TextField();
     20         private var _slider:Slider = new Slider();
     21  
     22         public function GridCollision() {
     23             stage.align=StageAlign.TOP_LEFT;
     24             stage.scaleMode=StageScaleMode.NO_SCALE;
     25  
     26             makeGrid();
     27             drawGrid();
     28  
     29             addChild(_slider);
     30             addChild(_txtGrid);
     31             addChild(_txtBasic);
     32  
     33             test();
     34  
     35             _slider.addEventListener(SliderEvent.THUMB_DRAG,sliderGrag);
     36             stage.addEventListener(MouseEvent.CLICK,stageClick);
     37  
     38         }
     39  
     40         private function test(isClear:Boolean=false):void {
     41             var i:int=0;
     42             if (isClear) {
     43                 for (i=numChildren-1; i>=0; i--) {
     44                     removeChild(getChildAt(i));
     45                 }
     46                 _balls.length=0;
     47             }
     48             _txtGrid.background=_txtBasic.background=true;
     49             _txtGrid.backgroundColor=_txtBasic.backgroundColor=0xffff99;
     50             _txtBasic.height=_txtGrid.height=20;
     51             _txtBasic.width=_txtGrid.width=135;
     52             _txtBasic.alpha=_txtGrid.alpha=0.9;
     53             _txtBasic.x=stage.stageWidth-_txtBasic.width;
     54             _slider.maximum=300;
     55             _slider.minimum=30;
     56             _slider.snapInterval=10;
     57             _slider.y=10;
     58             _slider.value=_numBalls;
     59             _slider.width=200;
     60             _slider.x=stage.stageWidth/2-_slider.width/2;
     61  
     62             makeBalls();//创建一堆小球
     63  
     64             var startTime:int;
     65             var elapsed:int;
     66  
     67             startTime=getTimer();
     68             for (i=0; i<10; i++) {
     69                 makeGrid();
     70                 assignBallsToGrid();
     71                 checkGrid();
     72             }
     73             elapsed=getTimer()-startTime;
     74             trace("网格检测:",elapsed);
     75             _txtGrid.text=_numBalls+"个球网络检测:"+elapsed.toString();
     76  
     77             startTime=getTimer();
     78             for (i=0; i<10; i++) {
     79                 basicCheck();
     80             }
     81             elapsed=getTimer()-startTime;
     82             trace("两两检测:",elapsed);
     83             _txtBasic.text=_numBalls+"个球两两检测:"+elapsed.toString();
     84  
     85             if (isClear) {
     86                 addChild(_txtBasic);
     87                 addChild(_txtGrid);
     88                 addChild(_slider);
     89             }
     90         }
     91  
     92         private function sliderGrag(e:SliderEvent):void {
     93             _numBalls=e.value;
     94             trace("sliderGrag");
     95         }
     96  
     97         private function stageClick(e:MouseEvent):void {
     98             trace("stageClick");
     99             test(true);
    100         }
    101  
    102         //创建_numBalls个小球实例,并随机摆放到舞台上
    103         private function makeBalls():void {
    104             _balls=new Array  ;
    105             for (var i:int=0; i<_numBalls; i++) {
    106                 var ball:Ball=new Ball(RADIUS);
    107                 ball.x=Math.random()*stage.stageWidth;
    108                 ball.y=Math.random()*stage.stageHeight;
    109                 addChild(ball);
    110                 ball.alpha=0.5;
    111                 _balls.push(ball);
    112             }
    113         }
    114  
    115         private function makeGrid():void {
    116             _grid=new Array  ;
    117             for (var i:int=0; i<stage.stageWidth/GRID_SIZE; i++) {//计算网格列数
    118                 _grid[i]=new Array  ;
    119                 for (var j:int=0; j<stage.stageHeight/GRID_SIZE; j++) {//计算网格行数
    120                     _grid[i][j]=new Array  ;//每个单元格对应一个数组(用来存放该单元格中的小球)
    121                 }
    122             }
    123         }
    124  
    125         private function drawGrid():void {
    126             // 画出行列线
    127             graphics.lineStyle(0,0x999999);
    128             for (var i:int=0; i<=stage.stageWidth; i+=GRID_SIZE) {
    129                 graphics.moveTo(i,0);
    130                 graphics.lineTo(i,stage.stageHeight);
    131             }
    132             for (i=0; i<=stage.stageHeight; i+=GRID_SIZE) {
    133                 graphics.moveTo(0,i);
    134                 graphics.lineTo(stage.stageWidth,i);
    135             }
    136         }
    137  
    138  
    139         private function assignBallsToGrid():void {
    140             for (var i:int=0; i<_numBalls; i++) {
    141                 // 球的位置除以格子大小,得到该球所在网格的行列数
    142                 var ball:Ball=_balls[i] as Ball;
    143                 var xpos:int=Math.floor(ball.x/GRID_SIZE);
    144                 var ypos:int=Math.floor(ball.y/GRID_SIZE);
    145                 _grid[xpos][ypos].push(ball);//将小球推入对应单元格数组
    146             }
    147         }
    148  
    149         private function checkGrid():void {
    150             for (var i:int=0; i<_grid.length; i++) {
    151                 for (var j:int=0; j<_grid[i].length; j++) {
    152                     checkOneCell(i,j);//单元格cell_self自身的碰撞检测
    153                     checkTwoCells(i,j,i+1,j);//单元格cell_self与单元格cell_right(右侧)的碰撞检测
    154                     checkTwoCells(i,j,i-1,j+1);//单元格cell_self与单元格cell_left_bottom(左下角)的碰撞检测
    155                     checkTwoCells(i,j,i,j+1);//单元格cell_self与单元格cell_bottom(下侧)的碰撞检测
    156                     checkTwoCells(i,j,i+1,j+1);//单元格cell_self与单元格cell_right_bottom(右下角)的碰撞检测
    157                 }
    158             }
    159         }
    160  
    161         //cellSelf与自身的检测
    162         private function checkOneCell(x:int,y:int):void {
    163             // 检测当前格子内所有的对象
    164             var cell:Array=_grid[x][y] as Array;
    165             for (var i:int=0; i<cell.length-1; i++) {
    166                 var ballA:Ball=cell[i] as Ball;
    167                 for (var j:int=i+1; j<cell.length; j++) {
    168                     var ballB:Ball=cell[j] as Ball;
    169                     checkCollision(ballA,ballB);
    170                 }
    171             }
    172         }
    173  
    174         //cellSelf与其它单元格的检测
    175         private function checkTwoCells(x1:int,y1:int,x2:int,y2:int):void {
    176             //确保要检测的格子存在
    177             if (x2<0) {
    178                 return;
    179             }
    180             if (x2>=_grid.length) {
    181                 return;
    182             }
    183             if (y2>=_grid[x2].length) {
    184                 return;
    185             }
    186             var cell0:Array=_grid[x1][y1] as Array;
    187             var cell1:Array=_grid[x2][y2] as Array;
    188  
    189             // 检测当前格子和邻接格子内所有的对象
    190             for (var i:int=0; i<cell0.length; i++) {
    191                 var ballA:Ball=cell0[i] as Ball;
    192                 for (var j:int=0; j<cell1.length; j++) {
    193                     var ballB:Ball=cell1[j] as Ball;
    194                     checkCollision(ballA,ballB);
    195                 }
    196             }
    197         }
    198  
    199         private function checkCollision(ballA:Ball,ballB:Ball):void {
    200             // 判断距离的碰撞检测
    201             var dx:Number=ballB.x-ballA.x;
    202             var dy:Number=ballB.y-ballA.y;
    203             var dist:Number=Math.sqrt(dx*dx+dy*dy);
    204             if (dist<ballA.radius+ballB.radius) {
    205                 //碰撞的小球变红色
    206                 ballA.color=0xff0000;
    207                 ballB.color=0xff0000;
    208             }
    209         }
    210  
    211         //(最原始的)两两检测
    212         private function basicCheck():void {
    213             for (var i: int=0; i < _balls.length - 1; i++) {
    214                 var ballA:Ball=_balls[i] as Ball;
    215                 for (var j: int=i+1; j < _balls.length; j++) {
    216                     var ballB:Ball=_balls[j] as Ball;
    217                     checkCollision(ballA, ballB);
    218                 }
    219             }
    220         }
    221     }
    222 }
    View Code


    在线演示

    上面这个示例,我们把"网格检测算法"与传统的"两两检测算法"每个跑10次,然后输出所用的时间来进行比较,拖动滑块可以调整小球的数量,点击舞台可以重新计算。

    反复比较可以发现,在小球数量接近100时,二种算法性能已经相差无已,在小球数量大于100的前提下,小球数量越多,网格算法性能越有优势。在对象数量较少的情况下,传统的两两检测算法反而更快!

    所以网格算法仅适用于大量对象的碰撞检测!

    如果考虑到代码重用,可以把这种算法封装一下:

      1 package {
      2     import flash.display.DisplayObject;
      3     import flash.display.Graphics;
      4     import flash.events.EventDispatcher;
      5      
      6     public class CollisionGrid extends EventDispatcher {
      7          
      8         private var _checks:Vector.<DisplayObject>;//用于保存需要碰撞检测的对象(注:Vector.<T>相当于c#中的泛型数组)
      9         private var _grid:Vector.<Vector.<DisplayObject>>;//网格(注:这里用“一维数组套一维数组”的方法替代了原来的二维数组)
     10         private var _gridSize:Number;
     11         private var _height:Number;
     12         private var _numCells:int;
     13         private var _numCols:int;
     14         private var _numRows:int;
     15         private var _Number;
     16          
     17         public function CollisionGrid(Number, height:Number, gridSize:Number) {
     18             _width=width;
     19             _height=height;
     20             _gridSize=gridSize;
     21              
     22             _numCols=Math.ceil(_width/_gridSize);//计算总列数            
     23             _numRows=Math.ceil(_height/_gridSize);//计算总行数
     24             _numCells=_numCols*_numRows;//单元格总数
     25         }
     26          
     27         //画格子
     28         public function drawGrid(graphics:Graphics):void {
     29             graphics.lineStyle(0, .5);
     30             for (var i:int = 0; i <= _width; i += _gridSize) {
     31                 graphics.moveTo(i, 0);
     32                 graphics.lineTo(i, _height);
     33             }
     34             for (i = 0; i <= _height; i += _gridSize) {
     35                 graphics.moveTo(0, i);
     36                 graphics.lineTo(_width, i);
     37             }
     38         }
     39          
     40         //将需要检测的对象(泛型)数组objects分配到网络
     41         public function assign(objects:Vector.<DisplayObject>):void {
     42             var numObjects:int=objects.length;
     43             _grid=new Vector.<Vector.<DisplayObject>>(_numCells);
     44             _checks = new Vector.<DisplayObject>();
     45             for (var i:int = 0; i < numObjects; i++) {
     46                 var obj:DisplayObject=objects[i];
     47                 //注意:这里用“Grid.[索引]”(定位)的方式,替换了原来的“Grid.[列][行]”(单元格的定位)方式--回想一下bitmap位图中的像素索引就更容易理解了
     48                 var index:int=Math.floor(obj.y/_gridSize)*_numCols+Math.floor(obj.x/_gridSize);
     49                 //“单元格”--延时实例化"
     50                 if (_grid[index]==null) {
     51                     _grid[index]=new Vector.<DisplayObject>  ;
     52                 }
     53                 //将对象推入"单元格"
     54                 _grid[index].push(obj);
     55             }
     56              
     57             //检测需要碰撞的对象,并保存到_checks数组
     58             checkGrid();
     59         }
     60          
     61         //"单元格"检测
     62         private function checkGrid():void {
     63             for (var i:int = 0; i < _numCols; i++) {
     64                 for (var j:int = 0; j < _numRows; j++) {
     65                     checkOneCell(i, j);
     66                     checkTwoCells(i, j, i + 1, j);
     67                     checkTwoCells(i, j, i - 1, j + 1);
     68                     checkTwoCells(i, j, i, j + 1);
     69                     checkTwoCells(i, j, i + 1, j + 1);
     70                 }
     71             }
     72         }
     73          
     74         //(自身)单个单元格的检测
     75         private function checkOneCell(x:int, y:int):void {
     76             var cell:Vector.<DisplayObject>=_grid[y*_numCols+x];
     77             if (cell==null) {
     78                 return;
     79             }
     80             var cellLength:int=cell.length;
     81              
     82             for (var i:int = 0; i < cellLength - 1; i++) {
     83                 var objA:DisplayObject=cell[i];
     84                 for (var j:int = i + 1; j < cellLength; j++) {
     85                     var objB:DisplayObject=cell[j];
     86                     _checks.push(objA, objB);
     87                 }
     88             }
     89         }
     90          
     91         //单元格(x1,y1)与单元格(x2,y2)的检测
     92         private function checkTwoCells(x1:int, y1:int, x2:int, y2:int):void {
     93             if (x2>=_numCols||x2<0||y2>=_numRows) {
     94                 return;
     95             }
     96             var cellA:Vector.<DisplayObject>=_grid[y1*_numCols+x1];
     97             var cellB:Vector.<DisplayObject>=_grid[y2*_numCols+x2];
     98             if (cellA==null||cellB==null) {
     99                 return;
    100             }
    101             var cellALength:int=cellA.length;
    102             var cellBLength:int=cellB.length;
    103             for (var i:int = 0; i < cellALength; i++) {
    104                 var objA:DisplayObject=cellA[i];
    105                 for (var j:int = 0; j < cellBLength; j++) {
    106                     var objB:DisplayObject=cellB[j];
    107                     _checks.push(objA, objB);
    108                 }
    109             }
    110         }
    111          
    112         public function get checks():Vector.<DisplayObject> {
    113             return _checks;
    114         }
    115     }
    116 }
    View Code

    注:除了单纯的封装以外,上面的代码还有三个重要的优化措施

    1.用Vector(泛型数组)代替了Array数组

    2.用一维数组嵌套取代了原来的二维数组

    3.延时实例化避免了创建无用的"单元格"

    用封装并优化后的代码重新测试下:

     1 package {
     2     import flash.display.Sprite;
     3     import flash.display.StageAlign;
     4     import flash.display.StageScaleMode;
     5     import flash.utils.getTimer;
     6     import flash.display.DisplayObject;
     7     import flash.events.MouseEvent;
     8     import flash.text.TextField;
     9  
    10     public class GridCollision2 extends Sprite {
    11         private const GRID_SIZE:Number=20;
    12         private const RADIUS:Number=10;
    13         private var _balls:Vector.<DisplayObject>;//这里用Vector代替了Array
    14         private var _grid:CollisionGrid;
    15         private var _numBalls:int=50;
    16         private var _text:TextField;
    17  
    18         public function GridCollision2() {
    19             stage.align=StageAlign.TOP_LEFT;
    20             stage.scaleMode=StageScaleMode.NO_SCALE;
    21             _text = new TextField();
    22             _text.background = true;
    23             _text.backgroundColor = 0xffff99;
    24             _text.width = 135;
    25             _text.height = 20;
    26             _text.alpha = 0.9;
    27              
    28              
    29             _grid=new CollisionGrid(stage.stageWidth,stage.stageHeight,GRID_SIZE);
    30             _grid.drawGrid(graphics);
    31  
    32             makeBalls();
    33             addChild(_text);
    34             test();
    35  
    36             stage.addEventListener(MouseEvent.CLICK,stageClick);
    37         }
    38  
    39         private function stageClick(e:MouseEvent):void {
    40             test(true);
    41         }
    42  
    43         private function test(isRestart:Boolean=false):void {
    44             if (isRestart) {
    45                 for (var i:int=0; i<_numBalls; i++) {
    46                     var ball:Ball=_balls[i] as Ball;
    47                     ball.x=Math.random()*stage.stageWidth;
    48                     ball.y=Math.random()*stage.stageHeight;
    49                     ball.color = 0xffffff;
    50                 }
    51             }
    52  
    53             var startTime:int;
    54             var elapsed:int;
    55             startTime=getTimer();
    56             for (i=0; i<10; i++) {
    57                 _grid.assign(_balls);//将所有需要检测的ball放入_grid.checks
    58                 var numChecks:int=_grid.checks.length;
    59                 for (var j:int=0; j<numChecks; j+=2) {
    60                     checkCollision(_grid.checks[j] as Ball,_grid.checks[j+1] as Ball);
    61                 }
    62             }
    63             elapsed=getTimer()-startTime;
    64             trace("Elapsed:",elapsed);
    65             _text.text = _numBalls + "个小球碰撞检测:" + elapsed.toString();
    66         }
    67          
    68         //初始化小球实例
    69         private function makeBalls():void {
    70             _balls=new Vector.<DisplayObject>(_numBalls);
    71             for (var i:int=0; i<_numBalls; i++) {
    72                 var ball:Ball=new Ball(RADIUS);
    73                 ball.x=Math.random()*stage.stageWidth;
    74                 ball.y=Math.random()*stage.stageHeight;
    75                 ball.alpha = 0.8;
    76                 addChild(ball);
    77                 _balls[i]=ball;
    78             }
    79         }
    80  
    81         //检测碰撞
    82         private function checkCollision(ballA:Ball,ballB:Ball):void {
    83             var dx:Number=ballB.x-ballA.x;
    84             var dy:Number=ballB.y-ballA.y;
    85             var dist:Number=Math.sqrt(dx*dx+dy*dy);
    86             if (dist<ballA.radius+ballB.radius) {
    87                 //(碰撞后的小球变红色)
    88                 ballA.color=0xff0000;
    89                 ballB.color=0xff0000;
    90             }
    91         }
    92     }
    93 }
    View Code


    在线演示

    对比之前未封装的示例,可以发现:执行时间缩短了近一半!说明优化的效果还是很不错的。

    静态的碰撞检测可能比较没劲,可以再结合以前学到的知识,让小球动起来。

     1 package {
     2     import flash.display.Sprite;
     3     import flash.display.StageAlign;
     4     import flash.display.StageScaleMode;
     5     import flash.display.DisplayObject;
     6     import flash.events.Event;
     7      
     8     public class GridCollision3 extends Sprite {
     9         private const GRID_SIZE:Number=20;
    10         private const RADIUS:Number=10;
    11         private var _balls:Vector.<DisplayObject>;
    12         private var _grid:CollisionGrid;
    13         private var _numBalls:int=100;
    14         public function GridCollision3() {
    15             stage.align=StageAlign.TOP_LEFT;
    16             stage.scaleMode=StageScaleMode.NO_SCALE;
    17             _grid=new CollisionGrid(stage.stageWidth,stage.stageHeight,GRID_SIZE);
    18             _grid.drawGrid(graphics);
    19             makeBalls();
    20             addEventListener(Event.ENTER_FRAME, onEnterFrame);
    21         }
    22         function onEnterFrame(event:Event):void {
    23             updateBalls();
    24             _grid.assign(_balls);
    25             var numChecks:int=_grid.checks.length;
    26             for (var j:int = 0; j < numChecks; j += 2) {
    27                 checkCollision(_grid.checks[j] as Ball, _grid.checks[j + 1] as Ball);
    28             }
    29         }
    30         private function makeBalls():void {
    31             _balls=new Vector.<DisplayObject>(_numBalls);
    32             for (var i:int = 0; i < _numBalls; i++) {
    33                 var ball:Ball=new Ball(RADIUS);
    34                 ball.x=Math.random()*stage.stageWidth;
    35                 ball.y=Math.random()*stage.stageHeight;
    36                 ball.vx=Math.random()*4-2;
    37                 ball.vy=Math.random()*4-2;
    38                 addChild(ball);
    39                 _balls[i]=ball;
    40             }
    41         }
    42         private function updateBalls():void {
    43             for (var i:int = 0; i < _numBalls; i++) {
    44                  
    45                 var ball:Ball=_balls[i] as Ball;
    46                 ball.update();
    47                 if (ball.x<RADIUS) {
    48                     ball.x=RADIUS;
    49                     ball.vx*=-1;
    50                 } else if (ball.x > stage.stageWidth - RADIUS) {
    51                     ball.x=stage.stageWidth-RADIUS;
    52                     ball.vx*=-1;
    53                 }
    54                 if (ball.y<RADIUS) {
    55                     ball.y=RADIUS;
    56                     ball.vy*=-1;
    57                 } else if (ball.y > stage.stageHeight - RADIUS) {
    58                     ball.y=stage.stageHeight-RADIUS;
    59                     ball.vy*=-1;
    60                 }
    61                 ball.color=0xffffff;
    62             }
    63         }
    64         private function checkCollision(ballA:Ball, ballB:Ball):void {
    65             var dx:Number=ballB.x-ballA.x;
    66             var dy:Number=ballB.y-ballA.y;
    67             var dist:Number=Math.sqrt(dx*dx+dy*dy);
    68             if (dist<ballA.radius+ballB.radius) {
    69                 ballA.color=0xff0000;
    70                 ballB.color=0xff0000;
    71             }
    72         }
    73     }
    74 }
    View Code


    在线演示

    当然这种网格算法不仅仅只能用于上面提供的"实打实"的碰撞,其中只要是基于距离的对象检测,它都适用。

    回顾一下以前做过的节点花园 (http://www.cnblogs.com/yjmyzz/archive/2010/04/28/1723003.html)示例,当时因为粒子数量比较少,还看不出有什么性能问题,让我们把粒子数量弄得多一点,比如500,再来测试下:

     1 package {
     2     import flash.display.Sprite;
     3     import flash.display.StageScaleMode;
     4     import flash.display.StageAlign;
     5     import flash.events.Event;
     6     import flash.geom.Point;
     7      
     8     [SWF(backgroundColor=0x000000,width="600",height="600",frameRate=100)]
     9     public class NodeGardenLines extends Sprite {
    10         private var particles:Array;
    11         private var numParticles:uint=300;
    12         private var minDist:Number=50;
    13         private var springAmount:Number=.001;
    14         public function NodeGardenLines() {
    15             init();
    16         }
    17         private function init():void {
    18             stage.scaleMode=StageScaleMode.NO_SCALE;
    19             stage.align=StageAlign.TOP_LEFT;
    20             particles = new Array();
    21             for (var i:uint = 0; i < numParticles; i++) {
    22                 var particle:Ball=new Ball(2,0x00ff00,false);
    23                 particle.x=Math.random()*stage.stageWidth;
    24                 particle.y=Math.random()*stage.stageHeight;
    25                 particle.vx=Math.random()*6-3;
    26                 particle.vy=Math.random()*6-3;
    27                 addChild(particle);
    28                 particles.push(particle);
    29             }
    30             addEventListener(Event.ENTER_FRAME, onEnterFrame);
    31              
    32             var fps:FPSshow = new FPSshow();
    33             addChild(fps);
    34         }
    35         private function onEnterFrame(event:Event):void {
    36             graphics.clear();
    37             for (var i:uint = 0; i < numParticles; i++) {
    38                 var particle:Ball=particles[i];
    39                 particle.x+=particle.vx;
    40                 particle.y+=particle.vy;
    41                 if (particle.x>stage.stageWidth) {
    42  
    43                     particle.x=0;
    44                 } else if (particle.x < 0) {
    45                     particle.x=stage.stageWidth;
    46                 }
    47                 if (particle.y>stage.stageHeight) {
    48                     particle.y=0;
    49                 } else if (particle.y < 0) {
    50                     particle.y=stage.stageHeight;
    51                 }
    52             }
    53             for (i=0; i < numParticles - 1; i++) {
    54                 var partA:Ball=particles[i];
    55                 for (var j:uint = i + 1; j < numParticles; j++) {
    56                     var partB:Ball=particles[j];
    57                     spring(partA, partB);
    58                 }
    59             }
    60         }
    61         private function spring(partA:Ball, partB:Ball):void {
    62             var dx:Number=partB.x-partA.x;
    63             var dy:Number=partB.y-partA.y;
    64             var dist:Number=Math.sqrt(dx*dx+dy*dy);
    65             if (dist<minDist) {
    66                 graphics.lineStyle(1, 0xffffff, 1 - dist / minDist);
    67                 graphics.moveTo(partA.x, partA.y);
    68                 graphics.lineTo(partB.x, partB.y);
    69                 var ax:Number=dx*springAmount;
    70                 var ay:Number=dy*springAmount;
    71                 partA.vx+=ax;
    72                 partA.vy+=ay;
    73                 partB.vx-=ax;
    74                 partB.vy-=ay;
    75             }
    76         }
    77     }
    78 }
    View Code


    在线演示

    留意一下现在的帧数,下面是采用网格算法后的代码:

     1 package {
     2     import flash.display.DisplayObject;
     3     import flash.display.Sprite;
     4     import flash.display.StageScaleMode;
     5     import flash.display.StageAlign;
     6     import flash.events.Event;
     7     import flash.geom.Point;
     8      
     9     [SWF(backgroundColor=0x000000,width="600",height="600",frameRate=100)]
    10     public class NodeGardenGrid extends Sprite {
    11         private var particles:Vector.<DisplayObject>;
    12         private var numParticles:uint=300;
    13         private var minDist:Number=50;
    14         private var springAmount:Number=.001;
    15         private var grid:CollisionGrid;
    16          
    17         public function NodeGardenGrid() {
    18             init();
    19         }
    20          
    21         private function init():void {
    22             stage.scaleMode=StageScaleMode.NO_SCALE;
    23             stage.align=StageAlign.TOP_LEFT;
    24             grid=new CollisionGrid(stage.stageWidth,stage.stageHeight,52);
    25             particles = new Vector.<DisplayObject>();
    26             for (var i:uint = 0; i < numParticles; i++) {
    27                 var particle:Ball=new Ball(2,0x00ff00,false);
    28                 particle.x=Math.random()*stage.stageWidth;
    29                 particle.y=Math.random()*stage.stageHeight;
    30                 particle.vx=Math.random()*6-3;
    31                 particle.vy=Math.random()*6-3;
    32                 addChild(particle);
    33                 particles.push(particle);
    34             }
    35             addEventListener(Event.ENTER_FRAME, onEnterFrame);
    36  
    37             var fps:FPSshow = new FPSshow();
    38             addChild(fps);
    39         }
    40          
    41         private function onEnterFrame(event:Event):void {
    42             graphics.clear();
    43             for (var i:uint = 0; i < numParticles; i++) {
    44                 var particle:Ball=particles[i] as Ball;
    45                 particle.x+=particle.vx;
    46                 particle.y+=particle.vy;
    47                 if (particle.x>stage.stageWidth) {
    48                     particle.x=0;
    49                 } else if (particle.x < 0) {
    50                     particle.x=stage.stageWidth;
    51                 }
    52                 if (particle.y>stage.stageHeight) {
    53                     particle.y=0;
    54                 } else if (particle.y < 0) {
    55                     particle.y=stage.stageHeight;
    56                 }
    57             }
    58             grid.assign(particles);
    59             var checks:Vector.<DisplayObject>=grid.checks;
    60             trace(checks.length);
    61             var numChecks:int=checks.length;
    62             for (i=0; i < numChecks; i += 2) {
    63                 var partA:Ball=checks[i] as Ball;
    64                 var partB:Ball=checks[i+1] as Ball;
    65                 spring(partA, partB);
    66             }
    67         }
    68          
    69         private function spring(partA:Ball, partB:Ball):void {
    70             var dx:Number=partB.x-partA.x;
    71             var dy:Number=partB.y-partA.y;
    72             var dist:Number=Math.sqrt(dx*dx+dy*dy);
    73             if (dist<minDist) {
    74                 graphics.lineStyle(1, 0x00ff00, 1 - dist / minDist);
    75                 graphics.moveTo(partA.x, partA.y);
    76                 graphics.lineTo(partB.x, partB.y);
    77                 var ax:Number=dx*springAmount;
    78                 var ay:Number=dy*springAmount;
    79                 partA.vx+=ax;
    80                 partA.vy+=ay;
    81                 partB.vx-=ax;
    82                 partB.vy-=ay;
    83             }
    84         }
    85     }
    86 }
    View Code


    在线演示

    如果用IE的朋友,貌似弹出窗口加载flash有些问题(偶尔会引发异常),建议用firefox或chrome浏览器浏览本文。

    在firefox下加速效果最为明显,比较意外的是在chrome下居然二种算法帧数相差无已。(极度怀疑google与adobe协力对chrome浏览器上的flash插件做了极大的优化)

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com 
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
     
  • 相关阅读:
    在 Flink 算子中使用多线程如何保证不丢数据?
    日处理数据量超10亿:友信金服基于Flink构建实时用户画像系统的实践
    Java编码技巧之高效代码50例
    codeforces 1284D. New Year and Conference(线段树)
    codeforces 1284C. New Year and Permutation(组合数学)
    codeforces 1284B. New Year and Ascent Sequence(二分)
    Codeforces Hello2020 A-E简要题解
    POJ2456 Aggressive cows(二分)
    POJ3122 Pie(二分)
    POJ3258 River Hopscotch(二分最大化最小值)
  • 原文地址:https://www.cnblogs.com/buerjiongjiong/p/4731099.html
Copyright © 2011-2022 走看看