html5 canvas 像素 碰撞检测,碰撞检测 · HTML5 Canvas半知半解 · 看云

## 碰撞检测

碰撞检测是物体与物体之间的交互,其实在前面的边界检测也是一种碰撞检测,只不过检测的对象是物体与边界之间。在本章中,我们将介绍更多的碰撞检测,比如:两个物体间的碰撞检测、一个物体与一个点的碰撞检测、基于距离的碰撞检测等等碰撞检测方法。

**什么是碰撞检测呢?**

简单来说,碰撞检测就是判定两个物体是否在同一时间内占用一块空间,用数学的角度来看,就是两个物体有没有交集。

检测碰撞的方法有很多,一般我们使用如下两种:

从几何图形的角度来检测,就是判断一个物体是否与另一个有重叠,我们可以用物体的矩形边界来判断。

检测距离,就是判断两个物体是否足够近到发生碰撞,需要计算距离和判断两个物体是否足够近。

**1、基于几何图形的碰撞检测**

基于几何图形的碰撞检测,一般情况下是检查一个矩形是否与其他矩形相交,或者某一个坐标点是否落在矩形内。

**1.1 两个物体间的碰撞检测(矩形边界检测法)**

在上一章中,我们介绍了一个 getBound() 方法,参数为球对象,返回矩形对象。

```

function getBound(body){

return {

x: (body.x - body.radius),

y: (body.y - body.radius),

width: body.radius * 2,

height: body.radius * 2

};

}

```

现在我们已经知道如何获取物体的矩形边界,那么只需检测两个对象的边界框是否相交,就可以判断两个物体是否碰撞了。我们在 tool.js 工具类中添加一个工具函数 tool.intersects :

```

tool.intersects = function(bodyA,bodyB){

return !(bodyA.x + bodyA.width < bodyB.x ||

bodyB.x + bodyB.width < bodyA.x ||

bodyA.y + bodyA.height < bodyB.y ||

bodyB.y + bodyB.height < bodyA.y);

};

```

这个函数传入两个矩形对象,如果返回true,表示两个矩形相交了;否则,返回false。(如果你看不明白这段代码,请看下图,让一个矩形分别位于另一个矩形的上下左右位置):

