html5画布弹性碰撞方块 [英] html5 canvas elastic collision squares

查看:30
本文介绍了html5画布弹性碰撞方块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我重新问这个问题,因为我没有在上一个问题中明确说明我想要什么.

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

我创建了一个有多个正方形的画布,并且希望每个正方形在它们接触时偏转.

这是我放在一起展示给黑色缓冲画布的一个快速小提琴 ,它是自包含关于它的位置、大小、颜色,并且还嵌入了更新速度、方向和绘画的方法.矩形对象可以由执行清除和调用对象更新方法的宿主对象维护.

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

总结

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

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

希望这会有所帮助!

I am re-asking this question since I did not make myself clear in what I wanted in my last question.

Does anyone know how to do elastic collision or handle collision in Canvas using rectangles? Or can point me in the right direction?

I created a canvas that has multiple square and would like each square to deflect when they touch.

Here is a quick fiddle that I put together showing to black buffer canvases http://jsfiddle.net/claireC/Y7MFq/10/

line 39 is where I started the collision detection and line 59 is where I tried to execute it. I will have more than 3 squares moving around and want them to deflect if/when they touch each other

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();

解决方案

Rectangular collision detection

To do a rectangular collision detection can be more complicated than it perhaps looks.

It's not just about figuring out if the two rectangles intersects or overlaps, but we also need to know at what angle they collide and what direction they move in order to deflect them properly, ideally transfer "velocity" to each other (mass/energy) and so forth.

This method that I present here will do the following steps:

  • First do a simple intersect detection to find out if they collide at all.
  • If an intersection: calculate the angle between the two rectangle
  • Divide a set primary rectangle into four zones of a circle where zone 1 is right, zone 2 is bottom and so forth.
  • Depending on zone, check in what direction the rectangle is moving, if towards the other rectangle deflect it based on which zone was detected.

Online demo

Version with higher speed here

Detect intersection and calculate angle

The code for detecting the intersection and angle is as follows, where r1 and r2 are here objects with properties x, y, w and 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;
}

This function will return an angle or null which we then use to determine deflection in our loop (that is: the angle is used to determine the hit zone in our case). This is needed so that they bounce off in the correct direction.

Why hit zones?

With just a simple intersection test and deflection you can risk the boxes deflecting like the image on the right, which is not correct for a 2D scenario. You want the boxes to continue in the same direction of where there is no impact as in the left.

Determine collision zone and directions

Here is how we can determine which velocity vector to reverse (tip: if you want a more physical correct deflection you can let the rectangles "absorb" some of the other's velocity but I won't cover that here):

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)

And that's pretty much it.

The hit flag is used so that when we get a hit we are marking the "situation" as a hit situation so we don't get internal deflections (which can happen at high speeds for example). As long as we get an angle after hit is set to true we are still in the same hit situation (in theory anyways). When we receive null we reset and are ready for a new hit situation.

Also worth to mention is that the primary rectangle here (whose side we check against) is the first one (the black in this case).

More than two rectangles

If you want to throw in more that two rectangle then I would suggest a different approach than used here when it comes to the rectangles themselves. I would recommend creating a rectangle object which is self-contained in regards to its position, size, color and also embeds methods to update velocity, direction and paint. The rectangle objects could be maintained by a host objects which performs the clearing and calls the objects' update method for example.

To detect collisions you could then iterate the array with these objects to find out which rectangle collided with the current being tested. It's important here that you "mark" (using a flag) a rectangle that has been tested as there will always be at least two in a collision and if you test A and then B you will end up reversing the effect of velocity change without using a flag to skip testing of the collision "partner" object per frame.

In conclusion

Note: there are special cases not covered here such as collision on exact corners, or where a rectangle is trapped between an edge and the other rectangle (you can use the hit flag mentioned above for the edge tests as well).

I have not optimized any of the code but tried to keep it as simple as I can to make it more understandable.

Hope this helps!

这篇关于html5画布弹性碰撞方块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