《making things move 》第九章

《making things move 》第九章
目前情况来看,比起研究RIA~~研究物理这些东西没什么钱途了,

不过我还是很喜欢这些东西的,希望有朝一日可以写出自己的物理引擎 [cool],今天内容:

碰撞检测:

1. 自带的 hittest

做过游戏的都应该比较熟,不多说

有两种用法:

mc.hitTest(mc) mc碰mc ,只能检测mc 外框碰撞,对不规则图形没办法

mc.hitTest(x,y,shapeflag) mc碰点 ,把shapeflag设置为true,可以检测不规则图形

缺陷:不规则形状的话,只能检测 mc和point碰撞

2. 距离法碰撞检测

适用:圆形mc

两球距离小于半径之和就碰上了~

attachments/200611/08_164634_1.gif

mc1.startDrag(true);
onEnterFrame = function()
{
 var dx:Number = mc2._x - mc1._x;
 var dy:Number = mc2._y - mc1._y;
 var dist:Number = Math.sqrt(dx * dx + dy * dy);
 if(dist < mc1._width / 2 + mc2._width / 2)
 {
 trace("hit");
 }
}

3. 碰撞 + spring

Media 点这里 显示/隐藏 媒体


不难,代码加注释了
//边框初始化
var left:Number = 0;
var right:Number = Stage.width;
var top:Number = 0;
var bottom:Number = Stage.height;
var centerBall:MovieClip;
var numBalls:Number = 10;
init();

function init()
{

// 加载小球,随即速度
 for (var i = 0; i<numBalls; i++)
 {
 var ball:MovieClip = attachMovie("Ball", "ball" + i, i);
 ball._x = Math.random() * Stage.width;
 ball._y = Math.random() * Stage.height;
 ball._width = ball._height = Math.random() * 40 + 20;
 ball.vx = Math.random() * 4 - 2;
 ball.vy = Math.random() * 4 - 2;
 }
//大球,不动
 centerBall = attachMovie("Ball", "centerball", i);
 centerBall._width = centerBall._height = 150;
 centerBall._x = Stage.width / 2;
 centerBall._y = Stage.height / 2;
}

function onEnterFrame(Void):Void
{
//所有小球移动
 for (var i = 0; i<numBalls; i++)
{
 var ball:MovieClip = this["ball" + i];
 move(ball);
 }
}
function move(ball:MovieClip)
{
 ball._x += ball.vx;
 ball._y += ball.vy;
//碰边就反弹
 if (ball._x + ball._width / 2 > right)
 {
 ball._x = right-ball._width / 2;
 ball.vx *= -1;
 }
 else if (ball._x - ball._width / 2 < left)
 {
 ball._x = left+ball._width / 2;
 ball.vx *= -1;
 }
 if (ball._y + ball._width / 2 > bottom)
 {
 ball._y = bottom-ball._width / 2;
 ball.vy *= -1;
 }
 else if (ball._y - ball._width / 2 < top)
 {
 ball._y = top+ball._width / 2;
 ball.vy *= -1;
 }
 var dx:Number = ball._x - centerBall._x;
 var dy:Number = ball._y - centerBall._y;
 var dist:Number = Math.sqrt(dx * dx + dy * dy);
 var minDist = ball._width/2+centerBall._width/2;

//如果碰撞
 if (dist < minDist)
 {
 var angle:Number = Math.atan2(dy, dx);
 //得到小球圆心应该在的位置
 var targetX:Number = centerBall._x +
 Math.cos(angle) * minDist;
 var targetY:Number = centerBall._y +
 Math.sin(angle) * minDist;
 //向应该的圆心目标给个加速度
 ball.vx += (targetX-ball._x) * .1;
 ball.vy += (targetY-ball._y) * .1;
 }
}

4. 今天重点了:多对象碰撞

假设有6个小球,分别叫 mc0, mc1, mc2, mc3, mc4, and mc5.
numClips = 6;
for(i=0; i<numClips; i++)
{
 clipA = this["mc" + i];
 for(j=0; j<numClips; j++)
 {
 clipB = this["mc" + j];
 if(clipA.hitTest(clipB))
 {
 // do whatever
 }
 }
}

可以看到6个mc要36次尝试(两个for,6*6=36). 看起来很合理 ? 哦, 这代码有两个很大的问题!

首先,看看第一次循环,变量 i 和 j 将同时等于 0 。所以clipA 为 mc0的引用 ,跟clipB 一样,hey!,你在尝试自己碰自己!那是不可能的。你可以先确定当clipA != clipB 再hitTest 或者简单的 i != j 。然后代码变成这样
numClips = 6;
for(i=0; i<numClips; i++)
{
 clipA = this["mc" + i];
 for(j=0; j<numClips; j++)
 {
 clipB = this["mc" + j];
 if(i != j && clipA.hitTest(clipB))
 {
 // do whatever
 }
 }
}

好了,你减少了6次尝试,现在是30次了,但这还是太多了,让我们看看图表,你到底尝试多少次:

mc0 和

mc1, mc2, mc3, mc4, mc5

mc1 和

mc0, mc2, mc3, mc4, mc5

