新技术论坛
搜索
查看: 850|回复: 0
打印 上一主题 下一主题

[HTML5] canvas中的碰撞检测笔记

[复制链接]
  • TA的每日心情
    开心
    2016-12-9 18:18
  • 签到天数: 85 天

    连续签到: 1 天

    [LV.6]常住居民II

    扫一扫,手机访问本帖
    楼主
    跳转到指定楼层
    发表于 2016-3-15 21:37:11 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
    用 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 > 0 && data2 > 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)
    •       return true;  // 碰撞
    •   }

    •   return false;
    • }

    复制代码


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


    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    手机版|Archiver|开发者俱乐部 ( ICP/ISP证:辽B-2-4-20110106号 IDC证:辽B-1-2-20070003号 )

    GMT+8, 2024-12-24 00:38 , Processed in 0.138962 second(s), 22 queries .

    X+ Open Developer Network (xodn.com)

    © 2009-2017 沈阳讯网网络科技有限公司

    快速回复 返回顶部 返回列表