概述

分离轴定理是一项用于检测碰撞的算法。其适用范围较广,涵盖检测圆与多边形,多边形与多边形的碰撞;缺点在于无法检测凹多边形的碰撞。本demo使用Js进行算法实现,HTML5 canvas进行渲染。

详细

一、准备工作,熟悉分离轴定理 算法原理

(翻译至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实现碰撞检测(分离轴定理)的更多相关文章

  1. javascript判断碰撞检测

    javascript判断碰撞检测 点与矩形的碰撞检测 <pre> /** * * @param x1 点 * @param y1 点 * @param x2 矩形view x * @par ...

  2. JavaScript动画-碰撞检测

    ▓▓▓▓▓▓ 大致介绍 碰撞检测是指在页面中有多个元素时,拖拽一个元素会出现碰撞问题,碰撞检测是以模拟拖拽和磁性吸附中的范围限定为基础的 效果:碰撞检测 ▓▓▓▓▓▓ 碰撞检测 先来看看碰撞检测的原理 ...

  3. javascript 实现加法分离。 plus(3)(4); // => 得到 7

    原文地址:http://cnodejs.org/topic/5230d5f0101e574521c86ff4 JavaScript 的设计是典型的函数式的编程范式匿名函数 JSON数据本身就是字符串, ...

  4. 使用jquery-tmpl使JavaScript与HTML分离

    背景:由于对JavaScript字符串拼接JavaScript变量产生了反感,也想用用JavaScript模板库,看了几个,由于时间原因选择了jQuery.tmpl.js,因为Visual Studi ...

  5. javascript九宫格碰撞检测

      JS九宫格碰撞检测这个东西 以前学过  这次主要是做面试项目web版的win10 桌面图片需要用碰撞检测 再写的时候竟然完全忘记了碰撞检测原理 和怎么写 综合来说还是写的太少  今天再学了一下 理 ...

  6. HTML5 Canvas核心技术—图形、动画与游戏开发.pdf8

    第6章 精灵 精灵(sprite),它是一种可以集成入动画之中的图像对象,赋予它们各种行为,精灵并非Canvas API的一部分,,但都是从它衍生而来 本章将会实现三种设计模式:策略模式(精灵与绘制器 ...

  7. “等一下,我碰!”——常见的2D碰撞检测

    转自:https://aotu.io/notes/2017/02/16/2d-collision-detection/ 在 2D 环境下,常见的碰撞检测方法如下: 外接图形判别法 轴对称包围盒(Axi ...

  8. JavaScript事件---事件入门

    内容提纲: 1.事件介绍 2.内联模型 3.脚本模型 4.事件处理函数 JavaScript事件是由访问Web页面的用户引起的一系列操作,例如:用户点击.当用户执行某些操作的时候,再去执行一系列代码. ...

  9. javascript中的cookie,以及事件解析

    Cookie: 它的意思是在本地的客户端的磁盘上以很小的文件形式保存数据,Cookie的处理原则上需要在服务器环境下运行,目前Chrome不可以在客户端操作Cookie,其他浏览器均可以,   Coo ...

随机推荐

  1. 解决Fragment每次进入都加载的问题

    1.首先了解一下fragment的生命周期 onCreate是指创建该fragment类似于Activity.onCreate,你可以在其中初始化除了view之外的东西,onCreateView是创建 ...

  2. 用Spark查询HBase中的表数据

    java代码如下: package db.query; import org.apache.commons.logging.Log; import org.apache.commons.logging ...

  3. JDBC结合JSP使用(1)

    1. 添加数据 在jsp页面中添加数据,和在serv中添加数据相似.获得页面中提交的数据以后,把数据保存到数据库表中,JSP的代码如下: add.jsp <%@ page language=&q ...

  4. 读书笔记-C#中装箱拆箱性能

    前言   最近在看王涛大神的<你必须知道的.NET(第二版)>一书,嗯,首先膜拜一下….     在书中的第五章-品味类型中,对装箱与拆箱一节感触很深,概念本身相信每一个程序猿都不陌生,装 ...

  5. ItemsControl

    <ItemsControl Grid.Row=" ItemsSource="{Binding Content.patientInfoList}" Width=&qu ...

  6. The Info-Button Standard: Bring Meaningful Use To the Patient

    http://thehealthcareblog.com/blog/2010/01/28/the-info-button-standard-bringing-meaningful-use-to-the ...

  7. ECMAScript5之Object学习笔记(二)

    继续第二部分 Object.freeze(obj) 看字面意思就是“把一个对象冻结”. 下面我们来看个简单的例子以作说明: // a person instance var person = { na ...

  8. OleView.exe:查看机器上的COM 组件。

      OleView.exe可以查看机器上安装的所有COM组件的类别以及各个类别下的COM组件.      

  9. MySQL源码升级

    mysql源码升级 升级的方法一般有两类: 1.利用mysqldump来直接导出sql文件,导入到新库中,这种方法是最省事儿的,也是最保险的,缺点的话,也显而易见,大库的mysqldump费时费力. ...

  10. Highstock生成股票K线图

    在线演示 本地下载 使用HightStock生成股票K线图例子.