检测物体碰撞实际上是需要检测物体是否相交,而实际应用中物体的形状大小各异,如果直接对物体的边缘进行碰撞检测,实际计算过程的代价非常高昂。如果物体的数量太多,比如像网络游戏中,通常少则几千用户,多则上万、几十万用户同时在线,而这些碰撞都要通过服务器检测,这样计算的消耗,即使是大型服务器也会崩溃,所以通常不需要十分精确的碰撞检测情况下,使用包围盒算法,即把物体放在一个多边形中,这个多边形就是包围盒。
基于这样一个概念,通常情况下我们就使用了最简单的几种多边形,比如圆形、矩形等。
先来看看如何使用圆形包围盒算法,圆形包围盒算法就是使用圆形作为包围盒将物体包围起来,检测的时候只需要检测两个圆形是否碰撞即可。检测圆形的碰撞比较容易,假设圆 A 的坐标(x1, y1),半径是 r1,圆 B 的坐标(x2, y2),半径是 r2,则如果满足不等式(y2−y1)2+(x2−x1)2≤(r1+r2)2则表示两个圆发生了碰撞,其实就是圆心之间的距离小于两个圆的半径之和即可,由于计算距离需要用到开方运算,效率较低,所以直接比较距离的平方。
关键代码:
function hitTest( source, target ) { /* 源物体和目标物体都包含 x, y 以及 width, height */ return !( Math.pow((source.x - target.x),2) + Math.pow((source.y - target.y),2) > Math.pow((source.r + target.r),2) ); }
完整示例代码:
<!DOCTYPE html> <html lang="en"> <head> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> <meta charset="UTF-8"> <title>盒包围碰撞算法-圆形</title> <style> #stage { border: 1px solid lightgray; } </style> </head> <body> <h1>是否碰撞:<span>否</span></h1> <canvas></canvas> </body> <script> window.onload = function () { var stage = document.querySelector('#stage'), ctx = stage.getContext('2d'); stage.width = 400; stage.height = 400; document.onkeydown = function (event) { var e = event || window.event || arguments.callee.caller.arguments[0]; //根据地图数组碰撞将测 switch (e.keyCode) { case 37: console.log("Left"); if (balls[0].x > 0) { balls[0].x -= 2; } break; case 38: console.log("Top"); if (balls[0].y > 0) { balls[0].y -= 2; } break; case 39: console.log("Right"); if (balls[0].x < stage.width) { balls[0].x += 2; } break; case 40: console.log("Bottom"); if (balls[0].y < stage.height) { balls[0].y += 2; } break; default: return false; } }; stage.addEventListener('click', function (event) { var x = event.clientX - stage.getBoundingClientRect().left; var y = event.clientY - stage.getBoundingClientRect().top; balls[0].x = x - balls[0].r/2; balls[0].y = y - balls[0].r/2; }); var player = { x: stage.width / 2 - 20, y: stage.height / 2 - 20, r: 40, c: "red" },balls = []; balls.push(player); for (var i = 0; i < 10; i++) { var ball = { x: 60 * i, y: 60 * i, r: 40, c: "blue" }; balls.push(ball); } function createBall(x, y, r, c) { ctx.beginPath(); ctx.fillStyle = c; ctx.arc(x, y, r, 0, Math.PI*2); ctx.fill(); } function hitTest( source, target ) { /* 源物体和目标物体都包含 x, y 以及 width, height */ return !( Math.pow((source.x - target.x),2) + Math.pow((source.y - target.y),2) > Math.pow((source.r + target.r),2) ); } function update() { ctx.globalAlpha=1; ctx.clearRect(0, 0, 400, 400); document.querySelector('.hitTest').innerHTML = "否"; for (var i = 1, len = balls.length; i < len; i++) { createBall(balls[i].x, balls[i].y, balls[i].r, balls[i].c); var flag = hitTest(balls[0],balls[i]); if(flag){ document.querySelector('.hitTest').innerHTML = "是"; ctx.globalAlpha=0.5; } } createBall(balls[0].x, balls[0].y, balls[0].r, balls[0].c); requestAnimationFrame(update); } update(); }; </script> </html>
这里使用在线HTML/CSS/JavaScript代码运行工具: 测试上述代码运行结果如下:
github地址:https://github.com/krapnikkk/JS-gameMathematics
更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《JavaScript数学运算用法总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript数组操作技巧总结》、《JavaScript排序算法总结》、《JavaScript遍历算法与技巧总结》、《JavaScript查找算法技巧总结》及《JavaScript错误与调试技巧总结》