demo: http://cnwander.com/demo/billiards/
原文地址:http://cnwander.com/blog/?p=11
先貼上代碼:
運行代碼框
[ctrl+a 全部選擇 提示:你可先修改部分代碼,再按運行]
雖厚顏冠名桌球,其實與真實桌球還相差甚遠,還有太多需要改進的地方。
具體待解決的問題:
其它肯定還會有很多問題,擔心假期把心玩油了,回頭沒心思繼續,干脆一氣呵成,趕得有些匆忙,問題回頭再慢慢解決吧,先發上來,對這塊有興趣的同學一塊兒探討探討。
大學數學基本是過場,高中的物理數學與忘得所剩無幾,真正做東西才發現自己這塊太薄弱,希望在這方面經驗比較豐富的同學不吝賜教。
|||
貼出一些關鍵代碼稍作解釋,有興趣的同學看看。
// ball class
function ball(type,x,y) {
...
this.type = type;
this.x = x; //位置
this.y = y;
this.angle = 0; //角度
this.v = 0; //速度(不包含方向)
...
return this;
}
描述小球的四個信息,小球的類型(母球,目標球),坐標,角度,速度
在更新坐標時,讀取小球的v,刷新小球的位置
在與邊沿碰撞時,更改小球angle
var formpos = getballpos(cueball.elem),
topos = getballpos(guideball),
angle = math.atan2(topos[0] - formpos[0],topos[1] - formpos[1]);
計算母球,與參考球之間的角度,其它任意小球之間也是如此
值得注意的是我采用的是math.atan2,而非math.atan,這是因為math.atan2返回的是(0 - math.pi)和(-math.pi - 0),可以確定唯一的角度,而math.atan不唯一。
//邊緣碰撞
if(ball.x < r || ball.x > w - r) {
ball.angle *= -1;
ball.v = ball.v * (1 - loss);
...
if(ball.type == "cue") {
if(ball.angle > 0) vy -= rollright;
else vy += rollright;
vx += rollup;
rollup *= 0.2;
rollright *= 0.2;
ball.v = math.sqrt(vx*vx + vy*vy);
ball.angle = math.atan2(vx,vy);
}
...
if(ball.y < r || ball.y > h - r) {
ball.angle = ball.angle > 0 ? math.pi - ball.angle : - math.pi - ball.angle ;
...
不考慮小球旋轉時,邊緣碰撞很簡單,更改小球angle即可
當小球旋轉時,如果碰到固定不動的物體時,那將會把速度作用給自身
并且自身旋轉速度減小
我這里計算球與球之間碰撞,并沒有將旋轉傳遞,這是不準確的,有待改善
這一段是核心,即小球與小球碰撞后各自的速度,其實并不難
先判斷兩球距離
var dis = math.sqrt(math.pow(disx,2)+math.pow(disy,2));
if(dis <= gap) {
//如果目標球是靜止的,則添加到數組movingballs
if(math.round(obj.v) == 0)
movingballs.push(obj);
//還原兩球相切狀態,用其它方式做我不知道,但用js來做,這一步相當關鍵,否則誤差將相當大
ball.x -= (gap - dis)*sin;
ball.y -= (gap - dis)*cos;
disx = obj.x - ball.x;
disy = obj.y - ball.y;
// 下面則是先將整個坐標系旋轉到相撞的水平方向
// 計算角度和正余弦值
var angle = math.atan2(disy, disx),
hitsin = math.sin(angle),
hitcos = math.cos(angle),
objvx = obj.v * math.sin(obj.angle),
objvy = obj.v * math.cos(obj.angle);
//trace(angle*180/math.pi);
// 旋轉坐標
var x1 = 0,
y1 = 0,
x2 = disx * hitcos + disy * hitsin,
y2 = disy * hitcos - disx * hitsin,
vx1 = vx * hitcos + vy * hitsin,
vy1 = vy * hitcos - vx * hitsin,
vx2 = objvx * hitcos + objvy * hitsin,
vy2 = objvy * hitcos - objvx * hitsin;
// 碰撞后的速度和位置
var plusvx = vx1 - vx2;
vx1 = vx2;
vx2 = plusvx + vx1;
//母球加塞
if(ball.type == "cue") {
vx1 += rollup;
rollup *= 0.2;
}
x1 += vx1;
x2 += vx2;
// 將位置旋轉回來
var x1final = x1 * hitcos - y1 * hitsin,
y1final = y1 * hitcos + x1 * hitsin,
x2final = x2 * hitcos - y2 * hitsin,
y2final = y2 * hitcos + x2 * hitsin;
obj.x = ball.x + x2final;
obj.y = ball.y + y2final;
ball.x = ball.x + x1final;
ball.y = ball.y + y1final;
// 將速度旋轉回來
vx = vx1 * hitcos - vy1 * hitsin;
vy = vy1 * hitcos + vx1 * hitsin;
objvx = vx2 * hitcos - vy2 * hitsin;
objvy = vy2 * hitcos + vx2 * hitsin;
//最終速度
ball.v = math.sqrt(vx*vx + vy*vy) * (1 - 0);
obj.v = math.sqrt(objvx*objvx + objvy*objvy) * (1 - 0);
// 計算角度
ball.angle = math.atan2(vx , vy);
obj.angle = math.atan2(objvx , objvy);
}
不一定用坐標旋轉,我只是習慣了用這種方式來計算碰撞,只要將正向碰撞的速度相差即可
新聞熱點
疑難解答