文章基于
MatterJs ^0.17.1
一、前言
碰撞是一个老生常谈的概念, 很多同学应该对它不陌生.
我们今天来详细讲一下MatterJs
中的碰撞.
什么是碰撞
在自然界中, 两个物体具有接触面积的行为叫做碰撞.
具象到代码中, 我们可以称两个对象的「具形」属性(宽、高、位置)具有交集, 则发生碰撞.
常见的碰撞有:
- 点与点
- 点与图形
- 点与线
- 线与图形
接下来我们就来看下MatterJs
是如何处理碰撞的.
二、寻找碰撞对
在MatterJs
中, 创建引擎的时候会创建一个Grid
实例.
这个Grid
模块是拿来查找整个物理世界中所有的body
的区域的. 然后存储一对对的区域对象.
当每一个物理刚体更新的时候, 会去更新对应的区域region
以及更新每一对pair
.
然后我们就可以拿到所有的刚体所在区域的列表.
Grid.update(grid, allBodies, engine, world.isModified);
gridPairs = grid.pairsList;
复制代码
拿到这些对应的刚体区块之后, 我们就需要去判断他们之间有没有碰撞了.
上面拿到的每一对刚体区块中都含有一对刚体包括其属性.
然后通过之前我们设置的刚体的属性collisionFilter
来判断刚体之间能否碰撞.
「物理世界中的刚体」中我们讲到过碰撞属性.
当下面的条件满足的时候, 就满足了碰撞的先决条件:
if (filterA.group === filterB.group && filterA.group !== 0)
return filterA.group > 0;
return (filterA.mask & filterB.category) !== 0 && (filterB.mask & filterA.category) !== 0;
复制代码
接下来, 我们可以看到MatterJs
使用了分离轴理论SAT
来处理碰撞条件.
「两个凸包多边形,当且仅当存在一条线,这两个多边形在这条线上的投影不相交,则这两个多边形也不相交」.
计算得出了可以碰撞的一对pair
.
两个刚体碰撞氛围三个阶段:
- collisionStart 碰撞开始
- collisionActive 碰撞中
- collisionEnd 碰撞结束
MatterJs
中会在三个阶段出发事件, 给用户反馈.
Events.trigger(engine, 'collisionStart', { pairs: pairs.collisionStart });
Events.trigger(engine, 'collisionActive', { pairs: pairs.collisionActive });
Events.trigger(engine, 'collisionEnd', { pairs: pairs.collisionEnd });
复制代码
源码性的东西就讲这些了, 下面我们来看下如何使用它.
三、如何使用matter中的碰撞检测
两个刚体碰撞, collisionStart
只会派发一次, collisionActive
只要还在碰撞状态就会触发.
结束碰撞会派发collisionEnd
事件.
我们在项目中监听这三个事件就可以得到我们想要的信息了.
举个例子?:
我们用一个矩形板子接住一个自由下落的小球.
/** 创建一个球 **/
var _cricle = Bodies.circle(200, 200, 50, {
mass: 0.1,
isStatic: false
}, 800);
var _cricle1 = Bodies.circle(200, 100, 50, {
mass: 0.1,
isStatic: false
}, 800);
var _rect = Bodies.rectangle(250, 500, 400, 50, {
isStatic: true,
});
Composite.add(world, [_cricle,_rect,_cricle1]);
/** 碰撞 **/
Matter.Events.on(engine, 'collisionStart', (e)=>{
console.log("888",e)
});
复制代码
先来看下物理世界:
我们来打印一下开始碰撞的信息:
可以看到刚体之间开始碰撞的信息会记录到e.source
中.
我们可以在刚体碰撞开始时做一些操作, 比如只需要一次碰撞就需要移除的操作.
接下来, 我们来看下collisionActive
触发:
Matter.Events.on(engine, 'collisionActive', (e)=>{
console.log("888",e.pairs)
});
复制代码
可以看下看到两个刚体处于一直碰撞状态, 那么每一帧都会派发出collisionActive
事件.
当一直处于碰撞状态, 是不会触发collisionEnd事件的.
接着我们来看下, 将板子的x位置移动一段距离, 使小球只能碰到一次,
/** 创建一个球 **/
var _cricle = Bodies.circle(200, 200, 50, {
mass: 0.1,
isStatic: false
});
var _rect = Bodies.rectangle(420, 500, 400, 50, {
isStatic: true,
});
Composite.add(world, [_cricle,_rect]);
/** 碰撞 **/
Matter.Events.on(engine, 'collisionStart', (e)=>{
console.log("start",e)
});
Matter.Events.on(engine, 'collisionActive', (e)=>{
console.log("active",e)
});
Matter.Events.on(engine, 'collisionEnd', (e)=>{
console.log("end",e)
});
复制代码
这样子我们可以看到:
我们来打印下触发事件携带的信息:
有点尴尬, 三个事件确实触发了, 但是都有一个问题, 我们的碰撞对不知道去哪里了, 说明还是有些bug的.
碰撞的实验只有大家自己去实验了, 这个bug不影响他的使用, 哈哈哈~~
碰撞检测的原理很多, 计算量也很多, 但是Matter
给我们提供的方法, 就能很好的应用与实践开发中了.
三、设置碰撞规则
Matter
中提供了两种设置碰撞规则的方法.
- 单一设置
group
category
和mask
配合使用
设置group
使用Body.nextGroup(isNonColliding)
方法, 设置group
的值.
当isNonColliding
为true
时, 生成自减的索引, 刚体不能碰撞, 为false
时, 生成自增的索引, 刚体可以碰撞.
举个例子?:
//可以碰撞
var group = Body.nextGroup(false);
//不可以碰撞
//var group = Body.nextGroup(false);
/** 创建一个球 **/
var _cricle = Bodies.circle(200, 200, 50, {
mass: 0.1,
isStatic: false,
collisionFilter: {
group: group
}
});
var _rect = Bodies.rectangle(250, 500, 400, 50, {
isStatic: true,
collisionFilter: {
group: group
}
});
Composite.add(world, [_cricle, _rect]);
复制代码
通过group
的设置来判断是否可以碰撞.
//可以碰撞
var group = Body.nextGroup(false);
//不可以碰撞
//var group = Body.nextGroup(false);
复制代码
设置category和mask
当A的category
和B的mask
一样的时候, 两者可以碰撞.
举个例子?:
我们指定_cricle
的category
为categoryB
,_rect
的category
为categoryB
, mask
为categoryA
,
这样子是不会出现碰撞的.
var categoryA = Body.nextCategory(),
categoryB = Body.nextCategory();
/** 创建一个球 **/
var _cricle = Bodies.circle(200, 200, 50, {
mass: 0.1,
isStatic: false,
collisionFilter: {
category: categoryB,
}
});
var _rect = Bodies.rectangle(250, 500, 400, 50, {
isStatic: true,
collisionFilter: {
category: categoryB,
mask: categoryA
}
});
Composite.add(world, [_cricle, _rect]);
复制代码
只有当_rect
的 mask
为categoryA
时, 两个刚体才会碰撞.
这个方法比group
多了更多的可匹配性, 我们可以更好的操作碰撞.
四、总结
本章总结
这一章主要讲了碰撞的三个阶段以及他们的触发事件:
- collisionStart
- collisionActive
- collisionEnd
同时也讲到了如何使用刚体的collisionFilter
属性设置碰撞规则.
在写文档demo的过程中, 也碰到了一个bug, 看来Matter
也有不尽如人意的地方.
全文总结
至此, MatterJs
全部就讲完了, 写这个系列也是心血来潮, 同时笔者自己也用过多次该库, 也被其轻量级和易上手所倾倒.
写文章过程中也发现了很多问题, 有很多部分源码阅读不够仔细深入, 写的可能有点出入, 望各位看官勿怪.
后续版本修改, 或是有了新的想法, 会继续更新这一系列文章, 有空也会写写实战小游戏满足各位.
往期文章呈现:
文章粗浅, 望诸位不吝您的评论和点赞~
注: 本文系作者呕心沥血之作,转载须声明