canvas中的碰撞检测笔记

canvas中的碰撞检测笔记

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

原文  http://www.cnblogs.com/zichi/p/5141044.html

主题 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;

像素级碰撞检测

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

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

方法一:

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

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

时间: 2024-09-09 09:14:42

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

HTML5 Canvas中使用路径描画二阶、三阶贝塞尔曲线

  在HTML5 Canvas中,可以用以下方法描画三阶和二阶的贝塞尔曲线: 代码如下: context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) context.quadraticCurveTo(cpx, cpy, x, y) 贝塞尔曲线是在二维平面上由一个"起始点",一个"结束点",以及一个或多个"控制点"定义的曲线.普通的三阶贝塞尔曲线使用两个控制点,而二阶曲线则只使用一个控制点. 要描画二阶贝

HTML5 Canvas中绘制矩形实例

让我们来看一下Canvas内置的简单几何图形 - 矩形的绘制.在Canvas中,绘制矩形有三种方法:填充(fillRect).描边(StrokeRect)以及清除(clearRect).当然,我们也可以使用"路径"来描绘包括矩形在内的所有图形. 以下是上述三种方法的API: 1.fillRect(x,y,width,height).绘制一个从(x,y)开始,宽度为width,高度为height的实心矩形. 2.strokeRect(x,y,width,height).绘制一个从(x,y

HTML5 在canvas中绘制文本附效果图

 一.绘制文本 在绘图环境中提供了两种方法在canvas中绘制文本. strokeText(text,x,y) : 在(x,y)处绘制空心的文本. fillText(text,x,y) : (x,y)处绘制实心的文本. 二.在canvas中绘制文本   代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-type" content="text/html; char

java基础-Java基础 canvas中加载图片问题

问题描述 Java基础 canvas中加载图片问题 public class GameCanvas extends Canvas implements Runnable{ boolean PaintState=true; Image soccerImage; public GameCanvas(){ try { soccerImage=ImageIO.read(new File(""/image/soccer.jpg""));} catch (IOException

3D游戏中的碰撞检测以及爬楼梯的算法

问题描述 3D游戏中的碰撞检测以及爬楼梯的算法 比如说在地面上的时候,人是直着往前走的,当遇到楼梯以后,就不能往前走了,而是会直接沿着楼梯向上走,请问这个怎么实现呢??比如这个例子中的上楼梯的算法是怎么实现的呢?http://www.babylonjs.com/Demos/WCafe/ 解决方案 简单碰撞检测的3D游戏3D游戏中的碰撞检测android 3D 游戏实现之简单碰撞检测

Java中jqGrid 学习笔记整理——进阶篇(二)_java

相关阅读: Java中jqGrid 学习笔记整理--进阶篇(一) 本篇开始正式与后台(java语言)进行数据交互,使用的平台为 JDK:java 1.8.0_71 myEclisp 2015 Stable 2.0 Apache Tomcat-8.0.30 Mysql 5.7 Navicat for mysql 11.2.5(mysql数据库管理工具) 一.数据库部分 1.创建数据库 使用Navicat for mysql创建数据库(使用其他工具或直接使用命令行暂不介绍) 2. 2.创建表 双击打

像素图-html5在canvas中插入图片

问题描述 html5在canvas中插入图片 在canvas中显示图片非常简单.可以通过修正层为图片添加印章.拉伸图片或者修改图片等,并且图片通常会成为canvas上的焦点.用HTML5 Canvas API内置的几个简单命令可以轻松地为canvas添加图片内容. 不过,图片增加了canvas操作的复杂度:必须等到图片完全加载后才能对其进行操作.浏览器通常会在页面脚本执行的同时异步加载图片.如果试图在图片未完全加载之前就将其呈现到canvas上,那么canvas将不会显示任何图片.因此,开发人员

canvas中的路径怎么存入数据库

问题描述 canvas中的路径怎么存入数据库 自定义了一个edittext然后canvas画的东西全部显示在了edittext上面,路径都保存下来了,怎么把canvas写的东西保存到数据库里面去? 然后取出来还可以还原 解决方案 把点坐标集合序列化成json存入数据库.

Android编程开发之在Canvas中利用Path绘制基本图形(圆形,矩形,椭圆,三角形等)_Android

本文实例讲述了Android编程开发之在Canvas中利用Path绘制基本图形的方法.分享给大家供大家参考,具体如下: 在Android中绘制基本的集合图形,本程序就是自定义一个View组件,程序重写该View组件的onDraw(Canvase)方法,然后在该Canvas上绘制大量的基本的集合图形. 直接上代码: 1.自定义的View组件代码: package com.infy.configuration; import android.content.Context; import andro