圓形與矩形碰撞(有旋轉矩形)
承上篇,如果是寬高與兩軸不平行,也就是經過旋轉的矩形,如何檢定他和圓形的碰撞?如下圖:
因為上一篇已經完成了圓形與擺正的矩形之碰撞檢定,那就可以將不平行的矩形轉正,利用已完成的函式來解決這個問題。首先要將矩形轉正,就需要找到角度,能夠直接從物件中得到旋轉角度是最好的(我自己設計遊戲物件就有做旋轉角度的 getter setter),沒有的話可能要考慮多寫一個方法。
如上圖,找到矩形從擺正到現在傾斜是轉了θ度的話,接下來考慮將圓形對矩形的中心旋轉 ( - θ ) 度。為什麼要這麼做呢?
其實是將圓形與矩形兩個都對矩形中心旋轉 ( - θ ) 度。如此一來兩個圖形就符合上一篇的前提,這樣來判斷相交情形,就代表原來的兩圖形是否碰撞的結果是一致的,因為對同一個旋轉中心旋轉同樣的角度,之前之後的兩圖形相對位置並沒有改變。再加上矩形本身對自己的中心旋轉,其寬的一半、高的一半和中心坐標,還是一樣,所以只需要考慮圓形對矩形中心旋轉後的結果,又半徑也不受旋轉影響,就剩會影響「兩中心點水平差」和「兩中心點鉛直差」的新圓心坐標了,所以結論是:
目標是找出圓心對矩形中心旋轉 ( - θ ) 後的新坐標
將圓形與矩形對矩形中心旋轉 ( - θ ) 得到的結果:
有旋轉得新坐標的方法是最好,不難也可以再重寫,步驟大概有:
- 先平移 ( - 矩形中心 ),即平移後坐標 = 圓心坐標 - 矩形中心坐標,
- 乘上 ( - θ ) 的旋轉矩陣 [ [cos(-θ) , - sin(-θ)] , [ sin(-θ) , cos(-θ) ] ]
- 再平移矩形中心坐標,坐標加上 矩形中心坐標
得圓形旋轉後新坐標,就可以引入前一篇的函式:
public boolean isCircRotateRectCollide(Circle circ, Rectangle rect) { double theta = Math.toRadians(rect.Angle() * (-1)); // 旋轉負的角度 Circle rotatedCirc = new Circle(); int transX = circ.X() - rect.X(); // 平移 int transY = circ.Y() - rect.Y(); int newX = (int)(Math.cos(theta)*transX - Math.sin(theta)*transY) + rect.X(); int newY = (int)(Math.sin(theta)*transX + Math.cos(theta)*transY) + rect.Y(); // 乘上旋轉矩陣並平移回來 rotatedCirc.setX(newX); // 另外建立一個圓形物件,設定 x, y, r rotatedCirc.setY(newY); rotatedCirc.setRadius(circ.Radius()); return isCircRectCollide(rotatedCirc, rect); // 藉由呼叫之前的函式 } // 引入已轉正的圓形和矩形
不呼叫之前函式的話,就再寫一遍內容:
public boolean isCircRotateRectCollide(Circle circ, Rectangle rect) { double theta = Math.toRadians(rect.Angle() * (-1)); // 旋轉負的角度 int transX = circ.X() - rect.X(); // 平移 int transY = circ.Y() - rect.Y(); int newX = (int)(Math.cos(theta)*transX - Math.sin(theta)*transY) + rect.X(); int newY = (int)(Math.sin(theta)*transX + Math.cos(theta)*transY) + rect.Y(); int deltaX = Math.abs( newX - rect.X() ); // 用新坐標求水平差和鉛直差 int deltaY = Math.abs( newY - rect.Y() ); double halfWidth = rect.Width() / 2f; double halfHeight = rect.Height() / 2f; int radius = circ.Radius(); if(deltaX > halfWidth + radius || deltaY > halfHeight + radius) { return false; } if(deltaX <= halfWidth || deltaY <= halfHeight) { return true; } double distance = Math.pow( deltaX - halfWidth, 2) + Math.pow( deltaY - halfHeight, 2); return distance < Math.pow(radius, 2); }
留言
張貼留言