2014-07-21 70 views
0

我的瓷磚地圖有衝突解決問題。我有一個撞在瓦片上的球。碰撞工作正常,除了當兩個瓦片之間發生碰撞時。然後我的碰撞解決方法變得很糟糕,而且球飛向錯誤的方向。我的球是長方形的,而方塊是長方形的。兩塊瓷磚之間的Tilemap衝突解決方案

這裏是演示了這個問題一個.gif:

enter image description here

球應該反映斷磚一次一個。

的算法是這樣的:

  1. 應用運動,之後檢查和解決衝突。
  2. 找出球重疊的方塊。
  3. 如果瓷磚被檢查是不是差強人意:
    • 找出球是多少重疊瓦的X和Y軸。
    • 僅通過在淺軸上將球移出瓦片來解決碰撞。 (無論哪個軸最少穿透)。
    • 繼續前進到下一個圖塊。
  4. 如果被檢查的磁貼是可通過的,則什麼也不做。

我已經讀過這個最小位移技術的問題是在解決碰撞後,可能會將球移動到另一個碰撞。修復應該是,那只是第二次運行算法。我試過這個,但它沒有幫助。這裏是我的衝突處理的代碼,在C++:

void PlayState::handleCollisions() { 

// Get ball bounding box. 
sf::FloatRect ballBounds = ball.sprite.getGlobalBounds(); 

// Find out the nearby tiles. 
int leftTile = (int)floor((float)ballBounds.left/level1.TILE_WIDTH); 
int rightTile = (int)ceil(((float)(ballBounds.left + ballBounds.width)/level1.TILE_WIDTH)) - 1; 
int topTile = (int)floor((float)ballBounds.top/level1.TILE_HEIGHT); 
int bottomTile = (int)ceil(((float)(ballBounds.top + ballBounds.height)/level1.TILE_HEIGHT)) - 1; 

// For each potentially colliding tile, 
for(int y = topTile; y <= bottomTile; ++y) { 
    for(int x = leftTile; x <= rightTile; ++x) { 

     // If this tile is collidable, 
     TileCollision collision = getCollision(x, y); 
     if(collision == Impassable) { 

      // Determine collision depth (with direction) and magnitude. 
      sf::FloatRect tileBounds = getTileBounds(x, y); 
      sf::Vector2f depth = Collision::getIntersectionDepth(ballBounds, tileBounds); 

      if(depth != sf::Vector2f(0, 0)) { 
       float absDepthX = std::abs(depth.x); 
       float absDepthY = std::abs(depth.y); 

       // Resolve the collision along the shallow axis. 
       if(absDepthY < absDepthX) { 

        // Resolve the collision along the Y axis. 
        ball.sprite.setPosition(ball.sprite.getPosition().x, ball.sprite.getPosition().y + depth.y); 

        // Perform further collisions with the new bounds. 
        sf::FloatRect ballBounds = ball.sprite.getGlobalBounds(); 

        // Y-distance to the tile center. 
        if(distanceY < 0) { 
         std::cout << "Collided from TOP." << std::endl; 
         ball.velocity.y = -ball.velocity.y; 
        } 
        else { 
         std::cout << "Collided from BOTTOM." << std::endl; 
         ball.velocity.y = -ball.velocity.y; 
        } 
       } 
       else { 
        // Resolve the collision along the X axis. 
        ball.sprite.setPosition(ball.sprite.getPosition().x + depth.x, ball.sprite.getPosition().y); 

        // Perform further collisions with the new bounds. 
        sf::FloatRect ballBounds = ball.sprite.getGlobalBounds(); 

        // X-distance to the tile center. 
        if(distanceX < 0) { 
         std::cout << "Collided from LEFT." << std::endl; 
         ball.velocity.x = -ball.velocity.x; 
        } 
        else { 
         std::cout << "Collided from RIGHT." << std::endl; 
         ball.velocity.x = -ball.velocity.x; 
        } 
       } 
      } 
     } 
    } 
} 
} 

我試圖運行算法第二次,就像這樣:

void PlayState::update(sf::Time deltaTime) { 

    ball.update(deltaTime); 

    handleCollisions(); 
    handleCollisions(); 
} 

有什麼事解決這個問題?

回答

0

做碰撞檢測的唯一正確方法是在計算中涉及速度矢量。

你不僅要測試離散位置,而且要測試它們之間的整條線。至少你可以檢測到所有的碰撞。例如,一個在下面的圖片是不可檢測時只測試當前位置(標記爲1和2)

" " "|2 
" " /<- second 'collision' point 
" " /| 
___/_| 
    /^--collision point 
1 

你應該知道至少當前和以前座標(其是由增量噸電流減去速度)。所以你必須計算可能碰撞點的座標,找出哪一個更接近起點,然後在碰撞點之後重新計算路徑。

ADD:如何查找和計算碰撞點。

首先,你必須找到你的球穿過哪條邊。對於矩形網格來說,並不難:如果前一個和當前座標位於不同的行(列)中,則行(列)邊被交叉。

|  |  |  |  | 1 |  |  | 1 
--+-----+-- --+-----+-- --+-----+-- --+-----+-- 
    | 1 |  |  | 1 |  |  |  | 
    | 0 |  | 0 |  | 0 |  | 0 | 
--+-----+-- --+-----+-- --+-----+-- --+-----+-- 
    |  |  |  |  |  |  |  | 
none crossed column  row   both 

然後,你應該解決球軌跡方程(X0,Y0 - 速度 - 前一點,VX,VY)針對碰撞邊緣方程(X = XC - 交叉垂直邊緣座標):

x = x0 + t⋅vx y = y0 + t⋅vy x = xc 

,並與碰撞方程x = xc,找到yc = y(y座標)和tc = t(時間)垂直壁,您需要解方程

t = (xc - x0)/vx y = y0 +t⋅vy (time and y coord of collision) 

對於水平邊緣,同樣適用於交換x和y。

當x和y的邊緣交叉選擇一個較早雜交(具有較少的噸)

  | 1 
      |/
      |/     Here, point a is a row collision 
      b*<---       b is a column collision 
      /|      t[a] should be less than t[b] 
     a/ |      because is closer to trajectory start 
    -----*--+-------- xc[row] 
    / | 
     0 | 
      | 
      yc[col] 

Ofcourse,當計算一個碰撞和更新COORDS和速度應該 重新計算碰撞,因爲可能更多:

----*--+ 
    /\ | 
    0 \| 
     * 
     /| 
     1 
+0

我將如何計算可能的碰撞點的座標? – lefti

+0

我已經更新瞭解答這個問題的答案 – Vovanium

+0

注意@mfa答案,最好把球當作點。 – Vovanium

1

我從頭開始在flash遊戲中實現了類似的碰撞檢測。我發現將球作爲一個單一點進行建模以及將其作爲一個矢量進行建模會更容易和更準確。爲了允許所需的球大小 - 半徑r填充被添加到框的邊緣。

問題變成檢測球的線段(ptFrom..ptTo)和附近塊的交集。如果有多個交叉點,請使用最近的一個ptFrom。使用線段的反射餘數重複碰撞檢測,直到不再有碰撞。

+0

嘿,你有什麼機會仍然有你的碰撞檢測方法的代碼,所以我可以看看? – lefti

+0

當我在家用電腦上時,我可以發佈重要的信息。兩個重要的功能是檢測球矢量與垂直或水平磚邊之間的交點。如果你想做一個通用的函數來處理所有的情況,你可以在你的遊戲中使用角度磚。 – mfa