JavaScript实现碰撞检测(分离轴定理)
概述
详细
一、准备工作,熟悉分离轴定理 算法原理
(翻译至http://www.sevenson.com.au/actionscript/sat/)
从根本上来讲,分离轴定理(以及其他碰撞算法)的用途就是去检测并判断两个图形之间是否有间隙。分离轴定理中用到的方法使算法本身显得十分独特。
我所听到过分离轴定理的最好类比方式是这样的:
假想你拿一个电筒从不同的角度照射到两个图形上,那么会有怎样的一系列的阴影投射到它们之后的墙壁上呢?

如果你用这个方式从每一个角度上对这两个图形进行处理,并都找不到任何的间隙,那么这两个图形就一定接触。如果你找到了一个间隙,那么这两个图形就显而易见地没有接触。
从编程的角度来讲,从每个可能的角度上去检测会使处理变得十分密集。不过幸运的是,由于多边形的性质,你只需要检测其中几个关键的角度。
你需要检测的角度数量就正是这个多边形的边数。也就是说,你所需检测的角度最大数量就是你要检测碰撞的两个多边形边数之和。举个例子,两个五边形就需要检测10个角度。

这是一个简易但比较啰嗦的方法,以下是基本的步骤:
步骤一:从需要检测的多边形中取出一条边,并找出它的法向量(垂直于它的向量),这个向量将会是我们的一个“投影轴”。

步骤二:循环获取第一个多边形的每个点,并将它们投影到这个轴上。(记录这个多边形投影到轴上的最高和最低点)

步骤三:对第二个多边形做同样的处理。

步骤四:分别得到这两个多边形的投影,并检测这两段投影是否重叠。

如果你发现了这两个投影到轴上的“阴影”有间隙,那么这两个图形一定没有相交。但如果没有间隙,那么它们则可能接触,你需要继续检测直到把两个多边形的每条边都检测完。如果你检测完每条边后,都没有发现任何间隙,那么它们是相互碰撞的。
这个算法基本就是如此的。
顺带提一下,如果你记录了哪个轴上的投影重叠值最小(以及重叠了多少),那么你就能用这个值来分开这两个图形。
那么如何处理圆呢?
在分离轴定理中,检测圆与检测多边形相比,会有点点奇异,但仍然是可以实现的。
最值得注意的是,圆是没有任何的边,所以是没有明显的用于投影的轴。但它有一条“不是很明显的”的投影轴。这条轴就是途经圆心和多边形上离圆心最近的顶点的直线。

在这以后就是按套路遍历另一个多边形的每条投影轴,并检测是否有投影重叠。
噢,对了,万一你想知道如何把圆投影到轴上,那你只用简单地把圆心投影上去,然后加上和减去半径就能得到投影长度了。
二、代码解析
1、html代码如下:
<canvas width="800" height="500" id="mycanvas">Loading...</canvas>
<div id="select-box"></div>
2、main.js主要是控制位移以及圆圈大小,代码如下:
var SHAPE_SIZE = 80, SHAPE_HANDLE_SIZE = 10;
var CANVAS_WIDTH, CANVAS_HEIGHT; var renderer;
var shA = null, shB = null; window.onload = function () {
var canvasTag = document.getElementById("mycanvas");
var canvas = canvasTag.getContext("2d"); CANVAS_WIDTH = canvasTag.width;
CANVAS_HEIGHT = canvasTag.height; renderer = new Renderer(canvas); setInterval(function () {
renderer.loopDraw();
}, 30); MouseEvent.addEvents(canvasTag); main();
}; function main () {
UIUtils.createSelect(); shA = UIUtils.createShape(150, 250, "shA-select");
renderer.add(shA); shB = UIUtils.createShape(540, 250, "shB-select");
renderer.add(shB);
} function getPolygonVertices (edges, r) {
var ca = 0, aiv = 360 / edges, ata = Math.PI / 180, list = new Array(); for (var k = 0; k < edges; k++) {
var x = Math.cos(ca * ata) * r,
y = Math.sin(ca * ata) * r; list.push(new Vec2(x, y)); ca += aiv;
} return list;
}
SHAPE_SIZE = 80, SHAPE_HANDLE_SIZE = 10 (SHAPE_SIZE设置外圆环大小,SHAPE_HANDLE_SIZE设置内圆大小),UIUtils.createShape(150, 250, "shA-select")设置第一个圆的x轴、y轴位移,还有外圆环选择的形状是什么,UIUtils.createShape(540, 250, "shB-select")设置第二个圆的x轴、y轴位移,还有外圆环选择的形状是什么。
3、SAT.js主要是控制拖动圆点时,外框的颜色等,部分代码如下:
var SAT = (function () {
function testCollision (A, B) {
var res, color = "#333333";
if (A.type == "polygon" && B.type == "polygon") {
res = polygonsCollisionTest(A, B);
} else if (A.type == "circle" && B.type == "circle") {
res = circlesCollisionTest(A, B);
} else {
var c, p;
if (A.type == "circle") {
c = A;
p = B;
} else {
c = B;
p = A;
}
res = circlePolygonCollisionTest(c, p);
}
if (res) {
color = "#FF0000";
}
A.color = B.color = color;
}
4、Circle.js是控制第二个圆的外框颜色等,代码如下:
function Circle (r) {
this.objectIndex = Renderer.objectIndex++;
this.type = "circle";
this.r = r;
this.x = 0;
this.y = 0;
this.color = "#333333";
}
Circle.prototype = {
draw : function (c) {
c.arc(0, 0, this.r, 0, Math.PI * 2);
},
getProjection : function (axis) {
var pro = Vec2.dot(new Vec2(this.x, this.y), axis) / axis.length();
return {min : pro - this.r, max : pro + this.r};
}
};
5、Polygon.js是控制第一个圆的外框颜色等,代码如下:
function Polygon (list) {
this.objectIndex = Renderer.objectIndex++;
this.type = "polygon";
this.vertices = list;
this.x = 0;
this.y = 0;
this.color = "#333333";
}
Polygon.prototype = {
getRootCoordinate : function () {
var list = this.vertices, res = new Array();
for (var i = 0, l = list.length; i < l; i++) {
var coord = list[i];
res.push(new Vec2(coord.x + this.x, coord.y + this.y));
}
return res;
},
draw : function (c) {
var list = this.vertices;
if (list.length <= 1) {
return;
}
c.moveTo(list[0].x, list[0].y);
for (var i = 1, l = list.length; i < l; i++) {
var coord = list[i];
c.lineTo(coord.x, coord.y);
}
c.closePath();
},
getSides : function () {
var list = this.vertices,
l = list.length,
res = new Array();
if (l >= 3) {
for (var j = 1, pre = list[0]; j < l; j++) {
var p = list[j];
res.push(Vec2.substract(p, pre));
pre = p;
}
res.push(Vec2.substract(list[0], list[l - 1]));
}
return res;
},
getProjection : function (axis) {
var list = this.getRootCoordinate(), min = null, max = null;
for (var i = 0, l = list.length; i < l; i++) {
var p = list[i];
var pro = Vec2.dot(p, axis) / axis.length();
if (min === null || pro < min) {
min = pro;
}
if (max === null || pro > max) {
max = pro;
}
}
return {min : min, max : max};
},
getNearestPoint : function (p1) {
var list = this.getRootCoordinate(), rP = list[0], minDis = Vec2.distance(p1, rP);
for (var i = 1, l = list.length; i < l; i++) {
var p2 = list[i], d = Vec2.distance(p1, p2);
if (d < minDis) {
minDis = d;
rP = p2;
}
}
return rP;
}
};
6、Renderer.js是控制整体外框的属性,比如颜色边框等,代码如下:
Renderer.prototype = {
loopDraw : function () {
var c = this.canvas;
c.fillStyle = "#ff0000";
c.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
for (var i = 0, l = this.displayList.length; i < l; i++) {
var o = this.displayList[i];
c.save();
c.translate(o.x, o.y);
c.beginPath();
c.globalAlpha = 0.6;
c.arc(0, 0, SHAPE_HANDLE_SIZE, 0, Math.PI * 2);
c.fillStyle = "#0000FF";
c.fill();
c.beginPath();
c.globalAlpha = 1;
o.draw(c);
c.strokeStyle = o.color;
c.lineWidth = 2;
c.stroke();
c.restore();
}
},
add : function (o) {
this.displayList.push(o);
},
remove : function (o) {
for (var i = 0, l = this.displayList.length; i < l; i++) {
var child = this.displayList[i];
if (child.objectIndex == o.objectIndex) {
this.displayList.splice(i, 1);
break;
}
}
}
};
三、文件以及演示截图
1、文件截图

2、演示截图

3、双击index.html文件即可运行看效果
四、兼容性
兼容主流浏览器
注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权
JavaScript实现碰撞检测(分离轴定理)的更多相关文章
- javascript判断碰撞检测
javascript判断碰撞检测 点与矩形的碰撞检测 <pre> /** * * @param x1 点 * @param y1 点 * @param x2 矩形view x * @par ...
- JavaScript动画-碰撞检测
▓▓▓▓▓▓ 大致介绍 碰撞检测是指在页面中有多个元素时,拖拽一个元素会出现碰撞问题,碰撞检测是以模拟拖拽和磁性吸附中的范围限定为基础的 效果:碰撞检测 ▓▓▓▓▓▓ 碰撞检测 先来看看碰撞检测的原理 ...
- javascript 实现加法分离。 plus(3)(4); // => 得到 7
原文地址:http://cnodejs.org/topic/5230d5f0101e574521c86ff4 JavaScript 的设计是典型的函数式的编程范式匿名函数 JSON数据本身就是字符串, ...
- 使用jquery-tmpl使JavaScript与HTML分离
背景:由于对JavaScript字符串拼接JavaScript变量产生了反感,也想用用JavaScript模板库,看了几个,由于时间原因选择了jQuery.tmpl.js,因为Visual Studi ...
- javascript九宫格碰撞检测
JS九宫格碰撞检测这个东西 以前学过 这次主要是做面试项目web版的win10 桌面图片需要用碰撞检测 再写的时候竟然完全忘记了碰撞检测原理 和怎么写 综合来说还是写的太少 今天再学了一下 理 ...
- HTML5 Canvas核心技术—图形、动画与游戏开发.pdf8
第6章 精灵 精灵(sprite),它是一种可以集成入动画之中的图像对象,赋予它们各种行为,精灵并非Canvas API的一部分,,但都是从它衍生而来 本章将会实现三种设计模式:策略模式(精灵与绘制器 ...
- “等一下,我碰!”——常见的2D碰撞检测
转自:https://aotu.io/notes/2017/02/16/2d-collision-detection/ 在 2D 环境下,常见的碰撞检测方法如下: 外接图形判别法 轴对称包围盒(Axi ...
- JavaScript事件---事件入门
内容提纲: 1.事件介绍 2.内联模型 3.脚本模型 4.事件处理函数 JavaScript事件是由访问Web页面的用户引起的一系列操作,例如:用户点击.当用户执行某些操作的时候,再去执行一系列代码. ...
- javascript中的cookie,以及事件解析
Cookie: 它的意思是在本地的客户端的磁盘上以很小的文件形式保存数据,Cookie的处理原则上需要在服务器环境下运行,目前Chrome不可以在客户端操作Cookie,其他浏览器均可以, Coo ...
随机推荐
- win7 启动管理器修改默认启动项
最近给我的超级本做了系统备份,以防万一,但是备份完成后,系统启动的时候总会首先进入Windows启动管理器,且默认启动项是Ghost,还需要选择一下才能进入Win7系统,如何解决这个问题呢? 方案一: ...
- c++模板类成员的声明和定义
c++模板类成员的声明和定义应该都放在*.h中,有普通类不一样. 如果定义放在*.cpp中,最终链接时,会报方法undefined错误. 参考:http://users.cis.fiu.edu/~we ...
- 中国计算机学会CCF推荐国际学术会议和期刊目录(PDF版,2015年)
total: CCF推荐国际学术会议和期刊目录(下载) parts: 点击下载: 计算机体系结构并行与分布计算存储系统.pdf 点击下载: 计算机网络.pdf 点击下载: 网络与信息安全.pdf ...
- 从原型模式(Prototype Pattern)到 Clone
前面提到抽象工厂的实现,这里说说抽象工厂的原型实现,与工厂方法的实现不同,原型实现有他自己的优点和缺点 原型的优点: 1. 效率:clone是native方法,比new的效率高,当使用复杂循环嵌套对象 ...
- Asp.Net Core App 部署故障示例 1
相关阅读:Windows + IIS 环境部署Asp.Net Core App 1. HTTP Error 502.5 – Process Failure 环境 Windows Server 201 ...
- OpenCV特征检测教程
http://docs.opencv.org/2.4/doc/tutorials/features2d/table_of_content_features2d/table_of_content_fea ...
- Nginx配置文件(nginx.conf)配置具体解释
欢迎扫码增加Java高知群交流 Nginx的配置文件nginx.conf配置具体解释例如以下: user nginx nginx ; Nginx用户及组:用户 组. window下不指定 wo ...
- Android 当修改一些代码时,使用什么编译命令可以最有效率
前言 欢迎大家我分享和推荐好用的代码段~~ 声明 欢迎转载,但请保留文章原始出处: CSDN:http://www.csdn.net ...
- Oracle Agile PLM Web Services 的实现
Oracle 的产品Agile PLM内置了许多Web Services,其他系统可以通过Web Servcies实现对Agile PLM系统资源的访问.快速学会使用的方法,是去Oracle的官网下载 ...
- iframe之onload事件小记
项目上做了一个具有wizard(向导)功能的菜单导航页面,子页面的引入通过主页面上iframe的src属性切换实现.为了有个良好的交互体验,每次更新iframe的src时,主页面上都显示一个模态的lo ...