矩形区域与圆心区域的碰撞检测

这里写图片描述
这是一篇关于矩形与圆形区域碰撞检测的文章。虽然这常常被认为很复杂,但实际上却相当简单。

首先,你也许知道怎样检测圆与点是否碰撞:测试圆心与点的距离是否小于等于圆的半径。

DeltaX = CircleX - PointX;
DeltaY = CircleY - PointY;
return (DeltaX * DeltaX + DeltaY * DeltaY) < (CircleRadius * CircleRadius);
  
  
  • 1
  • 2
  • 3

其实,矩形与圆的碰撞检测的原理和这没多大不同:首先找到矩形中离圆心最近的点,然后判断该点是否在圆内。

如果矩形没有被旋转,则使用如下公式即可找到矩形内离圆心最近的点:

NearestX = Max(RectX, Min(CircleX, RectX + RectWidth));
NearestY = Max(RectY, Min(CircleY, RectY + RectHeight));
  
  
  • 1
  • 2

注意:
1. (RectX, RectY) 对于X轴正向向右,Y轴正向向下时是矩形的左上角。
2. (RectX, RectY) 对于X轴正向向右,Y轴正向向上时是矩形的左下角。

结合以上两个步骤,使用如下3行代码即可检测矩形区域与圆形区域是否碰撞。

DeltaX = CircleX - Max(RectX, Min(CircleX, RectX + RectWidth));
DeltaY = CircleY - Max(RectY, Min(CircleY, RectY + RectHeight));
return (DeltaX * DeltaX + DeltaY * DeltaY) < (CircleRadius * CircleRadius);
  
  
  • 1
  • 2
  • 3

以下是示例程序:
可以用鼠标拖拽以及伸缩矩形和圆,碰撞时会高亮显示。

