[英]html5 canvas elastic collision squares
I am re-asking this question since I did not make myself clear in what I wanted in my last question. 我正在重新提出这个问题,因为我在上一个问题中没有说明我想要的内容。
Does anyone know how to do elastic collision or handle collision in Canvas using rectangles? 有没有人知道如何使用矩形在Canvas中进行弹性碰撞或处理碰撞? Or can point me in the right direction?
或者可以指出我正确的方向?
I created a canvas that has multiple square and would like each square to deflect when they touch. 我创建了一个具有多个正方形的画布,并希望每个正方形在触摸时偏转。
Here is a quick fiddle that I put together showing to black buffer canvases http://jsfiddle.net/claireC/Y7MFq/10/ 这是一个快速小提琴,我把它放在一起显示黑色缓冲画布http://jsfiddle.net/claireC/Y7MFq/10/
line 39 is where I started the collision detection and line 59 is where I tried to execute it. 第39行是我开始碰撞检测的地方,第59行是我试图执行它的地方。 I will have more than 3 squares moving around and want them to deflect if/when they touch each other
我将有超过3个正方形移动并希望它们在相互接触时偏转
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d");
context.fillStyle = "#FFA500";
context.fillRect(0, 0, canvas.width, canvas.height);
var renderToCanvas = function (width, height, renderFunction) {
var buffer = document.createElement('canvas');
buffer.width = width;
buffer.height = height;
renderFunction(buffer.getContext('2d'));
return buffer;
};
var drawing = renderToCanvas(100, 100, function (ctx) {
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
});
var drawing2 = renderToCanvas(100, 100, function (ctx) {
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, canvas.width, canvas.height);
});
var x = 0,
y = 0,
x2 = 200,
y2 = 10,
vx = .80,
vy = .80,
vx2 = .80,
vy2 = .80;
function collides(rectA, rectB) {
return !(rectA.x + rectA.width < rectB.x2 ||
rectB.x2 + rectB.width < rectA.x ||
rectA.y + rectA.height < rectB.y2 ||
rectB.y2 + rectB.height < rectA.y);
};
function executeFrame() {
x+=vx;
y+=vy;
x2+=vx2;
y2+=vy2;
if( x < 0 || x > 579) vx = -vx;
if( y < 0 || y > 265) vy = -vy;
if( x2 < 0 || x2 > 579) vx2 = - vx2;
if( y2 < 0 || y2 > 233) vy2 = - vy2;
if(collides(drawing, drawing2)){
//move in different direction
};
context.fillStyle = "#FFA500";
context.fillRect(0, 0, canvas.width, canvas.height);
context.drawImage(drawing, x, y);
context.drawImage(drawing2, x2, y2);
requestAnimationFrame(executeFrame);
}
//start animation
executeFrame();
To do a rectangular collision detection can be more complicated than it perhaps looks. 做矩形碰撞检测可能比看起来更复杂。
It's not just about figuring out if the two rectangles intersects or overlaps, but we also need to know at what angle they collide and what direction they move in order to deflect them properly, ideally transfer "velocity" to each other (mass/energy) and so forth. 这不仅仅是要确定两个矩形是否相交或重叠,而且我们还需要知道它们碰撞的角度以及它们移动的方向以便正确地偏转它们,理想情况下将“速度”相互转移(质量/能量)等等。
This method that I present here will do the following steps: 我在这里介绍的方法将执行以下步骤:
The code for detecting the intersection and angle is as follows, where r1
and r2
are here objects with properties x
, y
, w
and h
. 用于检测交叉点和角度的代码如下,其中
r1
和r2
在这里是具有属性x
, y
, w
和h
。
function collides(r1, r2) {
/// classic intersection test
var hit = !(r1.x + r1.w < r2.x ||
r2.x + r2.w < r1.x ||
r1.y + r1.h < r2.y ||
r2.y + r2.h < r1.y);
/// if intersects, get angle between the two rects to determine hit zone
if (hit) {
/// calc angle
var dx = r2.x - r1.x;
var dy = r2.y - r1.y;
/// for simplicity convert radians to degree
var angle = Math.atan2(dy, dx) * 180 / Math.PI;
if (angle < 0) angle += 360;
return angle;
} else
return null;
}
This function will return an angle or null
which we then use to determine deflection in our loop (that is: the angle is used to determine the hit zone in our case). 此函数将返回一个角度或
null
,然后我们用它来确定循环中的偏转(即:在我们的例子中,角度用于确定命中区域)。 This is needed so that they bounce off in the correct direction. 这是必要的,以便它们以正确的方向反弹。
With just a simple intersection test and deflection you can risk the boxes deflecting like the image on the right, which is not correct for a 2D scenario. 只需进行简单的交叉测试和偏转,您就可以像右侧图像那样使盒子偏转,这对2D场景来说是不正确的。 You want the boxes to continue in the same direction of where there is no impact as in the left.
您希望盒子在与左侧无冲击的方向相同的方向上继续。
Here is how we can determine which velocity vector to reverse (tip: if you want a more physical correct deflection you can let the rectangles "absorb" some of the other's velocity but I won't cover that here): 下面是我们如何确定要反转的速度矢量(提示:如果你想要一个更加物理正确的偏转,你可以让矩形“吸收”另一个的速度,但我不会在这里讨论):
var angle = collides({x: x, y: y, w: 100, h: 100}, /// rect 1
{x: x2, y: y2, w: 100, h: 100}); /// rect 2
/// did we have an intersection?
if (angle !== null) {
/// if we're not already in a hit situation, create one
if (!hit) {
hit = true;
/// zone 1 - right
if ((angle >= 0 && angle < 45) || (angle > 315 && angle < 360)) {
/// if moving in + direction deflect rect 1 in x direction etc.
if (vx > 0) vx = -vx;
if (vx2 < 0) vx2 = -vx2;
} else if (angle >= 45 && angle < 135) { /// zone 2 - bottom
if (vy > 0) vy = -vy;
if (vy2 < 0) vy2 = -vy2;
} else if (angle >= 135 && angle < 225) { /// zone 3 - left
if (vx < 0) vx = -vx;
if (vx2 > 0) vx2 = -vx2;
} else { /// zone 4 - top
if (vy < 0) vy = -vy;
if (vy2 > 0) vy2 = -vy2;
}
}
} else
hit = false; /// reset hit when this hit is done (angle = null)
And that's pretty much it. 这就是它。
The hit
flag is used so that when we get a hit we are marking the "situation" as a hit situation so we don't get internal deflections (which can happen at high speeds for example). 使用
hit
标志,以便当我们得到命中时,我们将“情况”标记为命中情况,因此我们不会获得内部偏转(例如,可能以高速发生)。 As long as we get an angle after hit is set to true we are still in the same hit situation (in theory anyways). 只要我们在命中之后获得一个角度设置为true,我们仍处于相同的命中状态(理论上无论如何)。 When we receive null we reset and are ready for a new hit situation.
当我们收到null时,我们重置并准备好迎接新的命中情况。
Also worth to mention is that the primary rectangle here (whose side we check against) is the first one (the black in this case). 另外值得一提的是这里的主要矩形(我们检查的那一面)是第一个(在这种情况下为黑色)。
If you want to throw in more that two rectangle then I would suggest a different approach than used here when it comes to the rectangles themselves. 如果你想投入更多的那两个矩形,那么我会建议一种与矩形本身不同的方法。 I would recommend creating a rectangle object which is self-contained in regards to its position, size, color and also embeds methods to update velocity, direction and paint.
我建议创建一个矩形对象 ,它在位置,大小,颜色方面是独立的,并且还嵌入了更新速度,方向和绘画的方法。 The rectangle objects could be maintained by a host objects which performs the clearing and calls the objects' update method for example.
矩形对象可以由执行清除的主机对象维护,例如调用对象的更新方法。
To detect collisions you could then iterate the array with these objects to find out which rectangle collided with the current being tested. 为了检测碰撞,您可以使用这些对象迭代数组,以找出哪个矩形与正在测试的电流相撞。 It's important here that you "mark" (using a flag) a rectangle that has been tested as there will always be at least two in a collision and if you test A and then B you will end up reversing the effect of velocity change without using a flag to skip testing of the collision "partner" object per frame.
在这里重要的是你“标记”(使用一个标志)一个经过测试的矩形,因为在碰撞中总是至少有两个,如果你测试A然后B,你最终会反转速度变化的影响而不使用一个标志,用于跳过每帧碰撞“伙伴”对象的测试。
Note : there are special cases not covered here such as collision on exact corners, or where a rectangle is trapped between an edge and the other rectangle (you can use the hit flag mentioned above for the edge tests as well). 注意 :这里没有涉及的特殊情况,例如精确角落的碰撞,或者边缘和另一个矩形之间存在矩形的情况(您也可以使用上面提到的命中标记进行边缘测试)。
I have not optimized any of the code but tried to keep it as simple as I can to make it more understandable. 我没有优化任何代码,但试图尽可能简单,以使其更容易理解。
Hope this helps! 希望这可以帮助!
The answer is actually quite simple: swap the velocities of each block when they collide. 答案实际上非常简单:在碰撞时交换每个区块的速度。 That's it!
而已! Also for your collision test change
RectA.x
to just x
, since they are normal variables given: 另外,对于碰撞测试,将
RectA.x
更改为x
,因为它们是给定的正常变量:
function collides(rectA, rectB) {
return !(x + rectA.width < x2 ||
x2 + rectB.width < x ||
y + rectA.height < y2 ||
y2 + rectB.height < y);
};
And swapping velocities: 并交换速度:
if(collides(drawing, drawing2)){
var t = vx; var t2 = vy;
vx = vx2; vy = vy2;
vx2 = t; vy2 = t2;
};
And after those small changes we have working elastic collisions: http://jsfiddle.net/Y7MFq/11/ 经过那些微小的变化后,我们发生了弹性碰撞: http : //jsfiddle.net/Y7MFq/11/
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.