简体   繁体   English

html5帆布弹性碰撞方块

[英]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();

Rectangular collision detection 矩形碰撞检测

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: 我在这里介绍的方法将执行以下步骤:

  • First do a simple intersect detection to find out if they collide at all. 首先进行简单的交叉检测,以确定它们是否完全碰撞。
  • If an intersection: calculate the angle between the two rectangle 如果是交点:计算两个矩形之间的角度
  • Divide a set primary rectangle into four zones of a circle where zone 1 is right, zone 2 is bottom and so forth. 将一组主矩形划分为一个圆的四个区域,其中区域1为右,区域2为底部,依此类推。
  • Depending on zone, check in what direction the rectangle is moving, if towards the other rectangle deflect it based on which zone was detected. 根据区域,检查矩形移动的方向,如果朝向另一个矩形,则根据检测到的区域对其进行偏转。

Online demo 在线演示

Version with higher speed here 此处速度更快的版本

Detect intersection and calculate angle 检测交叉点并计算角度

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 . 用于检测交叉点和角度的代码如下,其中r1r2在这里是具有属性xywh

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. 这是必要的,以便它们以正确的方向反弹。

Why hit zones? 为何命中区域?

示例场景

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. 您希望盒子在与左侧无冲击的方向相同的方向上继续。

Determine collision zone and directions 确定碰撞区域和方向

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). 另外值得一提的是这里的主要矩形(我们检查的那一面)是第一个(在这种情况下为黑色)。

More than two rectangles 两个以上的矩形

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,你最终会反转速度变化的影响而不使用一个标志,用于跳过每帧碰撞“伙伴”对象的测试。

In conclusion 结论

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM