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 ...
随机推荐
- RobotFramework自动化1-环境搭建
前言 Robot Framework是一款python编写的功能自动化测试框架.具备良好的可扩展性,支持关键字驱动,可以同时测试多种类型的客户端或者接口,可以进行分布式测试执行. Robot Fram ...
- [翻译] EnterTheMatrix
Enter The Matrix https://github.com/mpospese/EnterTheMatrix The sample application to accompany my c ...
- iptables配置实践
前言 在大企业中防火墙角色主要交给硬件来支持,效果自然没话说只是需要增加一点点成本,但对于大多数个人或者互联网公司来说选择系统自带的iptables或者第三方云防火墙似乎是更加合适的选择,通过一些合理 ...
- TOMCAT6热部署配置
在J2EE开发过程中,经常需要在启动tomcat后修改java类文件,tomcat默认会自动加载修改的类,但这只是重新启动整个项目换句话说就是自动帮我们重启tomcat,这样就浪费了大量的时间在等等t ...
- 【XJOI】【NOI考前模拟赛7】
DP+卡常数+高精度/ 计算几何+二分+判区间交/ 凸包 首先感谢徐老师的慷慨,让蒟蒻有幸膜拜了学军的神题.祝NOI2015圆满成功 同时膜拜碾压了蒟蒻的众神QAQ 填填填 我的DP比较逗比……( ...
- Informatica 常用组件Filter之三 创建FIL
在 Designer 中,切换到 Mapping Designer 并打开映射. 选择"转换-创建". 选择"过滤器转换",然后输入新的转换名称.过滤器转换的命 ...
- linux进程、调度、线程、进程上下文等几点理解
1.信号来自进程或内核 2.线程共享进程的代码空间和数据空间(全局变量或静态变量),文件描述符,信号,以及malloc分配的内存,每个线程拥有独立的栈空间和程序计数器,在创建线程时,调用pthread ...
- Android中Fragment的简单介绍
Android是在Android 3.0 (API level 11)引入了Fragment的,中文翻译是片段或者成为碎片(个人理解),可以把Fragment当成Activity中的模块,这个模块有自 ...
- python 读写CSV文件
#-*- coding: UTF-8 -*- import csv import os def WriteToCsv(): '''写CSV文件''' titls = ['序号', '链接', '备注' ...
- Escape字符总结
有如下的 escape字符. 对于十进制来说,\后面只涵盖3个字符,比如\1234,是\123和字符4. 但是对于十六进制,后面会涵盖四个字符,比如\x1234,后面的四个字符都在\的涵盖范围内.