繁体   English   中英

html5帆布弹性碰撞方块

[英]html5 canvas elastic collision squares

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

有没有人知道如何使用矩形在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();

矩形碰撞检测

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

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

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

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

在线演示

此处速度更快的版本

检测交叉点并计算角度

用于检测交叉点和角度的代码如下,其中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;
}

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

为何命中区域?

示例场景

只需进行简单的交叉测试和偏转,您就可以像右侧图像那样使盒子偏转,这对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时,我们重置并准备好迎接新的命中情况。

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

两个以上的矩形

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

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

结论

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

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

希望这可以帮助!

答案实际上非常简单:在碰撞时交换每个区块的速度。 而已! 另外,对于碰撞测试,将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/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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