用 canvas 做小游戏或者特效,碰撞检测是少不了的。本文将会涉及普通的碰撞检测,以及像素级的碰撞检测。(本文的碰撞检测均以矩形为例)

普通碰撞检测

普通的矩形碰撞检测比较简单。即已知两个矩形的各顶点坐标,判断是否相交,如相交,则为碰撞。

leetcode 有道题是给出两个矩形的坐标,求其相交面积(223. Rectangle Area),代码 可以直接拿过来用,如果面积大于 0,则为碰撞。

如果只需判断是否相交或者相交面积,非常简单,可以参考 这里

为了程序的可扩展性,如果碰撞,最好还能求得相交矩形的坐标信息(为像素级碰撞检测作准备),完善后的检测代码如下:

// 矩形一 top-left 坐标 (A, B), C 为 width, D 为 height
// 矩形二 同上
// 如果没有相交,返回 [0, 0, 0, 0]
// 如果相交,假设相交矩形对角坐标 (x0, y0) (x1, y1) -- x1 > x0 & y1 > y0
// return [x0, y0, x1, y1]
function check(A, B, C, D, E, F, G, H) {
  // 转为对角线坐标
  C += A, D += B, G += E, H += F;

  // 没有相交
  if (C <= E || G <= A || D <= F || H <= B)
    return [0, 0, 0, 0];

  var tmpX, tmpY;

  if (E > A) {
   tmpX = G < C ? [E, G] : [E, C];
  } else {
   tmpX = C < G ? [A, C] : [A, G];
  }

  if (F > B) {
   tmpY = H < D ? [F, H] : [F, D];
  } else {
   tmpY = D < H ? [B, D] : [B, H];
  }

  return [tmpX[0], tmpY[0], tmpX[1], tmpY[1]];
}

// 相交矩形坐标信息
var rect = check(fish.pos.x, fish.pos.y, fish.size.x, fish.size.y,
  cat.pos.x, cat.pos.y, cat.size.x, cat.size.y);

// 相交面积大于 0 即为碰撞
var isHit = (rect[2] - rect[0]) * (rect[3] - rect[1]) > 0;

像素级碰撞检测

为什么要有像素级检测?一图以蔽之。

一般游戏或者动画中的精灵都是矩形,仅仅判断矩形相交是不准确的,比如上图中,图片所在矩形已经相交,但是精灵其实并没有碰撞,所以我们需要进行像素级别的碰撞检测。

方法一:

同时检测两图在相交矩形内的像素,若存在一点在两个图上的 alpha 值不为 0,则发生碰撞。

因为还要对原始的图像(fish 图和 cat 图)分别提取像素点(进行判断),所以需要一个离屏的 canvas 。这里用了 canvas 的 getImageData 方法提取像素点 rgba 信息。

// a, b 为精灵对象
// a, b 分别拥有键值 img(精灵图像 DOM元素), pos(精灵瞬间位置 top-left 坐标), size(wdith, height 数据)
// rect 参数为 check() 函数返回值
function checkInDetail(a, b, rect) {
  // 离屏 canvas
  var canvas = document.createElement('canvas');
  _ctx = canvas.getContext('2d');

  _ctx.drawImage(a.img, 0, 0, a.size.x, a.size.y);
  // 相对位置
  var data1 = _ctx.getImageData(rect[0] - a.pos.x, rect[1] - a.pos.y, rect[2] - rect[0], rect[3] - rect[1]).data;

  _ctx.clearRect(0, 0, b.size.x, b.size.y);
  _ctx.drawImage(b.img, 0, 0, b.size.x, b.size.y);
  var data2 = _ctx.getImageData(rect[0] - b.pos.x, rect[1] - b.pos.y, rect[2] - rect[0], rect[3] - rect[1]).data;

  canvas = null;

  for(var i = 3; i < data1.length; i += 4) {
    if(data1[i] > 0 && data2[i] > 0)
      return true; // 碰撞
  }

  return false;
}

// 精灵对象实例
var fish = {
  img: document.getElementById('fish')
  , pos: new Vector2()
  , size: new Vector2()

  // ...
};

方法二:

先画一张图,然后将混合模式改为 source-in,这时再画图,新图片会仅仅出现与原有内容重叠的地方,其他地方透明度变为 0,这时就可以通过判断是否所有像素都透明来判断碰撞了。

// a, b 为精灵对象
// a, b 分别拥有键值 img(精灵图像 DOM元素), pos(精灵瞬间位置 top-left 坐标), size(wdith, height 数据)
// rect 参数为 check() 函数返回值
function _checkInDetail(a, b, rect) {
  // 离屏 canvas
  var canvas = document.createElement('canvas');
  _ctx = canvas.getContext('2d');

  // 将 (0, 0) 作为基准点,将 a 放入 (0, 0) 位置
  _ctx.drawImage(a.img, 0, 0, a.size.x, a.size.y);
  _ctx.globalCompositeOperation = 'source-in';
  _ctx.drawImage(b.img, b.pos.x - a.pos.x, b.pos.y - a.pos.y, b.size.x, b.size.y);

  var data = _ctx.getImageData(rect[0] - a.pos.x, rect[1] - a.pos.y, rect[2] - rect[0], rect[3] - rect[1]).data;

  canvas = null;

  // 改回来(虽然并没有什么卵用)
  _ctx.globalCompositeOperation = 'source-over';

  for(var i = 3; i < data.length; i += 4) {
    if (data[i])
      return true;  // 碰撞
  }

  return false;
}

我测试了几次,把相交的像素点都取了出来求得相交像素点总数,两种方法有时会相差一两个像素点。对于像素级碰撞检测来说,两种方法任取其一就可。

