2015-10-19 154 views
0

我試圖顯示在矩形中哪裏是玩家瞄準我的遊戲。他是這樣一個正方形:從不在矩形中間的點以角度查找矩形邊界點

image description

我發現了一個algorithm to find rectangle point by angle,應用它,結果是錯誤的(真正的遊戲截圖):

image description

我很快就意識到,這是因爲球員不在矩形的中心。所以我用我的觀點取代了algorighm的中心點。但結果是通過恆定距離剛剛移位後的點:

image description

所以,我想我現在的算法,需要的不僅僅是一個變化更多。我的代碼:

function boudaryatangle(theta, point) { 
    // reduce the theta 
    theta = theta % 360; 
    // force it to be the positive remainder, so that 0 <= theta < 360 
    theta = (theta + 360) % 360; 
    // force into the minimum absolute value residue class, so that -180 < theta <= 180 
    if (theta > 180) 
     theta -= 360; 
    // Convert to radians 
    theta = (theta * Math.PI/180); 
    // Get a rectangle that has width and height properties 
    var rect = this.game.pixels; 
    var width = rect.width; 
    var height = rect.height; 
    // If refference point wasn't provided as second argument 
    //TODO: MAKE IT WORK WITH OTHER THEN RECTANGLE CENTRE POINT! 
    if(typeof point=="undefined") { 
     point = new Float32Array(2); 
     point[0] = width/2; 
     point[1] = height/2; 
    } 
    // Here be mysterious math and stuff - all bellow explained here 
    var rectAtan = Math.atan2(height, width); 
    var tanTheta = Math.tan(theta); 
    // By default, region 1 and 3 
    var region = true; 

    var xFactor = 1; 
    var yFactor = 1; 

    if ((theta > -rectAtan) && (theta <= rectAtan)) { 
     yFactor = -1; // REGION 1 
    } else if ((theta > rectAtan) && (theta <= (Math.PI - rectAtan))) { 
     yFactor = -1; // REGION 2 
     region = false; 
    } else if ((theta > (Math.PI - rectAtan)) || (theta <= -(Math.PI - rectAtan))) { 
     xFactor = -1; // REGION 3 
    } else { 
     xFactor = -1; // REGION 4 
     region = false; 
    } 

    // If region 1, 3 
    if (region) { 
     point[0] += xFactor * (width/2.);          // "Z0" 
     point[1] += yFactor * (width/2.) * tanTheta; 
    } else { 
     point[0] += xFactor * (height/(2. * tanTheta));      // "Z1" 
     point[1] += yFactor * (height/2.); 
    } 
    return point; 
    } 

還有哪些地方需要應用參考點位置才能使其工作?它是而不是要求這個函數返回合理的結果,如果點在矩形之外。

回答

1

我們有:
- 點與(PX,PY)的座標(相對於矩形角部)
- 方向矢量(DX,DY),其中dx = Cos(theta), dy = Sin(theta)
- 寬度,矩形的高度。

我們需要找到與矩形相交的第一個射線點。所以做一些方程式:

if dx = 0 then 
    t = Infinity 
else 
if dx > 0 then 
    px + dx * t = width 
else 
    px + dx * t = 0 

if dy = 0 then 
    u = Infinity 
else 
if dy > 0 then 
    py + dy * u = height 
else 
    py + dy * u = 0 

解出t和u的這些方程。不要忘記從t和u中找到最小值:v = Min(t, u) - 這是第一個交點。它的座標是: (px + v * dx, py + v * dy)

P = (10, 20) 
theta = 135deg = 3 * Pi/4 => D = (-0.707, 0.707) 
W = 40, H = 50 

t ~ 14 
u ~ 42 
v = Min(t,u) = 14 
Int.X = 10 - 14 * 0.707 = 0 
// really there is is not need to calculate this value, 
//because we determined that the first intersection occurs for left edge 
Int.Y = 20 + 14 * 0.707 = 30 
0

