在物理模型,尤其是动量,碰撞类实验中,碰撞的检测方法的优劣无疑决定着整个程序摸拟效果的好坏。碰撞检测函数的构造难度随着碰撞复杂程度的增加而增加。
a) 平面上两个物体的碰撞:
//a)最简单的二物碰撞。
if(mc1.getBounds(_root).xMax>=mc2.getBounds(_root).xMin){//mc1.hitTest(mc2)
例子1:二球动量守恒(速度过大时会出问题,原因在序2中有讲)
点这里下载演示效果文件
例子2:三球动量守恒:(速度过大时会出问题,原因在序2中有讲)
UploadFile/2004-8/2004851211341.swf
这个是有方向的,只检测了
//b)框与小物块的碰撞。
//这里参考了FLASH SAMPLE中那个迷宫的源文件,里面的用getBounds来做的碰撞很棒。
function checks(mc1,mc2){//specialize collision detect
if(mc2.hitTest(mc1.getBounds(_root).xMax,mc1._y,true)){
collision(mc1,mc2);
}
else if(mc2.hitTest(mc1.getBounds(_root).xMin,mc1._y,true)){
collision(mc2,mc1);
}
}
例子:框与木块(速度过大时会出问题,原因在序2中有讲)
UploadFile/2004-8/200482021202779.swf
//c)两个小球的碰撞,半径法:
if(Math.sqrt((balla._x-ballb._x)*(balla._x-ballb._x)+(balla._y-ballb._y)*(balla._y-ballb._y))<=(balla.r+ballb.r))
///***Fundamental Function***///
function collisiondetect(mc1, mc2) {
if(Math.sqrt((mc1._x-mc2._x)*(mc1._x-mc2._x)+(mc1._y-mc2._y)*(mc1._y-mc2._y))<=mc1.r+mc2.r) {
collisionx(mc1, mc2);
collisiony(mc1, mc2);
}
}
function collisionx(mc1, mc2) {
var v1temp = mc1.vx;
var v2temp = mc2.vx;
mc1.vx=((mc1.m-e*mc2.m)/(mc1.m+mc2.m))*v1temp+((1+e)*mc2.m/(mc1.m+mc2.m))*v2temp;
mc2.vx = ((1+e)*mc1.m/(mc1.m+mc2.m))*v1temp-((e*mc1.m-mc2.m)/(mc1.m+mc2.m))*v2temp;
}
function collisiony(mc1, mc2) {
var v1temp = mc1.vy;
var v2temp = mc2.vy;
mc1.vy= ((mc1.m-e*mc2.m)/(mc1.m+mc2.m))*v1temp+((1+e)*mc2.m/(mc1.m+mc2.m))*v2temp;
mc2.vy = ((1+e)*mc1.m/(mc1.m+mc2.m))*v1temp-((e*mc1.m-mc2.m)/(mc1.m+mc2.m))*v2temp;
}
MovieClip.prototype.Move = function(vx, vy) {
// 构造专属的move函数。
this._x += vx;
this._y += vy;
};
///**Combine Function*////
function SumCollision() {
collisiondetect(balla, ballb);
collisiondetect(balla, ballc);
collisiondetect(ballb, ballc);
bordercollisiondetect(balla);
bordercollisiondetect(ballb);
bordercollisiondetect(ballc);
}
function SumMove() {
balla.Move(balla.vx, balla.vy);
ballb.Move(ballb.vx, ballb.vy);
ballc.Move(ballc.vx, ballc.vy);
}
_root.onEnterFrame = function() {
SumCollision();
SumMove();
Debugger();
};
但是不足之处是显然的,那就是没有加是否真正分离的判定,结果造成了这个有BUG的碰撞效果:
UploadFile/2004-8/20048520718753.swf
之所以出现两个小球在一起“打架”的情况,是因为两球碰后的速度不足以保证二者分开,于是在下次又进入碰撞判定,刚才的速度还是分离的,现在又相向了,于是就不断的呈现出“粘在一起”的现象。
后来想了一下,给每个球建立一个c属性,用于标识其是否碰撞,碰撞后为1,当检测两个球的距离大于半径之和, c变为0,进入碰撞检测判断,照此不断的在
“碰撞断定”---“碰后检测”----“分开”----“碰撞判定”间进行循环,效果的确不错,
UploadFile/2004-8/20048621362191.swf
例子2:
核心算法:
function PreciseCollisionRender(mc1, mc2) {// 确定是否分开
if(Math.sqrt((mc1._x-mc2._x)*(mc1._x-mc2._x)+(mc1._y-mc2._y)*(mc1._y-mc2._y))>mc1.r+mc2.r+1) {
mc1.c = 0;
mc2.c = 0;
}
}
function collisiondetect(mc1, mc2) {
if (mc1.c == 1 && mc2.c == 1) {
PreciseCollisionRender(mc1, mc2); // *****ok,no problem
}
elseif(Math.sqrt((mc1._x-mc2._x)*(mc1._x-mc2._x)+(mc1._y-mc2._y)*(mc1._y-mc2._y))<=mc1.r+mc2.r) {
collisionx(mc1, mc2);
collisiony(mc1, mc2);
mc1.c = 1;
mc2.c = 1;
}
}
其实用这样的两个球之间的c值进行标识的方法在逻辑上还是有些说不过去,如果几个球同时碰时问题似乎就复杂了,但是从效果上来看,由于在碰后的几帧内(即便是在墙角边),两球的分开也是很必然的,所以,这样一个理由不是很充足的写法倒也说得过去,怎样就完配了呢,我想了一下,用一个二维数组来标识一组球的碰撞情况,如ball1和ball2就用a[1][2]来标识,这样这两个小球间的“碰撞断定”---“碰后检测”----“分开”----“碰撞判定”的循球,就由这一个二维数组元素来进行唯一的标识,通过数组元素值倒底是1还是0决定每帧里两个小球间进入何种检测,这就是我比较满意的一个七球碰撞系统:
UploadFile/2004-8/200481117051510.swf
例子3
完整的程序清单:
#include "Physics.as"
_root.onLoad = function() {
ball1.CreatePhysicsFeature(1, 0, 0, 8, 6, 15);
ball2.CreatePhysicsFeature(1, 0, 0, -7, 5, 15);
ball3.CreatePhysicsFeature(1, 0, 0, 8, -9, 15);
ball4.CreatePhysicsFeature(1, 0, 0, 5, -5, 15);
ball5.CreatePhysicsFeature(1, 0, 0, 5, -2, 15);
ball6.CreatePhysicsFeature(1, 0, 0, 6, -6, 15);
ball7.CreatePhysicsFeature(1, 0, 0, 7, -6, 15);
for (var i = 1; i<8; i++) {//创建用于标识二球间状况的二维数组
eval("ball"+i).n(i);
}
_global.a = new Array();
for (var i = 1; i<7; i++) {
a[i] = new Array();
for (var j = i+1; j<8; j++) {
a[i][j] = 0;
}
}
};
///***Fundamental Function***///
function PreciseCollisionRender(mc1, mc2) {// 确定是否分开
if(Math.sqrt((mc1._x-mc2._x)*(mc1._x-mc2._x)+(mc1._y-mc2._y)*(mc1._y-mc2._y))>mc1.r+mc2.r+1) {
a[mc1.n][mc2.n] = 0;
}
}
function collisionx(mc1, mc2) {
var v1temp = mc1.vx;
var v2temp = mc2.vx;
mc1.vx=((mc1.m-e*mc2.m)/(mc1.m+mc2.m))*v1temp+((1+e)*mc2.m/(mc1.m+mc2.m))*v2temp;
mc2.vx = ((1+e)*mc1.m/(mc1.m+mc2.m))*v1temp-((e*mc1.m-mc2.m)/(mc1.m+mc2.m))*v2temp;
}
function collisiony(mc1, mc2) {
var v1temp = mc1.vy;
var v2temp = mc2.vy;
mc1.vy= ((mc1.m-e*mc2.m)/(mc1.m+mc2.m))*v1temp+((1+e)*mc2.m/(mc1.m+mc2.m))*v2temp;
mc2.vy = ((1+e)*mc1.m/(mc1.m+mc2.m))*v1temp-((e*mc1.m-mc2.m)/(mc1.m+mc2.m))*v2temp;
}
MovieClip.prototype.Move = function(vx, vy) {
// 构造专属的move函数。
this._x += vx;
this._y += vy;
};
function bordercollisiondetect(mc) {//碰边检测
if (mc.getBounds(_root).xMin<1) {
mc._x = 1+mc.r;
mc.vx *= -1;
}
if (mc.getBounds(_root).xMax>399) {
mc._x = 399-mc.r;
mc.vx *= -1;
}
if (mc.getBounds(_root).yMin<1) {
mc._y = 1+mc.r;
mc.vy *= -1;
}
if (mc.getBounds(_root).yMax>399) {
mc._y = 399-mc.r;
mc.vy *= -1;
}
}
//**Combine Function**//
function collisiondetect(mc1, mc2) {
if (a[mc1.n][mc2.n] == 1) {
PreciseCollisionRender(mc1, mc2);
}
elseif(Math.sqrt((mc1._x-mc2._x)*(mc1._x-mc2._x)+(mc1._y-mc2._y)*(mc1._y-mc2._y))<=mc1.r+mc.r) {
collisionx(mc1, mc2);
collisiony(mc1, mc2);
a[mc1.n][mc2.n] = 1;
}
}
function SumCollision() {
for (var i = 1; i<7; i++) {
for (var j = i+1; j<8; j++) {
collisiondetect(eval("ball"+i), eval("ball"+j));
}
}
for (var i = 1; i<8; i++) {
bordercollisiondetect(eval("ball"+i));
}
}
function SumMove() {
for (var i = 1; i<8; i++) {
eval("ball"+i).Move(eval("ball"+i).vx, eval("ball"+i).vy);
}
}
_root.onEnterFrame = function() {
SumCollision();
SumMove();
}