flash中物体运动基础之七---------碰撞处理

碰撞处理这是个很大的话题,要研究的东西太多了,就我所知,就as3而言它提供了三种方法来检测碰撞,它们是:

hitTestPoint,hitTestObject,hitTest

前两个方法是应用于显示对象上的,后面一个用于位图像素级的碰撞检测,具体使用方法帮助文档中有详细介绍的。

关于位图的碰撞检测和更高级的碰撞检测在《Flash Actionscript 3.0 动画高级教程》中有详细介绍,有兴趣的可以看一下,这里只讨论规则形状的hitTestPoint方法。

启动FlashDevelop,新建一个FlashIDE工程,新建一个Fla文件,创建一个MC,里面画上两根线条,线条要粗一些(原因稍后),将新建的MC拖入舞台,定义一个实例名称_roadBlock;

再新建一个Ball类,定义vx,vy分别表示Ball的x,y方向的速度,再在Fla文件的库中新建一个Ball类的实例。

新建一个Main类,作为Fla的文档类

 

一。简单的碰撞检测

简单碰撞检测,这里只是调用了hitTestPoint方法,主要代码如下:

   _ball.vy += GRAVITY;
   if (_roadBlock.hitTestPoint(_ball.x,_ball.y,true))
   {
    _ball.vy = 0;
   }
   _ball.y += _ball.vy;

在EnterFrame事件中检测_roadBlock是否碰到_ball,_ball的注册点在其几何中心,这样检测时在球体速度为0之前实际上球体早已碰到了线条。如上。

 

二。复杂一点的检测,精确一点的检测

上面是检测的球体的中心不能满足需求,而现在要检测球体边缘的点。效果如下:

要检测圆周上的点与障碍物的碰撞,先要取得圆周上的点,现在一直圆的半径和圆的坐标,则能取得圆周上的点。

半径:r = _ball.width/2;

圆周上的点:px=r*Math.cos(angle);py=r*Math.sin(angle);

angle取值范围为0~2*Math.PI,当angle为0时,px=r,py=0;当angle=90时,px=0,py=r;......

现在的问题是,究竟取圆周上的哪些点?

实际上要取所有的点,关键是取点的精度,即angle的增量,设dt为angle增量:

设dt=90,那么每一个EnterFrame需要做碰撞检测的只有圆周上的四个点,四个点中任一个碰到障碍物,即碰撞发生,设球体速度为0。

设dt=30,那么每一个EnterFrame需要做碰撞检测的就有360/30=12个点,12个点种任意一个碰到障碍物,即碰撞发生,比前面检测的点更多了。

设dt=1,那么每一个EnterFrame需要做碰撞检测的就有360个点。。。。。。

测试中,虽然360个点,但对于准确的检测还是很大误差,那将dt设为更小如:0.001,0.00001那么这样点数为36000,3600000或更大,但dt越小会导致检测一周的点的时间变长,导致占用的cup时间会更多,要取一个适当的数才行。

 

   //每一个EnterFrame中要检测碰撞的次数

   private var precision:Number = 20;

 

   _ball.vy += GRAVITY;
   for (var i:int = 0; i < precision;i++ )
   {
    var x:Number = _ball.x+radius * Math.cos(360*i/precision);
    var y:Number = _ball.y+radius * Math.sin(360*i/precision);
    
    if (_roadBlock.hitTestPoint(x,y,true))
    {
     _ball.vy = 0;
    }
   }
   _ball.y += _ball.vy;

同样是上面的代码,现在将障碍物中水平的那根线去掉,然后再检测会有不准确了,如下:

这个原因何在?或许还得寻找更加准确的碰撞检测方法。

 

三。检测碰撞的次数

在每一次EnterFrame中调用下面的代码,这样能显示每一次EnterFrame发生时有多少个点被检测到已经碰撞了。调整precision的值,_count的值会变化。

 _count = 0;
   _ball.vy += GRAVITY;
   for (var i:int = 0; i < precision;i++ )
   {
    var x:Number = _ball.x+radius * Math.cos(360*i/precision);
    var y:Number = _ball.y+radius * Math.sin(360*i/precision);
    
    if (_roadBlock.hitTestPoint(x,y,true))
    {
     _ball.vy = 0;
     _count++;
    }
   }
   textField.text = "" + _count;
   _ball.y += _ball.vy;
   _ball.x += _ball.vx;

为了更直观的看到碰撞的点,现在在每次碰撞时,根据所碰撞的点求出一个平均位置,来绘制一条线条,代码如下:

   _count = 0;
   var sumX:Number = 0;
   var sumY:Number = 0;
   _ball.vy += GRAVITY;
   for (var i:int = 0; i < precision;i++ )
   {
    var x:Number = _ball.x+radius * Math.cos(360*i/precision);
    var y:Number = _ball.y+radius * Math.sin(360*i/precision);
    
    if (_roadBlock.hitTestPoint(x,y,true))
    {
     _ball.vy = 0;
     sumX += x;
     sumY += y;
     _count++;
    }
    
   }
   if (_count>0)
   {
    x = sumX / _count;
    y = sumY / _count;
    lineMC.graphics.clear();
    lineMC.graphics.lineStyle(1);
    lineMC.graphics.moveTo(x, y);
    lineMC.graphics.lineTo(_ball.x, _ball.y);
   }
   textField.text = "" + _count;
   _ball.y += _ball.vy;
   _ball.x += _ball.vx;

lineMC是Main类中新定义的一个MC,当有碰撞发生时用来绘制线条。效果如下:

posted @ 2010-07-31 13:43  ywxgod  阅读(1068)  评论(0编辑  收藏  举报