
本教程详细介绍了如何使用 JavaScript 检测线段与圆是否相交。通过避免使用平方根运算,提供了一种高效的相交检测方法。同时,还提供了一个进阶函数,用于计算线段与圆的交点距离,并附带完整的代码示例和演示,帮助开发者理解和应用这些技术。
在 HTML5 Canvas 游戏中,碰撞检测是至关重要的一个环节。本教程将探讨如何使用 JavaScript 来判断一条线段是否与一个圆相交,并提供优化后的代码示例,避免了昂贵的平方根运算,提升性能。
线段与圆相交的判断
核心思想是计算圆心到线段的距离,并与圆的半径进行比较。如果圆心到线段的距离小于圆的半径,则线段与圆相交。为了优化性能,我们避免使用 Math.sqrt() 函数,而是直接比较距离的平方。
以下是一个用于检测线段与圆是否相交的 JavaScript 函数:
立即学习“Java免费学习笔记(深入)”;
function rayInterceptsCircle(ray, circle) { const dx = ray.p2.x - ray.p1.x; const dy = ray.p2.y - ray.p1.y; const u = Math.min(1, Math.max(0, ((circle.x - ray.p1.x) * dx + (circle.y - ray.p1.y) * dy) / (dy * dy + dx * dx))); const nx = ray.p1.x + dx * u - circle.x; const ny = ray.p1.y + dy * u - circle.y; return nx * nx + ny * ny < circle.radius * circle.radius;}
代码解释:
ray: 包含线段端点 p1 和 p2 的对象,例如:{ p1: {x: 0, y: 50}, p2: {x: 300, y: 50} }circle: 包含圆心坐标 x、y 和半径 radius 的对象,例如:{ x: 150, y: 120, radius: 60 }dx 和 dy: 线段的 x 和 y 方向的差值。u: 一个参数,表示圆心投影到线段上的位置,范围在 0 到 1 之间。Math.min(1, Math.max(0, …)) 确保 u 的值在 0 和 1 之间,这意味着我们只考虑线段上的点,而不是线段的延长线。nx 和 ny: 圆心到线段上最近点的向量。*`nx nx + ny ny circle.radius**: 判断圆心到线段的距离的平方是否小于圆半径的平方。避免了使用Math.sqrt()` 函数,提高了性能。
计算线段与圆的交点距离 (进阶)
如果你需要知道线段与圆的交点距离,可以使用以下函数:
function rayDist2Circle(ray, circle) { const dx = ray.p2.x - ray.p1.x; const dy = ray.p2.y - ray.p1.y; const vcx = ray.p1.x - circle.x; const vcy = ray.p1.y - circle.y; var v = (vcx * dx + vcy * dy) * (-2 / Math.hypot(dx, dy)); const dd = v * v - 4 * (vcx * vcx + vcy * vcy - circle.radius * circle.radius); if (dd <= 0) { return Infinity; } return (v - Math.sqrt(dd)) / 2;}
代码解释:
如果线段没有与圆相交,函数返回 Infinity。如果线段与圆相交,函数返回线段的起点到交点的距离。Math.hypot(dx, dy) 计算线段的长度,避免了平方根运算。
完整示例代码
Line Circle Intersection canvas { position: absolute; top: 0px; left: 0px; } const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); const TAU = Math.PI * 2; requestAnimationFrame(renderLoop); var W = canvas.width, H = canvas.height; const Point = (x, y) => ({x, y}); const Ray = (p1, p2) => ({p1, p2}); const Circle = (p, radius) => ({x: p.x, y: p.y, radius}); function drawRayLeng(ray, len) { ctx.beginPath(); ctx.lineTo(ray.p1.x, ray.p1.y); if (len < Infinity) { const dx = ray.p2.x - ray.p1.x; const dy = ray.p2.y - ray.p1.y; const scale = len / Math.hypot(dx, dy); ctx.lineTo(ray.p1.x + dx * scale , ray.p1.y + dy * scale); } else { ctx.lineTo(ray.p2.x, ray.p2.y); } ctx.stroke(); } function drawRay(ray) { ctx.beginPath(); ctx.lineTo(ray.p1.x, ray.p1.y); ctx.lineTo(ray.p2.x, ray.p2.y); ctx.stroke(); } function drawCircle(circle) { ctx.beginPath(); ctx.arc(circle.x, circle.y, circle.radius, 0, TAU); ctx.stroke(); } function rayInterceptsCircle(ray, circle) { const dx = ray.p2.x - ray.p1.x; const dy = ray.p2.y - ray.p1.y; const u = Math.min(1, Math.max(0, ((circle.x - ray.p1.x) * dx + (circle.y - ray.p1.y) * dy) / (dy * dy + dx * dx))); const nx = ray.p1.x + dx * u - circle.x; const ny = ray.p1.y + dy * u - circle.y; return nx * nx + ny * ny < circle.radius * circle.radius; } function rayDist2Circle(ray, circle) { const dx = ray.p2.x - ray.p1.x; const dy = ray.p2.y - ray.p1.y; const vcx = ray.p1.x - circle.x; const vcy = ray.p1.y - circle.y; var v = (vcx * dx + vcy * dy) * (-2 / Math.hypot(dx, dy)); const dd = v * v - 4 * (vcx * vcx + vcy * vcy - circle.radius * circle.radius); if (dd <= 0) { return Infinity; } return (v - Math.sqrt(dd)) / 2; } const mouse = {x : 0, y : 0} function mouseEvents(e){ mouse.x = e.pageX; mouse.y = e.pageY; } document.addEventListener("mousemove", mouseEvents); const c1 = Circle(Point(150, 120), 60); const r1 = Ray(Point(0, 50), Point(300, 50)); function renderLoop(time) { ctx.clearRect(0, 0, W, H); r1.p1.x = c1.x + Math.cos(time / 5000) * 100; r1.p1.y = c1.y + Math.sin(time / 5000) * 100; r1.p2.x = mouse.x; r1.p2.y = mouse.y; ctx.lineWidth = 0.5; drawCircle(c1); drawRay(r1); ctx.lineWidth = 5; if (rayInterceptsCircle(r1, c1)) { ctx.strokeStyle = "red"; drawRayLeng(r1, rayDist2Circle(r1, c1)); } else { drawRay(r1); } ctx.strokeStyle = "black"; requestAnimationFrame(renderLoop); }
使用方法:
将以上代码复制到 HTML 文件中。使用浏览器打开该 HTML 文件。移动鼠标,线段的终点会跟随鼠标移动。如果线段与圆相交,线段会变成红色,并且只绘制到交点处。
总结
本教程提供了一种高效且易于理解的 JavaScript 方法来检测线段与圆的相交。通过避免使用平方根运算,提高了性能,尤其是在需要进行大量碰撞检测的场景下。同时,还介绍了如何计算线段与圆的交点距离,为更复杂的碰撞处理提供了基础。在实际应用中,可以根据具体需求选择使用哪个函数,并进行适当的调整和优化。
以上就是JavaScript 教程:检测线段与圆的相交的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1510193.html
微信扫一扫
支付宝扫一扫