![](https://box.kancloud.cn/fbb5ac590132efc02e78399a8d96fdad_388x314.jpg)

检测函数已经知道了,当要检测两个物体是否相交时,就可以做如下判断:

```

if (tool.intersects(objectA,objectB)) {

console.log('撞上了');

}

```

注意:这里传入的必须是矩形对象。如果是球,可调用getBound()方法返回矩形对象。如果已经是矩形对象,就直接传入。

这里有一个需要注意的问题,有些时候,我们的物体是不规则的,如果我们采取矩形边界检测,有时候会不精确(只有真正的矩形才是精确的):

![](https://box.kancloud.cn/598a7c9e6e01e4a95419490a72412494_600x195.jpg)

在上面的图中,有矩形、圆形和五角形,我们都可以采取矩形边界检测法,不过,你会发现,当物体是不规则的形状时,虽然通过上面的 tool.intesects() 方法判断两个物体已经碰撞,但实际上并没有,所以矩形边界检测法对不规则的图形来说,这只是一种不精确的检测方法,如果你要精确检测,那就要做更多的检测了。当然,矩形边界检测法对于大多数情况下已经足够了。

实例又来了(用iframe插入会导致页面卡,所以放在单独页面中,点击可看):http://ghmagical.com/Iframe/show/code/intersect

```

if(activeRect !== rect && tool.intersects(activeRect, rect)) {

activeRect.y = rect.y - activeRect.height;

activeRect = createRect();

};

```

这个例子是不是有点像俄罗斯方块呢,每一次只有一个活动物体,然后循环检测它是否与已经存在的物体碰撞,如果碰撞,则将活动物体放在与它碰撞物体的上面,然后创建一个新的方块。

**1.2 物体与点的碰撞检测**

在前面我们在 tool工具类中添加了一个工具函数 tool.containsPoint,它接受三个参数,第一个是矩形对象,后面两个是一个点的x和y的坐标,返回值是true或false:

```

tool.containsPoint = function(body, x, y){

return !(x < body.x || x > (body.x + body.width)

|| y < body.y || y > (body.y + body.height));

};

```

其实,tool.containsPoint()函数就是在检测点与矩形是否碰撞。

比如,要检测点(50,50)是否在一个矩形内:

```

if(tool.containsPoint(body,50,50)){

console.log('在矩形内');

}

```

tool.intesects()和tool.containsPoint()方法都会遇到精确问题,对矩形最精确,越不规则,精确率就越小。大多数情况下,都会采取这两种方法。当然,如果你要对不规则图形采取更精确的方法,那你就要写更多的代码去执行精确的检测了。

**2、基于距离的碰撞检测**

距离就是指两个物体间的距离,当然,物体总是有高宽的,这就还要考虑高宽。一般我们会先确定两个物体的最小距离,然后计算当前距离,最后进行比较,如果当前距离比最小距离小,那肯定发生了碰撞。

这种距离检测法,对圆来说是最精确的,而对于其他图形,或多或少会有一些精确问题。

**2.1 基于距离的简单碰撞检测**

基于距离的碰撞检测的最理想的情况是:有两个正圆形要进行碰撞检测,从圆的中心点开始计算。

要检测两个圆是否碰撞,其实就是比较两个圆的中心点的距离与两个圆的半径和的大小关系。

```

dx = ballB.x - ballA.x;

dy = ballB.y - ballA.y;

dist = Math.sqrt(dx * dx + dy * dy);

if(dist < ballA.radius + ballB.radius){

console.log('碰撞了');

}

```

实例:canvas-demo/distanceIntersect.html

在上面的例子中,碰撞距离就是一个球的半径加上另一个球的半径,也是碰撞的最小距离,而两者真正的距离就是圆心与圆心的距离。

```

var dx = ballB.x - ballA.x;

var dy = ballB.y - ballA.y;

var dist = Math.sqrt(dx * dx + dy * dy);

if(ball != ballB && dist < ballA.radius + ballB.radius){

ctx.strokeStyle = 'red';

var txt = '你压着我了';

var tx = ballA.x - ctx.measureText(txt).width / 2;

ctx.font = '30px Arial'

ctx.strokeText(txt,tx,ballA.y);

};

```

**2.2 弹性碰撞**

就像2.1节里的例子一样,当两个球碰撞时,我们加入了文字提示,当然,我们还可以做更多操作,比如这节要讲的弹性碰撞。

实例:canvas-demo/springIntersect.html

首先我们加入一个放在canvas中心的圆球ballA,然后加入多个随机大小和随机速度的圆球,让它们做匀速运动,遇到墙就反弹,最后在每一帧使用基于距离的方法检测小球是否与中央的圆球ballA发生了碰撞,如果发生了碰撞,则计算弹动目标点和两球间的最小距离来避免小球完全撞上圆球ballA。

对于小球和圆球ballA的碰撞,我们可以这样理解,我们在ballA外设置了目标点,然后让小球向目标点弹动,一旦小球到达目标点,就不再继续碰撞,弹性运动就结束了,继续做匀速运动。

下面的效果就像一群小气泡在大气泡上反弹,小气泡撞入大气泡一点距离,这个距离取决于小气泡的速度,然后被弹出来。

如果你看不懂它如何反弹的,那你就要回到上一章看看《缓动和弹动》是如何实现的了。

**3、多物体的碰撞检测策略**

这一节并不会介绍新的碰撞检测方法,而是介绍如何优化多物体碰撞代码。

如果你用过二维数组,那么你肯定知道如何去遍历数组元素,通常的方法是使用两个循环函数,而多物体的碰撞检测,也类似二维数组:

```

for(var i = 0; i < objects.length; i++){

var objectA = objects[i];

for(var j = 0; j < objects.length; j++){

var objectB = objects[j];

if(tool.intersects(objectA,objectB){}

}

};

```

上面的方法的语法是没错的,不过这段代码有两个效率问题:

**(1)多余的自身碰撞检测**

它检测了同一个物体是否自身碰撞,比如:第一个物体(i=0)是objects[0],在第二次循环中,第一个物体(j=0)也是objects[0],是不是完全没必要的检测,我们可以这样避免:

```

if(i != j && tool.intersects(objectA,objectB){}

```

这样会节省了i次碰撞检测

**(2)重复碰撞检测**

第一次(i=0)循环时,我们检测了objects[0](i=0)和objects[1](j=1)的碰撞;第二次(i=1)循环时,代码似乎又检测了objects[1](i=1)和objects[0](j=0)的碰撞,这岂不是多余的吗?

我们应该做如下的避免:

```

for(var i = 0; i < objects.length; i++){

var objectA = objects[i];

for(var j = i + 1; j < objects.length; j++){

var objectB = objects[j];

if(tool.intersects(objectA,objectB){}

}

};

```

这样处理后,不仅避免了自身碰撞检测,而且减少了重复碰撞检测。

实例:canvas-demo/collision.html

在上面的例子中,两个球在碰撞后的弹动代码并没有太大的区别,只不过这里将ballB当成了中央位置的圆球而已:

```

function checkCollision(ballA, ballB) {

var dx = ballA.x - ballB.x;

var dy = ballA.y - ballB.y;

var dist = Math.sqrt(dx * dx + dy * dy);

var min_dist = ballB.radius + ballA.radius;

if(dist < min_dist) {

var angle = Math.atan2(dy, dx);

var tx = ballB.x + Math.cos(angle) * min_dist;

var ty = ballB.y + Math.sin(angle) * min_dist;

var ax = (tx - ballA.x) * spring * 0.5;

var ay = (ty - ballA.y) * spring * 0.5;

ballA.vx += ax;

ballA.vy += ay;

ballB.vx += (-ax);

ballB.vy += (-ay);

};

};

```

上面代码最后四行的意思是:不仅ballB要从ballA弹开,而且ballA要从ballB弹出,它们的加速度的绝对值是相同的,方向相反。

不知道你有没有注意到,ax和ay的计算都乘以0.5,这是因为当ballA移动ax时,ballB也反向移动ax,那么就造成了 ax 变成 2ax ,所以要乘以0.5,才是真正的加速度。当然,你也可以将spring减小成原来的一半。

**总结**

碰撞检测是很多动画中必不可少的,你必须掌握基于几何图形的碰撞检测、基于距离的碰撞检测方法,以及如何更有效的的检测多物体间的碰撞。

**附录**

**重要公式:**

(1)矩形边界碰撞检测

```

tool.intersects = function(bodyA,bodyB){

return !(bodyA.x + bodyA.width < bodyB.x ||

bodyB.x + bodyB.width < bodyA.x ||

bodyA.y + bodyA.height < bodyB.y ||

bodyB.y + bodyB.height < bodyA.y);

};

```

(2)基于距离的碰撞检测

```

dx = objectB.x - objectA.x;

dy = objectB.y - objectA.y;

dist = Math.sqrt(dx * dx + dy * dy);

if(dist < objectA.radius + objectB.radius){}

```

(3)多物体碰撞检测

```

for(var i = 0; i < objects.length; i++){

var objectA = objects[i];

for(var j = i + 1; j < objects.length; j++){

var objectB = objects[j];

if(tool.intersects(objectA,objectB){}

}

};

```

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/300836.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

法国一家夜总会上演机器人钢管舞

全世界只有3.14 % 的人关注了青少年数学之旅星空新闻报道&#xff0c;法国SC-Club夜总会9月3日邀请机器人跳钢管舞&#xff0c;庆祝其开业5周年。该机器人脚踩高跟鞋&#xff0c;头上是一台CCTV监控仪。发明者称&#xff0c;这是为了引发人们对于“窥探”的思考。店主表示这次演…

AJAX,只是一种过渡技术吗?

在CSDN首页上用大标题写着“谁来革AJAX的命&#xff0c;Flash还是WPF”&#xff0c;同时在下面列举了Adobe推出Flex 2.0 力拼Ajax&#xff0c;以及袁红岗在6月份的文章Ajax&#xff0c;只是一种过渡技术&#xff0c;其中袁在文章中说到“Ajax其实是一种新瓶装旧酒的过渡技术&am…

li或dd 浮动后增加图片时高度多出3-5px的问题

转载的&#xff0c;确实能解决问题&#xff0c;原因不明&#xff0c;求指教&#xff0c;谢谢&#xff01; 本人实验&#xff0c;chrome下多出3px&#xff0c;firefox下多出5px li或dd 浮动后增加图片时高度多出3-5px的问题 dd或li或a标签内&#xff0c;插入图片后&#xff0c;下…

Binding(三):资源和ValueConverter

这节讲资源和值转换器(ValueConverter)。资源在XAML中&#xff0c;我们想要使用外部的数据或者类&#xff0c;需要引入其命名空间&#xff0c;然后将其定义为XAML页面的资源&#xff0c;供给控件使用&#xff0c;或者我们需要封装一个共用的样式&#xff0c;同样也需要定义成资…

c语言 overflow_C语言和其他语言的不得不说的差别!

提到C语言&#xff0c;我们知道c语言和其他高级语言的最大的区别就是C语言是要操作内存的&#xff01;我们需要知道——变量&#xff0c;其实是内存地址的一个抽像名字罢了。在静态编译的程序中&#xff0c;所有的变量名都会在编译时被转成内存地址。机器是不知道我们取的名字的…

html省市区选择器代码,js实现一个省市区三级联动选择框代码分享

运行效果&#xff1a;部分代码&#xff1a;当然首先你数据库中要有这个table&#xff0c;不然你没有数据.....^_^所在地:${xzqh.province}js代码&#xff1a;/*** 加载市**/function loadCity() {var provinceId $("#provinceSelect option:selected").val();if(pro…

做人开心最重要

做人开心最重要 转载于:https://www.cnblogs.com/LeeWenjie/archive/2006/11/23/569502.html

你和学霸的差距,不只是成绩

教育不是只有在校园学习不是只有课堂阅读不是只有书上这几个移动的课堂&#xff0c;可以让大家随时关注它们带你学习更多知识【关注方式 】1 - 搜索公众号ID关注2 - 长按二维码识别关注数学好物ID: webaoming简介&#xff1a;“数学好物”是一个致力为数学爱好者与家长&#x…

Storyboard.storyboard could not be opened. Could not read the archive.

Storyboard.storyboard could not be opened. Could not read the archive. Please use a newer version of Xcode. Consider changing the documents Development Target to preserve compatibility. 用低版本xcode打开高版本xcode下创建的项目时会出现这个错误。 解决办法&a…

Binding(二):控件关联和代码提升

上节我们讲到&#xff0c;使用Binding&#xff0c;我们可以关联后台代码中的属性&#xff0c;在某些情况下&#xff0c;我们可能需要将两个控件关联起来&#xff0c;借助Binding&#xff0c;我们也可以轻松的实现。关联控件设想这样一个场景&#xff0c;界面中有个CheckBox&…

python实战excel_实战python 读写EXCEL表

python/EXCEL读取保存import xlwtimport xlrdimport osdef read_excel(filename):row_list []bk xlrd.open_workbook(filename)try:sh bk.sheet_by_name("Sheet1")except:print("no sheet in %s named Sheet1" % filename)else:# 获取行数nrows sh.nro…

网页表格线框html,关于Dreamweaver中怎么让html网页中的table边框细线显示?

关于Dreamweaver中怎么让html网页中的table边框细线显示?对于不是编程的我们&#xff0c;用Dreamweaver制作表格的情况下。默认表格边框会标的比较粗&#xff0c;今天给大家介绍下制作西表格超级简单的方法。是复制一段CSS内嵌样式方法来制作细线表格&#xff0c;然后再通过bo…

众望所归!中国机器狗能【后空翻】了。。。

全世界只有3.14 % 的人关注了青少年数学之旅在北京亦庄举办的世界机器人大会昨天结束了&#xff0c;这次大会上明显的感觉机械狗多了不少&#xff0c;中瑞福宁展示了四足多用途机器人Anymal&#xff0c;深度学习加持爬行、走路、跑步、跳舞样样精通&#xff1a;上楼梯的Anymal算…

男人别让爱你的女孩流泪

曾经以为付出就会有回报&#xff0c;付出才知道&#xff0c;在她的心里我始终不是最重要&#xff0c;无可奈何花落去&#xff0c;似曾相识燕归来。 经年少轻狂的我不知道什么是珍贵&#xff0c;直到有天&#xff0c;我回忆过去&#xff0c;我才知道眼泪的味道是咸的…… 第一次…

Binding(一):数据绑定

这节开始分几期来讲一下WPF中Binding的相关用法&#xff0c;本节主要讲使用Binding进行数据绑定。Binding的作用在WinForm中&#xff0c;我们要想对控件赋值&#xff0c;需要在后台代码中拿到控件对象进行操作&#xff0c;这种赋值形式&#xff0c;从根本上是无法实现界面与逻辑…

浙江省2021年英语高考成绩查询,浙江高考成绩查询、志愿填报时间公布!

为期四天的2021年浙江省高考将于明天下午落幕对于高考生来说志愿填报是除考试外的重要环节今晚&#xff0c;浙江省教育考试院发布2021年浙江省高考志愿填报日程安排根据计划6月26日左右将公布各段分数线和一分一段线提供考试成绩和位次号查询所有考生高考志愿均实行网上填报浙江…

background 旋转_第4章 旋转的圆弧(《Python趣味创意编程》教学视频)

&#xff08;图书介绍&#xff1a;童晶&#xff1a;《Python趣味创意编程》新书预告&#xff09;本章我们将实现旋转的圆弧&#xff0c;如图所示。首先学习圆弧的绘制和旋转&#xff1b;接着利用全局变量实现圆弧逐渐变长&#xff0c;学习if选择语句&#xff0c;实现圆弧长度的…

大师之路-GoLive 视频教程

全部章节 (54Files/1.2G)01 输出网页设计稿 (素材)02 建立站点03 操作方式初识04 用版面网格进行布局05 创建翻转及脚本库设置06 通过关系图策划站点07 使用表格08 表格的控制和嵌套09 应用表样式和表数据处理10 表格制作实战(1/2)11 表格制作实战(2/2)12 色彩应用13 管理站点色…

世纪渣男何书桓! | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源网络&#xff0c;侵权删&#xff09;渣男&#xff0c;实锤&#xff01;↓ ↓ ↓

你也可以搞懂的微服务第一篇——来自ThoughtWork的学习体验

????欢迎点赞 &#xff1a;???? 收藏 ⭐留言 ???? 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;????本文作者&#xff1a;由webmote 原创&#xff0c;首发于 【掘金】????作者格言&#xff1a;生活在于折腾&#xff0c;当你不折…