DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> 關於JavaScript >> JS小游戲實例:2D桌面台球
JS小游戲實例:2D桌面台球
編輯:關於JavaScript     

網頁制作poluoluo文章簡介:JS實現2D桌球小游戲。

DEMO: http://cnwander.com/demo/billiards/
原文地址:http://cnwander.com/blog/?p=11

先貼上代碼:

運行代碼框

[Ctrl+A 全部選擇 提示:你可先修改部分代碼,再按運行]

雖厚顏冠名桌球,其實與真實桌球還相差甚遠,還有太多需要改進的地方。
具體待解決的問題:

  1. 由於浏覽器上刷新頻率不可能太高,可能當檢測兩球距離時,兩球已經重疊了大部分,甚至完全越過。
    完全越過的情況先不考慮,重疊部分如果還原到精確的相切狀態,運行非常緩慢,所以我只采用了計算量較少的近似值,具體問題主要體現在開球時,多球碰撞時有些詭異。
    (如果哪位有好的優化計算方法,可以拿出來與wander分享,那wander真的感激不盡)
  2. 球自身滾動與桌面摩擦力問題。如正中擊球的瞬間,母球滑行狀態的摩擦力會大於向前滾動時的摩擦力,小於縮桿時摩擦力等等。這個問題好解決,只是剛開始沒考慮進去,之後也沒添加了,具體體現在兩球正撞後,撞擊球將完全靜止,這是不正確的。
  3. 能量損耗問題,無論與邊沿碰撞還是球與球相撞,都是直接減去一個固定值,這肯定是存在很大問題的。

其它肯定還會有很多問題,擔心假期把心玩油了,回頭沒心思繼續,干脆一氣呵成,趕得有些匆忙,問題回頭再慢慢解決吧,先發上來,對這塊有興趣的同學一塊兒探討探討。

大學數學基本是過場,高中的物理數學與忘得所剩無幾,真正做東西才發現自己這塊太薄弱,希望在這方面經驗比較豐富的同學不吝賜教。

網頁制作poluoluo文章簡介:JS實現2D桌球小游戲。

貼出一些關鍵代碼稍作解釋,有興趣的同學看看。

// 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);
}

  • 坐標旋轉的公式
    x1 = Math.cos(angle) * x - Math.sin(angle) * y;
    y1 = Math.cos(angle) * y + Math.sin(angle) * x;
  • 反坐標旋轉
    x1 = Math.cos(angle) * x + Math.sin(angle) * y;
    y1 = Math.cos(angle) * y - Math.sin(angle) * x;

不一定用坐標旋轉,我只是習慣了用這種方式來計算碰撞,只要將正向碰撞的速度相差即可

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved