昨天群里有朋友问到, 顺便发下. FTP连不上, 我直接贴代码得了.
FTP弄好了, Demo地址 http://www.vilic.info/demo/collide/
沙发同学Lin.x爆料地下的代码部分半角被替换成了全角, 会报错. 如果需要代码的话, 打开Demo自己另存为吧.
groinup.js
/*
圆形碰撞算法
www.vilic.info
by Vilic Vane
©2010 Groinup
*/
var groinup = new function () {var array = this.array = new function () {
this.clear = function (aimArray) {
aimArray.length = 0;
};this.remove = function (item, arr) {
var i;
for (i in arr)
if (arr[i] == item)
arr.splice(i, 1);
};this.addUnique = function (item, arr) {
var found = false;
var i;
for (i in arr)
if (arr[i] == item) {
found = true;
break;
}
if (!found) arr.push(item);
};
};var object = this.object = new function () {
this.copy = function (obj) {
var newObj = {};
var i;
for (i in obj)
newObj[i] = obj[i];
return newObj;
};
} ();var math = this.math = new function () {
var sqr = this.sqr = function (n) { return n * n; };
var sqrt = this.sqrt = function (n) { return Math.sqrt(n); };var modulus = this.modulus = function (a1, a2) {
var x1 = a1.x;
var y1 = a1.y;
var x2 = a2.x;
var y2 = a2.y;return (x1 * x2 + y1 * y2) / sqrt((sqr(x1) + sqr(y1)) * (sqr(x2) + sqr(y2)));
};var point = this.point = new function () {
var getDistance = this.getDistance = function (pos1, pos2) {
return sqrt(sqr(pos1.x – pos2.x) + sqr(pos1.y – pos2.y));
};
} ();var circle = this.circle = new function () {
this.getDistance = function (pos1, pos2, r1, r2) { return point.getDistance(pos1, pos2) – r1 – r2; };
};var Position = this.Position = function (x, y) {
this.x = x;
this.y = y;
};} ();
var physics = this.physics = new function () {
var sqr = math.sqr;
var sqrt = math.sqrt;var getMovingDistance = this.getMovingDistance = function (v0, a, t) {
return v0 * t + 0.5 * a * math.sqr(t);
};var Velocity = this.Velocity = function (vx, vy) {
this.x = vx;
this.y = vy;
};var Acceleration = this.Acceleration = function (ax, ay) {
this.x = ax;
this.y = ay;
};
} ();var collide = this.collide = new function () {
//函数引用
var sqr = math.sqr;
var sqrt = math.sqrt;
var modulus = math.modulus;var Circle = this.Circle = function (m, r, p, v, a, goFunc) {
var _this = this;
this.m = m;
this.r = r;
this.p = p;
this.v = v;this.a = a || new physics.Acceleration(0, 0);
a = this.a;var acTime = 0;
this.resetAcTime = function () { acTime = 0; };
this.onCollide = function () { };this.collideCallBack = function (p, c) {
_this.onCollide(p, c);
};this.onVelocityReverse = function () { };
this.onSpeedUp = function () { };
this._isOnSurface = false;
if (goFunc)
this.go = function (t) {
acTime += t;
if (goFunc(acTime) == false) go(t);
};
else
this.go = go;function go(t) {
var vx = v.x + a.x * t;
var vy = v.y + a.y * t;var sqrVxy0 = sqr(v.x) + sqr(v.y);
var sqrVxy = sqr(vx) + sqr(vy);p.x += (vx + v.x) / 2 * t;
p.y += (vy + v.y) / 2 * t;v.x = vx;
v.y = vy;if (sqrVxy >= sqrVxy0) _this.onSpeedUp();
}
};var Surface = this.Surface = function () {
var kmax = sqrt(Number.MAX_VALUE) – 1;
var circles = [];
var isSearching = false;this.add = function (circle) {
circle._isOnSurface = true;
circles.push(circle);
};this.remove = function (circle) {
circle._isOnSurface = false;
if (!isSearching) array.remove(circle, circles);
};this.go = function (time, step) {
collideSearch(time, step);
};function collideSearch(time, step) {
isSearching = true;var sAmt = Math.floor(time / step);
for (var i = 0; i < sAmt; i++)
search();isSearching = false;
function search() {
var i;var removeList = [];
for (i in circles)
circles[i].go(step);var getDis = math.circle.getDistance;
var len = circles.length;
for (var j = 0; j < len; j++) {
var c1 = circles[j];//判断元素是否已经移除
if (!c1._isOnSurface) {
removeList.push(c1);
continue;
}
for (var k = j + 1; k < len; k++) {
var c2 = circles[k];//判断元素是否已经移除
if (!c1._isOnSurface) {
removeList.push(c1);
break;
}
else
if (!c2._isOnSurface) {
array.addUnique(c2, removeList);
continue;
}var d = getDis(c1.p, c2.p, c1.r, c2.r);
if (getDis(c1.p, c2.p, c1.r, c2.r) <= 0)
dealCollide(c1, c2);
}
}//移除在搜索过程中被搁置的需要移除的项
for (i in removeList)
array.remove(removeList[i], circles);
}function dealCollide(c1, c2) {
//获取相关数据
var p1 = c1.p, p2 = c2.p;
var v1 = c1.v, v2 = c2.v;
var m1 = c1.m, m2 = c2.m;
var x1 = p1.x, x2 = p2.x, y1 = p1.y, y2 = p2.y;
var vx1 = v1.x, vx2 = v2.x, vy1 = v1.y, vy2 = v2.y;var a11 = { x: vx1, y: vy1 };
var a12 = { x: x2 – x1, y: y2 – y1 };
var a21 = { x: vx2, y: vy2 };
var a22 = { x: x1 – x2, y: y1 – y2 };//计算两个圆圆心连线的斜率
var k;
var dx = x1 – x2;//判断斜率是否存在
if (dx) k = (y1 – y2) / dx;
//如果不存在,则去尽可能大的值
else k = kmax;//**速度分解
//*第一个圆
//圆心连线方向的分速度
var vox1 = apart(vx1, vy1);
var voy1 = vox1 * k;//与连线方向垂直的分速度
var vpx1 = vx1 – vox1;
var vpy1 = vy1 – voy1;//*第二个圆
//圆心连线方向的分速度
var vox2 = apart(vx2, vy2);
var voy2 = vox2 * k;//与连线方向垂直的分速度
var vpx2 = vx2 – vox2;
var vpy2 = vy2 – voy2;//碰撞后与连线方向垂直的分速度不变,连线方向的分速度变化
var vox1a = calculateV(m1, m2, vox1, vox2);
var vox2a = calculateV(m2, m1, vox2, vox1);var voy1a = calculateV(m1, m2, voy1, voy2);
var voy2a = calculateV(m2, m1, voy2, voy1);//重新复合速度
v1.x = vox1a + vpx1;
v1.y = voy1a + vpy1;
v2.x = vox2a + vpx2;
v2.y = voy2a + vpy2;//告知动量变化量
var dp1 = sqrt(sqr(vox1a – vox1) + sqr(voy1a – voy1)) * m1;
var dp2 = sqrt(sqr(vox2a – vox2) + sqr(voy2a – voy2)) * m2;c1.collideCallBack(dp1, c2);
c2.collideCallBack(dp2, c1);//分解出与圆心连线方向的分速度的x方向速度
function apart(vx, vy) {
return (k * vy + vx) / (sqr(k) + 1);
}//根据动量和能量守恒计算速度变化
function calculateV(m1, m2, v1, v2) {
return ((m1 – m2) * v1 + 2 * m2 * v2) / (m1 + m2);
}
}
}
};
} ();
}
index.html
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd“>
<html xmlns=”http://www.w3.org/1999/xhtml“>
<head>
<title>碰撞类</title>
<style type=”text/css”>
#c1 { position: absolute; margin: -10px; width: 20px; height: 20px; background-color: Red; -webkit-border-radius: 10px; -moz-border-radius: 10px; -o-border-radius: 10px; }
#c2 { position: absolute; margin: -15px; width: 30px; height: 30px; background-color: Blue; -webkit-border-radius: 15px; -moz-border-radius: 15px; -o-border-radius: 15px; }
#c3 { position: absolute; margin: -6px; width: 12px; height: 12px; background-color: Green; -webkit-border-radius: 6px; -moz-border-radius: 6px; -o-border-radius: 6px; }
</style>
<script src=”groinup.js” type=”text/javascript”></script>
</head>
<body>
<div id=”c1″></div>
<div id=”c2″></div>
<div id=”c3″></div>
</body>
<script type=”text/javascript”>
//使用举例//获取元素
var c1 = document.getElementById(‘c1’);
var c2 = document.getElementById(‘c2’);
var c3 = document.getElementById(‘c3’);//容器
var surface = new groinup.collide.Surface();//定义碰撞的圆形, 参数依次为 质量, 半径, 位置, 速度, 加速度, 搜索过程中的回调函数
var ci1 = new groinup.collide.Circle(10, 10, new groinup.math.Position(210, 200), new groinup.physics.Velocity(40, 10), 0, null);
var ci2 = new groinup.collide.Circle(5, 15, new groinup.math.Position(400, 220), new groinup.physics.Velocity(-50, -10), 0, null);
var ci3 = new groinup.collide.Circle(2, 6, new groinup.math.Position(100, 320), new groinup.physics.Velocity(60, -10), 0, null);//向容器中添加圆形
surface.add(ci1);
surface.add(ci2);
surface.add(ci3);//控制运动
var i = 0;
var interval = setInterval(setPos, 30);function setPos() {
if (i++ > 100) clearInterval(interval);//移动, 参数分别为移动的时间和单次搜索的时间(单次搜索的时间越短, 运动越精确)
surface.go(0.05, 0.005);//设置位置
sP(c1, ci1.p.x, ci1.p.y);
sP(c2, ci2.p.x, ci2.p.y);
sP(c3, ci3.p.x, ci3.p.y);function sP(o, x, y) {
o.style.left = x + ‘px’;
o.style.top = y + ‘px’;
}
}</script>
</html>