html5帆布弹性碰撞方块

时间:2014-01-24 06:03:29

标签: javascript html5 canvas html5-canvas collision

我正在重新提出这个问题,因为我在上一个问题中没有说明我想要的内容。

有没有人知道如何使用矩形在Canvas中进行弹性碰撞或处理碰撞?或者可以指出我正确的方向?

我创建了一个具有多个正方形的画布,并且希望每个正方形在触摸时都会偏转。

这是一个快速的小提琴,我把它放在一起展示给黑色缓冲画布http://jsfiddle.net/claireC/Y7MFq/10/

第39行是我开始碰撞检测的地方,第59行是我尝试执行它的地方。我将有超过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();

2 个答案:

答案 0 :(得分:4)

矩形碰撞检测

进行矩形碰撞检测可能比看起来更复杂。

这不仅仅是要确定两个矩形是否相交或重叠,而且我们还需要知道它们碰撞的角度以及它们移动的方向以便正确地偏转它们,理想情况下将“速度”相互转移(质量) /能量)等等。

我在这里介绍的方法将执行以下步骤:

  • 首先进行简单的交叉检测,以确定它们是否发生碰撞。
  • 如果交叉点:计算两个矩形之间的角度
  • 将一个主要矩形划分为一个圆圈的四个区域,其中区域1为右,区域2为底部,依此类推。
  • 根据区域,检查矩形移动的方向,如果朝向另一个矩形,则根据检测到的区域对其进行偏转。

Online demo

Version with higher speed here

检测交叉点并计算角度

用于检测交叉点和角度的代码如下所示,其中r1r2是此处具有属性xyw的对象, 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;
}

此函数将返回角度null,然后我们用它来确定循环中的偏转(即:在我们的例子中,角度用于确定命中区域)。这是必要的,以便它们以正确的方向反弹。

为什么要击中区域?

Example scenario

只需进行简单的交叉测试和偏转,您就可以像右侧图像那样使盒子偏转,这对2D场景来说是不正确的。您希望这些方框在与左侧没有影响的方向相同的方向上继续。

确定碰撞区域和方向

以下是我们如何确定要反转的速度矢量(提示:如果你想要更多的物理正确的偏转,你可以让矩形“吸收”另一个的速度,但我不会覆盖这里):

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)

这就是它。

使用hit标志,以便当我们得到命中时,我们将“情况”标记为命中情况,因此我们不会获得内部偏转(例如,可能在高速时发生)。只要我们在命中之后获得一个角度设置为true,我们仍处于相同的命中状态(理论上无论如何)。当我们收到null时,我们重置并准备好迎接新的命中情况。

另外值得一提的是这里的主要矩形(我们检查的那一面)是第一个(在这种情况下是黑色)。

两个以上的矩形

如果你想投入更多的两个矩形,那么我建议采用与此处使用的方法不同的方法来处理矩形本身。我建议创建一个rectangle object,它的位置,大小,颜色都是独立的,并且还嵌入了更新速度,方向和颜色的方法。矩形对象可以由执行清除的主机对象维护,例如调用对象的更新方法。

要检测碰撞,您可以使用这些对象迭代数组,以找出哪个矩形与正在测试的电流相撞。在这里重要的是你“标记”(使用一个标志)一个经过测试的矩形,因为在碰撞中总是至少有两个,如果你测试A然后B,你最终会反转速度变化的影响而不使用一个标志,用于跳过每帧碰撞“伙伴”对象的测试。

总结

注意:此处未涉及特殊情况,例如精确角落碰撞,或者边缘与另一个矩形之间存在矩形(您可以使用上面提到的命中标记)边缘测试也是如此)。

我没有对任何代码进行优化,但试图尽可能简单地使其更容易理解。

希望这有帮助!

答案 1 :(得分:0)

答案实际上非常简单:在碰撞时交换每个区块的速度。而已!另外,对于您的碰撞测试,请将RectA.x更改为x,因为它们是给定的正常变量:

    function collides(rectA, rectB) {
      return !(x + rectA.width < x2 ||
       x2 + rectB.width < x ||
       y + rectA.height < y2 ||
       y2 + rectB.height < y);
      }; 

交换速度:

        if(collides(drawing, drawing2)){
            var t = vx; var t2 = vy;
            vx = vx2; vy = vy2;
            vx2 = t; vy2 = t2;
        };

经过那些微小的变化后,我们发生了弹性碰撞:http://jsfiddle.net/Y7MFq/11/