Java簡單遊戲開發之碰撞檢測

前言

    不久之前在論壇上有人發貼,使用java編寫的超級馬里奧如何實現碰撞檢測,筆者自己以前

  也做過Tank大戰。裏面同樣涉及到碰撞檢測,翻翻U盤裏的東西還在,什麼時候也給共享出來。

  這篇文章就簡單遊戲中的碰撞檢測做一個簡單的總結。首先需聲明的是這裏只是2D的碰撞檢測。

    文章出處地址:http://blog.csdn.net/kiritor/article/details/8948097

碰撞檢測

    對於形狀之間如何來判斷是否是碰撞的這要根據具體的形狀來定。在新手練手的小遊戲中,

 物體形狀一般可以設定爲矩形區域,這類規則圖形。它的碰撞檢測可以通過java API中的

  Rectangle類來實現碰撞的檢測。

 規則圖形碰撞檢測(Rectangle)

    首先我們查看API關於Rectangle類的介紹:它就是指定座標空間的一個區域,這個區域是通過

  指定左上角x、y座標和去高度和寬度來確定的。

  接下來看起具體的方法public Rectangleintersection(Rectangle r),這個方法就是碰撞檢測

  的關鍵了,如果兩個Rectangle對象有交集,那麼他們就有碰撞了。而每個形狀我們都可以得到他

  們的Rectangle對象,這樣圖形的碰撞檢測也就得以實現了。

/* 判斷子彈是否擊中障礙物 */
public boolean isHit(com.Alex.map.Map map) {
    boolean flag = true;// 代表沒有撞到
    // 分類別的得到所有的障礙物
    List<Stuff> stuffList = new Vector<Stuff>();
    stuffList.addAll(map.getBricks());
    stuffList.addAll(map.getIrons());
    stuffList.addAll(map.getWaters());
    for (int i = 0; i < stuffList.size(); i++) {
        Stuff a = stuffList.get(i);
        Rectangle tankRectangle = new Rectangle(bullet2.getRec());
        Rectangle stuffRectangle = new Rectangle(a.getX(), a.getY(), 20, 20);
        if (stuffRectangle.intersects(tankRectangle)) {
            flag = false;// 撞到了
            break;
        }
    }
    return flag;
}

          上述這個例子就是判斷Tank發出的子彈是否對地圖中的障礙物有碰撞,如果有的話

     就做相關的操作(子彈爆炸、障礙物消失)。上述代碼中子彈對象有一個getRec()方法就是

     得到子彈圖形的Rectangle對象,具體實現就是根據其座標和width、height來生成的。

          採用此種方法進行碰撞檢測需要注意,對於圖片的實現處理應該儘量的去掉圖標邊角

     的空白,不然實際效果可以產生肉眼可辨的誤差。也就是說Rectangle儘量的包住圖形

     且Rectangle的區域儘量小。這種碰撞檢測的方法被稱之爲多矩形碰撞


          一旦有一個矩形數組中的矩形與另外一個矩形數組的矩形發生碰撞就可認爲發生了

      多矩形碰撞。其中多圓形碰撞也是同樣的道理,只是包裹的圖形區域是圓形罷了。

      不過仔細思考多矩形碰撞同樣會有誤差,雖然這種誤差十分小。


  像素級別的碰撞檢測

       像素級別的碰撞檢測算得上是最精確的碰撞檢測方法了。

       首先遍歷算出一張位圖所有的像素點座標,然後與另外一張位圖上的所有點座標進行對比,

   一旦有一個像素點的座標相同,就立刻取出這兩個座標相同的像素點,通過位運算取出這兩個

   像素點的最高位(透明度)進行對比,如果兩個像素點都是非透明像素則判定這兩張位圖發生

   碰撞。

       介紹了像素碰撞之後可以得到兩個結論:

          1、像素碰撞很精確,不論位圖之間是否帶有透明像素,都可以精確判斷;

          2、正是因爲像素碰撞的這種高精確判定,從而也會造成代碼效率明顯降低!

               假設兩張100×100 大小的位圖利用像素級檢測碰撞,僅是遍歷兩張位圖的像素  

               就要循環100×100×2=20000 句邏輯代碼;況且還要對篩選出來的相同座標的

               像素點進行遍歷對比其透明值!這種效率可想而知!

      當然,這裏的像素碰撞只是大致提供一種思路,肯定還可以進行代碼優化;但是不論再優的

      代碼,使用像素級進行碰撞檢測終會導致整個程序的運行效率大大降低。因此像素級別的碰

      撞檢測在遊戲開發中是儘量避免使用的!

   規則圖形碰撞檢測2

         對於子彈和障礙物的碰撞檢測,採用上述第一種方法就可以簡單的實現了,不過子彈

     是圓形的有沒有更加精確的碰撞檢測方法呢?也就是實現圓形和矩形的碰撞檢測嘛。

        這裏我們需要簡單的運行下幾何數學的知識,給個簡單的圖就會明白了。


         小圓有個運動軌跡,軌跡的線如果和他對着的正方形的相對某一象限的邊有焦點,那麼

      就能碰撞,邊就是那一個象限的邊(還要把圓半徑算進去),具體代碼就不實現了,讀者可

      自己嘗試着去實現。    

  不規則圖形碰撞檢測

     對於矩形碰撞,很多人都知道。但面對多邊形圖形,大多數採用多矩形覆蓋的方式。

       SAT 一種可以快速檢測不規則的凸多邊形是否碰撞的算法給出兩個凸多邊形體,

    如果我們能找到一個軸線,使兩物體在此軸線上的投影不重疊,則這兩個物體之間沒有

    發生碰撞,這個軸線叫做Separating Axis(紅色軸線)。

       對於2D來說,紅色線就是垂直與多邊形邊的軸。

      因此,如果我們要檢查兩多邊形是否碰撞,就去檢查兩多邊形在每個所有可能的軸上的投影

   是否重疊。

