canvas中的碰撞检测笔记

简介: canvas中的碰撞检测笔记时间 2016-01-1908:29:00  博客园精华区原文  http://www.cnblogs.com/zichi/p/5141044.html主题 Canvas用canvas做小游戏或者特效,碰撞检测是少不了的。

canvas中的碰撞检测笔记

时间 2016-01-19 08:29:00   博客园精华区
主题  Canvas

用 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;

像素级碰撞检测

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

canvas中的碰撞检测笔记

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

方法一:

同时检测两图在相交矩形内的像素,若存在一点在两个图上的 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_x('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_x('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;
}

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

目录
相关文章
|
8月前
|
移动开发 前端开发 JavaScript
使用Canvas绘制图形和动画
使用Canvas绘制图形和动画
130 0
|
9月前
|
前端开发
canvas画布实现代码雨
canvas画布实现代码雨
65 0
|
前端开发 JavaScript
教你如何利用canvas画布绘制哆啦A梦
订阅专栏 教你如何利用canvas画布绘制哆啦A梦 最近一直在练习使用canvas画布标签,今天教大家如何使用canvas画布绘制哆啦A梦。如图: HTML代码: <canvas id="my_canvas"></canvas> 1 CSS代码: canvas { display:block; margin:0 auto; background: pink } 1 2 3 4 5 JavaScript代码: var oCanvas = document.getEleme
教你如何利用canvas画布绘制哆啦A梦
|
前端开发
canvas绘制五角星
canvas绘制五角星
147 0
|
前端开发 JavaScript
教你如何用Canvas绘制整身的哆啦A梦
教你如何用Canvas绘制整身的哆啦A梦 上牌你文章我们说到了如何使用canvas绘制哆啦A梦,但当时只是绘制了哆啦A梦的头部,之后有学员留言说可不可以教大家绘制整身的哆啦A梦,想了想,决定安排一下。如图: 在这里插入图片描述 HTML代码: &lt;canvas id=&quot;my_canvas&quot;&gt;&lt;/canvas&gt; &lt;canvas id=&quot;my_canvas2&quot;&gt;&lt;/canvas&gt; 1 2 CSS代码: * { margin: 0; } #my_canvas {
教你如何用Canvas绘制整身的哆啦A梦
|
前端开发 JavaScript
canvas中的拖拽、缩放、旋转 (上) —— 数学知识准备
canvas中的拖拽、缩放、旋转 (上) —— 数学知识准备
717 0
canvas中的拖拽、缩放、旋转 (上) —— 数学知识准备
|
前端开发
Canvas绘制圆形头像
Canvas绘制圆形头像
197 0
Canvas绘制圆形头像
|
前端开发
Canvas图形绘制
Canvas图形绘制
145 0
Canvas图形绘制
|
前端开发
|
前端开发
用canvas绘制一个烟花动画
前言 在我们日常开发中贝塞尔曲线无处不在: svg 中的曲线(支持 2阶、 3阶) canvas 中绘制贝塞尔曲线 几乎所有前端2D或3D图形图表库(echarts,d3,three.js)都会使用到贝塞尔曲线 所以掌握贝塞尔曲线势在必得。这篇文章主要是实战篇,不会介绍和贝塞尔相关的知识, 如果有同学对贝塞尔曲线不是很清楚的话:可以查看我这篇文章——深入理解SVG 绘制贝塞尔曲线 第一步我们先创建ctx, 用ctx 画一个二阶贝塞尔曲线看下。二阶贝塞尔曲线有1个控制点,一个起点,一个终点。 const canvas = document.getElementById( 'canvas'
用canvas绘制一个烟花动画