mc2 和

mc0, mc1, mc3, mc4, mc5

mc3 和

mc0, mc1, mc2, mc4, mc5

mc4 和

mc0, mc1, mc2, mc3, mc5

mc5 和

mc0, mc1, mc2, mc3, mc4

先看第一次 mc0和mc1 。再看第2轮循环第一次 mc1和mc0。 ??两次是相同的? ,如果mc0没碰到mc1,当然mc1就没碰到mc0,相反也一样。
仔细看看有很多次类似这样的相同尝试,如果删掉所有相同的,那么就会得到下边的表

mc0 with mc1, mc2, mc3, mc4, mc5
mc1 with mc2, mc3, mc4, mc5
mc2 with mc3, mc4, mc5
mc3 with mc4, mc5
mc4 with mc5
mc5 with nothing!

你看到了,第一次循环mc0跟每个其他影片尝试一下。所以以后就没有mc需要跟mc0再次尝试了。把mc0从列表中删除,然后mc1跟所有剩下的尝试一下,接着把他也从列表中删除,直到尝试到mc5,所有的mc都跟mc5尝试过,所以不需要再尝试了。

现在代码变成什么样了? 看看:
numClips=6
for(i=0; i<numClips-1; i++)
{
 clipA = this["mc" + i];
 for(j=i+1; j<numClips; j++)
 {
 clipB = this["mc" + j];
 if(clipA.hitTest(clipB))
 {
 // do whatever
 }
 }
}

解释就不翻译了:

Notice that the first, outer for loop now goes to one less than the total number of clips. As you just saw in the final comparison chart, you don’t need to test the last clip against anything, as it’s already been thoroughly tested.

In the inner loop, you always start with one higher than the index of the outer loop. This is because you’ve already tested everything lower, and you don’t want to test the same index, which, as you saw, would be testing a clip against itself. So, this even lets you get rid of that check. The result is just a few characters different from the original code, but gives you a 100% performance increase!

Also, even beyond its performance impact, in many cases, doing double hit testing might have unwanted results. If you’re changing the velocity or some other value when you detect a collision, you may wind up changing it twice, resulting in who knows what kind of effect. Of course, the specifics would vary according to the actions you’re taking, but in general, you want one collision to result in one action.

终极例子,看完今天可以下课了:

Media 点这里 显示/隐藏 媒体

var left:Number = 0;
var right:Number = Stage.width;
var top:Number = 0;
var bottom:Number = Stage.height;
var numBalls:Number = 30;
var spring:Number = 0.05;
var gravity:Number = 0.3;
init();
//初始化球
function init()
{
 for (var i = 0; i<numBalls; i++)
 {
 var ball:MovieClip = attachMovie("ball", "ball" + i, i);
 ball._x = Math.random() * Stage.width;
 ball._y = Math.random() * Stage.height;
 ball._width = ball._height = Math.random() * 40 + 20;
 ball.vx = 0;
 ball.vy = 0;
 }
}
function onEnterFrame(Void):Void
{
 for(var i=0;i<numBalls-1;i++)
 {
 var ballA:MovieClip = this["ball" + i];
 for(var j=i+1;j<numBalls;j++)
 {
 var ballB:MovieClip = this["ball" + j];
//ballB-ballA 就想象以ballA为中心
 var dx:Number = ballB._x - ballA._x;
 var dy:Number = ballB._y - ballA._y;
 var dist:Number = Math.sqrt(dx*dx + dy*dy);
 var minDist:Number = ballA._width / 2 +
 ballB._width / 2;
//如果小于半径之和,很明显就碰撞了,看下边怎么处理
 if(dist < minDist)
 {
 var angle:Number = Math.atan2(dy, dx);
 //ballB应该在的那个点
 var targetX:Number = ballA._x +
 Math.cos(angle) * minDist;
 var targetY:Number = ballA._y +
 Math.sin(angle) * minDist;
//ballB向targetX给一个加速度
 var ax:Number = (targetX - ballB._x) *
 spring;
 var ay:Number = (targetY - ballB._y) *
 spring;
//相反ballA反方向也要给一个加速度
 ballA.vx -= ax;
 ballA.vy -= ay;
 ballB.vx += ax;
 ballB.vy += ay;
 }
 }
 }
 for (var i = 0; i<numBalls; i++)
 {
 var ball:MovieClip = this["ball" + i];
 move(ball);
 }
}
//简单移动的函数
function move(ball:MovieClip)
{
 ball.vy += gravity;
 ball._x += ball.vx;
 ball._y += ball.vy;
 if (ball._x + ball._width / 2 > right)
 {
 ball._x = right-ball._width / 2;
 ball.vx *= -.9;
 }
 else if (ball._x - ball._width / 2 < left)
 {
 ball._x = left+ball._width / 2;
 ball.vx *= -.9;
 }
 if (ball._y + ball._width / 2 > bottom)
 {
 ball._y = bottom-ball._width / 2;
 ball.vy *= -.9;
 }
 else if (ball._y - ball._width / 2 < top)
 {
 ball._y = top+ball._width / 2;
 ball.vy *= -.9;
 }
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值