我试图在矩形中显示玩家瞄准我的游戏的位置。他在这样的广场上:
我找到了algorithm to find rectangle point by angle,应用它并且结果错误(真正的游戏截图):
我很快意识到这是因为玩家不在矩形的中心。所以我用我的观点替换了algorighm中的中心点。但结果只是移动了一点距离:
所以我猜我当前的算法需要的不仅仅是一个变化。我的代码:
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;
}
我还需要在哪里应用参考点位置才能使其工作?如果点超出矩形,则此函数返回合理的结果
答案 0 :(得分: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
答案 1 :(得分:1)
玩家&#34;看到&#34;在拐角处,根据他瞄准角落的哪一侧,碰撞发生在垂直或水平侧。
让我们来表示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==0
或s==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
可以对四个象限重复相同的讨论,将w
和/或h
替换为0
。最终的代码结构是
if s >= 0:
if c >= 0:
Corner (w, h)
else:
Corner (0, h)
else:
if c >= 0:
Corner (w, 0)
else:
Corner (0, 0)
注意为四个象限正确调整比较方向。
选择此配方以尝试达到最小量的计算,同时保持数值可靠。我们列出
答案 2 :(得分:0)
算法的许多部分,例如height / 2.
,表明您仍然认为该点位于矩形的中心。如果您的点可以位于矩形中的任何位置,则必须调整所有数字,以便您使用y
或height - 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·π/ 2为南。如果使用其他定义,则应调整代码。
当点位于矩形之外或者找不到解决方案时,代码返回null
。