<canvas id="canvas" width="600" height="300"></canvas>
<script type="text/javascript">
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d");
    var rx1 = 140, ry1 = 100, rx2 = 260, ry2 = 220, cx = 440, cy = 160, cr = 40;
    var mx = 0, my = 0, qx = 0, qy = 0;
    var min = Math.min;
    var max = Math.max;
    function clamp(v, x, y) {
        return v < x ? x : v > y ? y : v;
    }
    function dist(x1, y1, x2, y2) {
        var dx = x1 - x2, dy = y1 - y2;
        return Math.sqrt(dx * dx + dy * dy);
    }
    function p2c(px, py, cx, cy, cr) {
        return dist(px, py, cx, cy) < cr;
    }
    function r2c(rx1, ry1, rx2, ry2, cx, cy, cr) {
        return dist(clamp(cx, rx1, rx2), clamp(cy, ry1, ry2), cx, cy) < cr;
    }
    function p2r(px, py, rx1, ry1, rx2, ry2) {
        return px > rx1 && py > ry1 && px < rx2 && py < ry2;
    }
    context.strokeCircle = function(x, y, r) {
        this.beginPath();
        this.arc(x, y, r, 0, Math.PI * 2, false);
        this.closePath();
        this.stroke();
    }
    var hx, hy, hr = 7; // handle data
    var over = -1, dragging = false;
    function render() {
        var time = 0 | new Date(), x, y;
        updateTime = time + 25;
        //
        context.clearRect(0, 0, 640, 320);
        //
        if (over >= 0 && dragging) {
            x = mx - qx;
            y = my - qy;
            switch (over) {
                case 0:
                    cx += x - hx; qx = mx - hx;
                    cy += y - hy; qy = my - hy;
                    break;
                case 1: cr = max(16, dist(cx, cy, x, y)); break;
                case 2: rx1 = min(x, rx2 - 16); ry1 = min(y, ry2 - 16); break;
                case 3: rx2 = max(x, rx1 + 16); ry1 = min(y, ry2 - 16); break;
                case 4: rx1 = min(x, rx2 - 16); ry2 = max(y, ry1 + 16); break;
                case 5: rx2 = max(x, rx1 + 16); ry2 = max(y, ry1 + 16); break;
                case 6: ry1 = min(y, ry2 - 16); break;
                case 7: ry2 = max(y, ry1 + 16); break;
                case 8: rx1 = min(x, rx2 - 16); break;
                case 9: rx2 = max(x, rx1 + 16); break;
                case 10:
                    rx1 += x - hx;
                    ry1 += y - hy;
                    rx2 += x - hx;
                    ry2 += y - hy;
                    qx = mx - hx;
                    qy = my - hy;
                    break;
            }
        }
        //
        var co = r2c(rx1, ry1, rx2, ry2, cx, cy, cr);
        context.strokeStyle = co ? "red" : "blue";
        context.strokeCircle(cx, cy, cr);
        context.strokeCircle(cx, cy, 1);
        context.strokeRect(rx1, ry1, rx2 - rx1, ry2 - ry1);
        if (!co) {
            x = clamp(cx, rx1, rx2);
            y = clamp(cy, ry1, ry2);
            context.strokeCircle(x, y, 4);
            var dx = x - cx;
            var dy = y - cy;
            var dd = Math.sqrt(dx * dx + dy * dy);
            var csx = cx + dx / dd * cr;
            var csy = cy + dy / dd * cr;
            context.strokeCircle(csx, csy, 4);
            //
            context.beginPath();
            context.moveTo(x, y);
            context.lineTo(csx, csy);
            context.closePath();
            context.stroke();
        }
        //
        if (over >= 0 && !dragging) {
            context.strokeStyle = "red";
            context.strokeCircle(hx, hy, hr);
        }
    }
    var interval = null;
    var redraw = false;
    function update() {
        if (redraw) {
            redraw = false;
            render();
        }
    }
    function hp(i, x, y, c) {
        if (p2c(mx, my, x, y, hr)) {
            hx = x;
            hy = y;
            over = i;
            canvas.style.cursor = c || "move";
            return true;
        } else return false;
    }
    function handleMouse(e) {
        mx = e.offsetX;
        my = e.offsetY;
        if (!dragging) {
            var cdx = mx - cx;
            var cdy = my - cy;
            var cd = dist(mx, my, cx, cy);
            var cex = cx + cdx / cd * cr;
            var cey = cy + cdy / cd * cr;
            if (!((cd < cr - hr && hp(0, mx, my))
                || hp(1, cex, cey)
                || hp(2, rx1, ry1, "nw-resize")
                || hp(3, rx2, ry1, "ne-resize")
                || hp(4, rx1, ry2, "sw-resize")
                || hp(5, rx2, ry2, "se-resize")
                || hp(6, clamp(mx, rx1, rx2), ry1, "n-resize")
                || hp(7, clamp(mx, rx1, rx2), ry2, "s-resize")
                || hp(8, rx1, clamp(my, ry1, ry2), "w-resize")
                || hp(9, rx2, clamp(my, ry1, ry2), "e-resize")
                || hp(10, clamp(mx, rx1, rx2), clamp(my, ry1, ry2))
            )) {
                canvas.style.cursor = "";
                over = -1;
            }
        }
    }
    canvas.onmousemove = function(e) {
        handleMouse(e);
        redraw = true;
    }
    canvas.onmousedown = function(e) {
        e.preventDefault();
        handleMouse(e);
        if (over >= 0) {
            dragging = true;
            qx = mx - hx;
            qy = my - hy;
            redraw = true;
        }
    }
    canvas.onmouseup = function(e) {
        handleMouse(e);
        if (dragging) {
            dragging = false;
            over = -1;
            redraw = true;
        }
    }
    canvas.onmouseover = function(_) {
        if (interval == null) interval = setInterval(update, 10);
    }
    canvas.onmouseout = function(_) {
        if (interval != null) {
            clearInterval(interval);
            interval = null;
        }
    }
    render();
</script>
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170

翻译自: https://yal.cc/rectangle-circle-intersection-test/

这里写图片描述
这是一篇关于矩形与圆形区域碰撞检测的文章。虽然这常常被认为很复杂,但实际上却相当简单。

猜你喜欢

转载自blog.csdn.net/weixin_43712770/article/details/89512080
今日推荐