碰撞处理这是个很大的话题,要研究的东西太多了,就我所知,就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,当有碰撞发生时用来绘制线条。效果如下: