Javascript html5画布弹性碰撞方块

Javascript html5画布弹性碰撞方块,javascript,html,canvas,html5-canvas,collision,Javascript,Html,Canvas,Html5 Canvas,Collision,我再次提出这个问题,因为我在上一个问题中没有明确说明我想要什么 有人知道如何使用矩形在画布上进行弹性碰撞或处理碰撞吗?或者你能给我指出正确的方向吗 我创建了一个有多个正方形的画布,希望每个正方形在接触时都发生偏转 这是一把我拼凑起来的快速小提琴,展示给黑色缓冲画布 第39行是我开始碰撞检测的地方,第59行是我尝试执行碰撞检测的地方。我将有3个以上的方块移动,如果它们彼此接触,我希望它们偏转 var canvas = document.getElementById("canvas"), c

我再次提出这个问题,因为我在上一个问题中没有明确说明我想要什么

有人知道如何使用矩形在画布上进行弹性碰撞或处理碰撞吗?或者你能给我指出正确的方向吗

我创建了一个有多个正方形的画布,希望每个正方形在接触时都发生偏转

这是一把我拼凑起来的快速小提琴,展示给黑色缓冲画布

第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();
var canvas=document.getElementById(“canvas”),
context=canvas.getContext(“2d”);
context.fillStyle=“#FFA500”;
context.fillRect(0,0,canvas.width,canvas.height);
var renderToCanvas=函数(宽度、高度、renderFunction){
var buffer=document.createElement('canvas');
buffer.width=宽度;
缓冲区高度=高度;
renderFunction(buffer.getContext('2d');
返回缓冲区;
};
变量绘图=渲染画布(100,100,函数(ctx){
ctx.fillStyle=“#000”;
ctx.fillRect(0,0,canvas.width,canvas.height);
});
var drawing2=renderToCanvas(100100,函数(ctx){
ctx.fillStyle=“蓝色”;
ctx.fillRect(0,0,canvas.width,canvas.height);
});
var x=0,
y=0,
x2=200,
y2=10,
vx=.80,
vy=.80,
vx2=0.80,
vy2=0.80;
函数冲突(rectA,rectB){
返回!(rectA.x+rectA.width579)vx=-vx;
如果(y<0 | | y>265)vy=-vy;
如果(x2<0 | | x2>579)vx2=-vx2;
如果(y2<0 | | y2>233)vy2=-vy2;
如果(碰撞(绘图、绘图2)){
//向不同的方向移动
};
context.fillStyle=“#FFA500”;
context.fillRect(0,0,canvas.width,canvas.height);
背景。绘图图像(绘图,x,y);
上下文。drawImage(drawing2,x2,y2);
requestAnimationFrame(ExecuteName);
}
//启动动画
executeFrame();

答案其实很简单:在碰撞时交换每个块的速度。就这样!此外,对于碰撞测试,请将
RectA.x
更改为仅
x
,因为它们是给定的正常变量:

    function collides(rectA, rectB) {
      return !(x + rectA.width < x2 ||
       x2 + rectB.width < x ||
       y + rectA.height < y2 ||
       y2 + rectB.height < y);
      }; 
在这些小的变化之后,我们有了弹性碰撞:

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

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

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

  • 首先做一个简单的相交检测,看看它们是否发生碰撞
  • 如果相交:计算两个矩形之间的角度
  • 将设置的主矩形划分为圆形的四个区域,其中区域1为右侧,区域2为底部,依此类推
  • 根据区域,检查矩形的移动方向,如果朝向另一个矩形,则根据检测到的区域使其偏转
➔ ➔ 检测交点并计算角度 用于检测交点和角度的代码如下所示,其中
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;
}
差不多就是这样

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

同样值得一提的是,这里的主矩形(我们检查其一侧)是第一个(本例中为黑色)

两个以上的矩形 如果你想加入两个以上的矩形,那么我建议使用一种不同于这里使用的方法来处理矩形本身。我建议创建一个位置、大小、颜色都是独立的,并且还嵌入了更新速度、方向和绘制的方法。矩形对象可以由执行清除并调用对象的更新方法的主机对象来维护

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

总之 注意:此处未涉及特殊情况,例如精确角上的碰撞,或矩形被困在边和其他矩形之间(您也可以使用上面提到的命中标志进行边测试)

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


希望这有帮助

如果你在你贴的(,ref)小提琴上看到第二次碰撞,你可以看到它们在角度上没有正确偏转。除此之外,这是一种很好且简单的检测方法。这是因为该公式是基于一维的解决方案,如果将碰撞视为跨越一维线,则碰撞是正确的。能量
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;
}
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)