canvas中的碰撞检测笔记的更多相关文章

  1. Html5 Canvas动画基础碰撞检测的实现

    在Canvas中进行碰撞检测,大家往往直接采用游戏引擎(Cocos2d-JS.Egret)或物理引擎(Box2D)内置的碰撞检测功能,好奇的你有思考过它们的内部运行机制吗?下面将针对基本的碰撞检测技术 ...

  2. 在canvas中使用html元素

    让div悬浮于canvas之上   使用z-index控制层及顺序 慕课网canvas demo <div id="canvas-wrapper"> <canva ...

  3. 讲解Canvas中的一些重要方法

    Canvas所提供的各种方法根据功能来看大致可以分为几类: 第一是以drawXXX为主的绘制方法: 第二是以clipXXX为主的裁剪方法: 第三是以scale.skew.translate和rotat ...

  4. <canvas>中isPointInPath()方法在不同绘制内容中的效果

    <canvas>是HTML5中新增加的一个元素,我们可以使用脚本(通常使用JavaScript)在上面绘制图形,就像个画布一样.我们可以用它来绘制图表.制作一些动画.默认大小为300px ...

  5. Canvas与javaScript特效笔记

    第六章   Canvas与javaScript特效笔记 q  <canvas>标签的用途 HTML5 canvas 提供了通过 JavaScript 绘制图形的方法,此方法使用简单但功能强 ...

  6. 关于Unity中的碰撞检测和管理(2D)

    创建Unity3D项目和Unity2D项目的区别. 1.3D项目有摄像机和太阳光,2D项目只有摄像机 2.3D项目使用的贴图类型是Texture纹理,2D项目使用的贴图类型是Sprite 2D/UI ...

  7. HTML5在canvas中绘制复杂形状附效果截图

    HTML5在canvas中绘制复杂形状附效果截图 一.绘制复杂形状或路径 在简单的矩形不能满足需求的情况下,绘图环境提供了如下方法来绘制复杂的形状或路径. beginPath() : 开始绘制一个新路 ...

  8. 转:openwrt中luci学习笔记

    原文地址:openwrt中luci学习笔记 最近在学习OpenWrt,需要在OpenWrt的WEB界面增加内容,本文将讲述修改OpenWrt的过程和其中遇到的问题. 一.WEB界面开发         ...

  9. VS2013中Python学习笔记[Django Web的第一个网页]

    前言 前面我简单介绍了Python的Hello World.看到有人问我搞搞Python的Web,一时兴起,就来试试看. 第一篇 VS2013中Python学习笔记[环境搭建] 简单介绍Python环 ...

随机推荐

  1. 软件工程(C编码实践)学习总结及心得

    徐礼超  原创作品转载请注明出处:http://www.cnblogs.com/xulichao <软件工程(C编码实践篇)>MOOC课程http://mooc.study.163.com ...

  2. CSS之旅——第二站 如何更深入的理解各种选择器

    上篇我们说了为什么要使用css,这篇我们就从选择器说起,大家都知道浏览器会把远端过来的html解析成dom模型,有了dom模型,html就变成 了xml格式,否则的话就是一堆“杂乱无章”的string ...

  3. SQL动态列查询

    数据库中为了实现表格数据的自由设置,我们经常设计纵表,或者列定义的表(如下KeyValue),定义一个列超级多的表中每个字段的意义. 但是在设计时简单的东西却很容易被人们忘记,如下一个简单但是很松散的 ...

  4. MySQL Database on Azure 的用户名

    MySQL Database on Azure是中国版Windows Azure上的一个PaaS服务,类似于AWS上的RDS.MySQL Database on Azure的用户名与on-premis ...

  5. 时光煮雨 Unity3D实现2D人物移动-总结篇

    系列目录 [Unity3D基础]让物体动起来①--基于UGUI的鼠标点击移动 [Unity3D基础]让物体动起来②--UGUI鼠标点击逐帧移动 时光煮雨 Unity3D让物体动起来③—UGUI DoT ...

  6. 在PYTHON中使用StringIO的性能提升实测(更新list-join对比)

    刚开始学习PYTHON,感觉到这个语言真的是很好用,可以快速完成功能实现. 最近试着用它完成工作中的一个任务:在Linux服务器中完成对.xml.gz文件的解析,生成.csv文件,以供SqlServe ...

  7. java分层开发

    既然是分层开发,首先我们需要知道的是分为那几个层,并且是干什么的? 1.实体层(entity) 对应数据库中的一张表,有了它可以降低耦合性,同时也是数据的载体. 2.数据访问对象(data acces ...

  8. Sphinx和coreseek检索引擎

    Sphinx是检索英文用,coreseek是检索中文用. Sphinx(斯芬克斯)是一个基于SQL的全文检索引擎,可以结合MySQL,PostgreSQL做全文搜索,它可以提供比数据库本身更专业的搜索 ...

  9. noip模拟赛(10.4) 背包(pack)

    [题目描述] 蛤布斯有n种商品,第i种物品的价格为ai,价值为bi.有m个人来向蛤布斯购买商品,每个人每种物品只能购买一个.第j个人有cj的钱,他会不停选择一个能买得起的价格最高的商品买走(如果有多个 ...

  10. bzoj2548[Cstc2002]灭鼠行动

    Description 最近,有一些繁殖力很强的老鼠在下水道非常猖獗,灭鼠特工队正在计划消灭这些老鼠.下水道只有东西方向和南北方向的管道,如图所示. 灭鼠特工队的队员拥有强大的武器.他们将在某些时刻t ...