/// 檢測2個矩形是否發生碰撞
/// </summary>
/// <returns></returns>
public static bool IsIntersect (Vector2[] A, Vector2[] B)
 {
    Vector2 AX, AY, BX, BY;
    AX = new Vector2();
    AY = new Vector2();
    BX = new Vector2();
    BY = new Vector2();
                                          
    AX.X = A[0].X - A[1].X;
    AX.Y = A[0].Y - A[1].Y;           
    AY.X = A[0].X - A[3].X;          
    AY.Y = A[0].Y - A[3].Y;           
    BX.X = B[0].X - B[1].X;          
    BX.Y = B[0].Y - B[1].Y;           
    BY.X = B[0].X - B[3].X;          
    BY.Y = B[0].Y - B[3].Y;           
    //對於AX上:          
    if (Tmp(AX, A, B)) return false;          
    if (Tmp(AY, A, B)) return false;         
    if (Tmp(BX, A, B)) return false;          
    if (Tmp(BY, A, B)) return false;          
    return true;     
}
                                                
private static bool Tmp(Vector2 IS,Vector2[] A,Vector2[] B)
 {
    float[] v = new float[4];
    for (int i = 0; i < 4; i++)
    {              
        float tmp = (IS.X * A[i].X + IS.Y * A[i].Y) / (IS.X * IS.X + IS.Y * IS.Y);
                 v[i] = tmp * IS.X * IS.X + tmp * IS.Y * IS.Y;         
    }
    float[] vv = new float[4];
    for (int i = 0; i < 4; i++)
    {
        float tmp = (IS.X * B[i].X + IS.Y * B[i].Y) / (IS.X * IS.X + IS.Y * IS.Y);
                  vv[i] = tmp * IS.X * IS.X + tmp * IS.Y * IS.Y;
    }
    if (Math.Max(Math.Max(v[0], v[1]),Math.Max(v[2],v[3])) >Math.Min(Math.Min(vv[0],vv[1]),Math.Min(vv[2],vv[3])) && Math.Min(Math.Min(v[0],v[1]),Math.Min(v[2],v[3])) < Math.Max(Math.Max(vv[0],vv[1]),Math.Max(vv[2],vv[3]))) {
     return false;
    }//表示暫時不知道是否碰撞          
    else return true;//表示知道未碰撞
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章