html5画布弹性碰撞方块

use*_*111 4 html javascript canvas collision html5-canvas

我正在重新提出这个问题,因为我没有在上一个问题中明确我想要什么。

有谁知道如何使用矩形在 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();
Run Code Online (Sandbox Code Playgroud)

小智 6

矩形碰撞检测

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

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

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

  • 首先做一个简单的相交检测,看看它们是否发生了碰撞。
  • 如果相交:计算两个矩形之间的角度
  • 将一组主要矩形划分为圆形的四个区域,其中区域 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;
}
Run Code Online (Sandbox Code Playgroud)

这个函数将返回一个角度,或者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)
Run Code Online (Sandbox Code Playgroud)

仅此而已。

使用该hit标志是为了当我们被击中时,我们将“情况”标记为击中情况,这样我们就不会出现内部偏转(例如,高速时可能会发生这种情况)。只要我们在 hit 设置为 true 后得到一个角度,我们仍然处于相同的命中情况(理论上无论如何)。当我们收到 null 时,我们会重置并准备好迎接新的命中情况。

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

两个以上的长方形

如果您想放入两个以上的矩形,那么当涉及到矩形本身时,我会建议一种与此处使用的方法不同的方法。我建议创建一个矩形对象,该对象在其位置、大小、颜色方面是自包含的,并且还嵌入了更新速度、方向和绘制的方法。例如,矩形对象可以由执行清除并调用对象的更新方法的宿主对象维护。

要检测碰撞,您可以使用这些对象迭代数组以找出哪个矩形与正在测试的当前碰撞。在这里,重要的是您“标记”(使用标志)一个已经过测试的矩形,因为碰撞中总是至少有两个,如果您测试 A 和 B,您最终将逆转速度变化的效果而不使用跳过每帧碰撞“伙伴”对象测试的标志。

综上所述

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

我没有优化任何代码,但尽量保持简单,使其更易于理解。

希望这可以帮助!