物理世界中的碰撞检测-MatterJs(六)

文章基于 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)
});
复制代码

先来看下物理世界:

WechatIMG286.png

我们来打印一下开始碰撞的信息:

WechatIMG282.png
可以看到刚体之间开始碰撞的信息会记录到e.source中.
我们可以在刚体碰撞开始时做一些操作, 比如只需要一次碰撞就需要移除的操作.

接下来, 我们来看下collisionActive触发:

Matter.Events.on(engine, 'collisionActive', (e)=>{
    console.log("888",e.pairs)
});
复制代码

WechatIMG283.png
可以看下看到两个刚体处于一直碰撞状态, 那么每一帧都会派发出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)
});
复制代码

这样子我们可以看到:

WechatIMG286.png
我们来打印下触发事件携带的信息:

WechatIMG284.png

有点尴尬, 三个事件确实触发了, 但是都有一个问题, 我们的碰撞对不知道去哪里了, 说明还是有些bug的.

碰撞的实验只有大家自己去实验了, 这个bug不影响他的使用, 哈哈哈~~

碰撞检测的原理很多, 计算量也很多, 但是Matter给我们提供的方法, 就能很好的应用与实践开发中了.

三、设置碰撞规则

Matter中提供了两种设置碰撞规则的方法.

  • 单一设置group
  • categorymask配合使用

设置group

使用Body.nextGroup(isNonColliding)方法, 设置group 的值.
isNonCollidingtrue时, 生成自减的索引, 刚体不能碰撞, 为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一样的时候, 两者可以碰撞.

举个例子?:
我们指定_criclecategorycategoryB,_rectcategorycategoryB, maskcategoryA,
这样子是不会出现碰撞的.

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]);
复制代码

只有当_rectmaskcategoryA时, 两个刚体才会碰撞.
这个方法比group多了更多的可匹配性, 我们可以更好的操作碰撞.

四、总结

本章总结

这一章主要讲了碰撞的三个阶段以及他们的触发事件:

  • collisionStart
  • collisionActive
  • collisionEnd

同时也讲到了如何使用刚体的collisionFilter属性设置碰撞规则.

在写文档demo的过程中, 也碰到了一个bug, 看来Matter也有不尽如人意的地方.

全文总结

至此, MatterJs全部就讲完了, 写这个系列也是心血来潮, 同时笔者自己也用过多次该库, 也被其轻量级和易上手所倾倒.

写文章过程中也发现了很多问题, 有很多部分源码阅读不够仔细深入, 写的可能有点出入, 望各位看官勿怪.

后续版本修改, 或是有了新的想法, 会继续更新这一系列文章, 有空也会写写实战小游戏满足各位.

往期文章呈现:

文章粗浅, 望诸位不吝您的评论和点赞~
注: 本文系作者呕心沥血之作,转载须声明

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享