你的算法很多地方,例如height/2.,建議大家還是假設點在矩形的中心。如果您的觀點可以位於矩形的任何位置,則必須調整所有數字,以便使用(例如)yheight - y,具體取決於您的射線指向上或向下。

當你的點是任意的時候,你不能再做的簡化就是角度與四個角的對稱性。這意味着你不能使用你的區域方法;至少它會更復雜。

另一種方法是用四個邊界來計算光線的交點,看看它們是否躺在矩形:

function borderPoint(rect, pt, angle) { 

    // catch cases where point is outside rectangle 
    if (pt.x < rect.left) return null; 
    if (pt.x > rect.left + rect.width) return null; 
    if (pt.y < rect.top) return null; 
    if (pt.y > rect.top + rect.height) return null; 

    var dx = Math.cos(angle); 
    var dy = Math.sin(angle); 

    if (dx < 1.0e-16) {   // left border 
     var y = (rect.left - pt.x) * dy/dx + pt.y; 

     if (y >= rect.top && y <= rect.top + rect.height) { 
      return {x: rect.left, y: y}; 
     } 
    } 

    if (dx > 1.0e-16) {   // right border 
     var y = (rect.left + rect.width - pt.x) * dy/dx + pt.y; 

     if (y >= rect.top && y <= rect.top + rect.height) { 
      return {x: rect.left + rect.width, y: y}; 
     } 
    } 

    if (dy < 1.0e-16) {   // top border 
     var x = (rect.top - pt.y) * dx/dy + pt.x; 

     if (x >= rect.left && x <= rect.left + rect.width) { 
      return {x: x, y: rect.top}; 
     } 
    } 

    if (dy > 1.0e-16) {   // bottom border 
     var x = (rect.top + rect.height - pt.y) * dx/dy + pt.x; 

     if (x >= rect.left && x <= rect.left + rect.width) { 
      return {x: x, y: rect.top + rect.height}; 
     } 
    } 

    return null; 
} 

該代碼使用的角度的數學定義,即0是東, π/2是北,π是西和3&middot; π/2是南部。如果您使用其他定義,則應修改代碼。

代碼返回null當點位於矩形之外或者找不到解決方案時。

1

玩家「看到」角落,並且根據他瞄準的角落的哪一側,碰撞發生在垂直或水平一側。

讓我們表示c = cos Θs = sin Θ。從(x, y)沿方向(c, s)的線的方程式是(X - x) s - (Y - y) c = 0。左側的表達式在行的一側是正的,而在另一側是負的。

在第一象限(c, s > 0)中,角落爲(w, h),V/H決定取決於(w - x) s - (h - y) c的符號。如果交集與垂直方面,我們有

X = w, (w - x) s - (Y - y) c = 0, or Y = y + (w - x) s/c. 

對於水平側,

Y = h, (X - x) s - (h - y) c = 0, or X = x + (h - y) c/s. 

我們可以寫

Corner (w, h): 
    if (w - x) s < (h - y) c: 
     X= w; Y= y + (w - x) s/c 
    else: 
     X= x + (h - y) c/s; Y= h 

注意,如果c==0s==0,解決的辦法是仍然有效,因爲將採取的分支是不會產生零除的。在比較中是否使用<<=沒有什麼區別(如果角度本身相等,兩種方式均可)。

這可以是微優化爲

Corner (w, h): 
    S= (w - x) s; C= (h - y) c 
    if S < C: 
     X= w; Y= y + S/c 
    else: 
     X= x + C/s; Y= h 

同樣的討論可以重複四個象限,通過0替換w和/或h。最終的代碼結構是

if s >= 0: 
    if c >= 0: 
     Corner (w, h) 
    else: 
     Corner (0, h) 
else: 
    if c >= 0: 
     Corner (w, 0) 
    else: 
     Corner (0, 0) 

請注意正確調整四個象限的比較方向。

這個公式被選擇來儘量達到最小的計算量,同時保持數值上的可靠性。我們列出

  • 兩個用於象限的選擇標誌測試,
  • 一個比較涉及兩個產品,兩個加法,
  • 一個加法和交集計算一個部門。