Sam*_* D. 0 html javascript canvas detection collision
我正在使用 JavaScript 和 HTML Canvas 创建游戏。这是一款多人 2D 游戏,其中的坦克试图互相攻击。坦克可以移动也可以旋转。如何计算旋转矩形物体的碰撞检测?我知道,我可以让它们变成方形并使用圆形检测,但是当坦克撞到墙上时它看起来非常混乱。感谢所有试图提供帮助的人:)
有很多方法可以做到。最简单的方法。当您计算点和线之间的叉积时,如果该点位于线的右侧,则为负,如果在左侧,则为正。如果您依次进行四个边中的每一个,并且它们都是相同的符号,则该点必须在里面。
得到一条线和一个点的叉积
//x1,y1,x2,y2 is a line
// px,py is a point
// first move line and point relative to the origin
// so that the line and point is a vector
px -= x1;
py -= y1;
x2 -= x1;
y2 -= y1;
var cross = x2 * py - y2 * px;
if(cross < 0){
// point left of line
}else if(cross > 0) {
// point right of line
}else {
// point on the line
}
Run Code Online (Sandbox Code Playgroud)
但是对于每个对象和每个子弹来说,这是很多数学运算。
最好的方法是将子弹转换为坦克局部坐标系,然后它只是测试边界,左,右,上,下的简单问题。
为此,您需要反转坦克变换矩阵。不幸的是,简单的方法目前仍然落后于浏览器标志/前缀,因此您需要在 javascript 中创建和操作转换。(在ctx.getTransform()全面实现并填补 canvas 2d API 中非常需要的性能漏洞之前应该不会太久)
所以你在 x,y 处有一个坦克并旋转了 r 并且你用
ctx.translate(x,y);
ctx.rotate(r);
// render the tank
ctx.fillRect(-20,-10,40,20); // rotated about center
Run Code Online (Sandbox Code Playgroud)
变换包含我们进行计算所需的一切,我们需要做的就是反转它,然后将项目符号与反转矩阵相乘
var tankInvMatrix = ctx.getTransform().invertSelf(); // get the inverted matrix
Run Code Online (Sandbox Code Playgroud)
子弹在 bx,因此创建一个DOMPoint
var bullet = new DOMPoint(bx,by);
Run Code Online (Sandbox Code Playgroud)
然后对于每个坦克用DOMMatrix.transformPoint变换子弹
var relBullet = tankInvMatrix.transformPoint(bullet); // transform the point
// returning the bullet
// relative to the tank
Run Code Online (Sandbox Code Playgroud)
现在只需在坦克局部坐标空间中进行测试
if(relBullet.x > -20 && relBullet.x < 20 && relBullet.x > -10 && relBullet.x < 10){
/// bullet has hit the tank
}
Run Code Online (Sandbox Code Playgroud)
好吧,直到成为常态之前,您必须走很长的路。使用相同的 x,y,r 用于坦克,bx,by 用于子弹。
// create a vector aligned to the tanks direction
var xdx = Math.cos(r);
var xdy = Math.sin(r);
// set the 2D API to the tank location and rotation
ctx.setTransform(xdx,xdy,-xdy,xdx,x,y); // create the transform for the tank
// draw the tank
ctx.fillRect(-20,-10,40,20); // rotated about center
// create inverted matrix for the tank
// Only invert the tank matrix once per frame
var d = xdx * xdx - xdy * -xdy;
var xIx = xdx / d;
var xIy = -xdy / d;
// I am skipping c,d of the matrix as it is perpendicular to a,b
// thus c = -b and d = a
var ix = (-xdy * y - xdx * x) / d;
var iy = -(xdx * y - xdy * x) / d;
// For each bullet per tank
// multiply the bullet with the inverted tank matrix
// bullet local x & y
var blx = bx * xIx - by * xIy + ix;
var bly = bx * xIy + by * xIx + iy;
// and you are done.
if(blx > -20 && blx < 20 && bly > -10 && bly < 10){
// tank and bullet are one Kaaboommmm
}
Run Code Online (Sandbox Code Playgroud)
太多的底片,xdx,xdy 等让我无法查看我是否正确(结果我在行列式中输入了错误的符号)所以这里有一个快速演示来展示它的实际效果。
使用鼠标在坦克身上移动,它会显示它被击中为红色。您可以轻松地将其扩展以撞击坦克的运动部件。您只需要对炮塔进行逆变换即可在局部空间中获取子弹来进行测试。
添加代码以阻止坦克作为交叉画布边缘在视觉上弹出和弹出。这是通过OFFSET在显示时从每个罐中减去 1来完成的。在通过添加OFFSET到测试坐标进行命中测试时,必须将此偏移量考虑在内。
//x1,y1,x2,y2 is a line
// px,py is a point
// first move line and point relative to the origin
// so that the line and point is a vector
px -= x1;
py -= y1;
x2 -= x1;
y2 -= y1;
var cross = x2 * py - y2 * px;
if(cross < 0){
// point left of line
}else if(cross > 0) {
// point right of line
}else {
// point on the line
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1517 次 |
| 最近记录: |