javascript-html5画布弹性碰撞方块

我重新提出这个问题,因为我在上一个问题中没有明确表达自己的意思.

有谁知道如何在矩形中使用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底部,依此类推.
>根据区域,检查矩形向哪个方向移动,如果朝着另一个矩形移动,则根据检测到的区域将其偏转.

Online demo

Version with higher speed here

检测相交并计算角度

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

此函数将返回一个角度或零值,然后我们将使用该角度或角度来确定循环中的挠度(即:该角度用于确定本例中的碰撞区域).这是必需的,以便它们朝正确的方向反弹.

为什么要击中区域?

仅通过简单的相交测试和偏转,您就可以冒着像右图所示的框那样偏转框的风险,这对于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)

就是这样.

使用命中标记,以便当我们受到命中时,将“状况”标记为命中情况,这样就不会出现内部偏斜(例如,可能发生在高速状态下).只要在将击中设置为true之后获得一个角度,我们仍然处于相同的击中情况(无论如何理论上来说).当我们收到null时,我们将重置并为新的命中情况做好准备.

还值得一提的是,此处的主要矩形(我们要检查的那一侧)是第一个矩形(在这种情况下为黑色).

超过两个矩形

如果您想扔掉两个以上的矩形,那么对于矩形本身,我建议使用与此处不同的方法.我建议创建一个rectangle object,它在位置,大小,颜色方面是独立的,并且还嵌入了更新速度,方向和绘画的方法.矩形对象可以由宿主对象维护,这些对象执行清除操作并调用对象的update方法.

为了检测冲突,您可以使用这些对象迭代数组,以找出哪个矩形与被测试的电流发生了碰撞.在这里重要的是您“标记”(使用标记)已测试的矩形,因为在碰撞中将始终至少存在两个矩形,如果先测试A,然后测试B,则最终将反转速度变化的效果,而无需使用一个标志,以跳过每帧碰撞“伙伴”对象的测试.

结论

注意:这里没有涵盖特殊情况,例如在确切的角上发生碰撞,或者在一个边和另一个长方形之间夹有一个矩形(您也可以将上面提到的hit标志用于边测试).

我没有优化任何代码,但尝试使它尽可能简单以使其更易于理解.

希望这可以帮助!

上一篇:【持续更新】一些PS的快捷键使用(看敬伟老师网课)


下一篇:鸿蒙构建系统——gn官方FAQ翻译,以及gn官方文档分享