阅读 181

物理世界中的复合体-MatterJs(四)

文章基于 MatterJs ^0.17.1

一、前言

「上一章」中, 我们介绍了matterJs中的刚体.
了解了刚体的属性和方法, 以及其如何存在于matterJs中.
这一章我们讲一下物理世界中的另一个物体的存在 ---- 复合体Composite.

什么是复合体呢?
由刚体和复合材料通过某种约束组合在一起的的物体就叫做复合体
复合体可以看做是一个刚体, 他的物理属性是由多个刚体和约束相互制约得出来的.

matterJs中, Composite模块提供了复合体的基础属性和方法. 同时matrerjs还为我们提供了一系列的复合材料, 包括:

  • stack 堆叠
  • chain 链
  • mesh 网格
  • pyramid 锥体
  • newtonsCradle 牛顿摆球
  • car 小车
  • softBody 软体材料

matterJs中, World也是一个复合体, 具有复合体的一切属性和方法.

接下来, 我们就一起来看下matterJs中的复合体吧.

二、Matter..Composite模块

这个模块为matterJs提供了创建和处理复合体的方法.

1、Composite.create

函数: Composite.create = function(options)
参数:

  • options 复合体的配置属性

创建一个新的复合体, 我们可以为他提供以下的配置:

属性名默认值介绍
type'composite'类型标识
parentnull父级复合体, 可以是world或者其他复合体
isModifiedfalse复合体是否被修改
bodies[]复合体中的所有刚体
constraints[]复合体中的所有约束
composites[]复合体中的所有复合体
label'Composite'名字标识
plugin{}注册插件

举个例子🌰:

/** 创建一个复合体**/
const _composite = Composite.create({
    label: "test",
    isModified: true
});
Composite.add(world,_composite);
/** 创建一个圆形 **/
var _cricle = Bodies.circle(50, 50, 50, {
    mass: 0.1,
    isStatic: true
}, 80);
Composite.add(_composite, _cricle);
复制代码

这里我们往world中添加了一个composite, 再往这个composite中添加了一个圆形刚体.

在复合体中我们不要去直接修改这些属性, 而是要通过它提供的方法去修改.

2、Composite.setModified

函数: Composite.setModified = function(composite, isModified, updateParents, updateChildren)
参数:

  • composite 需要被修改的复合体
  • isModified 是否可以修改属性的值 true | false
  • updateParents 是否修改父级 true 修改 默认false不修改
  • updateChildren 是否修改子级 true 修改 默认false不修改

修改composite上的isModified属性的时候, 我们需要使用这个方法.
不过一般我们不会使用到这个方法.在我们向复合体中添加、移除物体或者对复合体旋转、平移等操操作的时候都需要将isModified置为true.
matterJs已经为我们做了这个操作.

Composite.setModified(composite, true, true, false);
复制代码

就比如我们上面的add操作, 其实也将isModified置为true了.

3、Composite.add

函数: Composite.add = function(composite, object)
参数:

  • composite 需要添加进入的目标复合体
  • object 需要添加的物体 可以是单个也可以是多个集合的一个数组

在添加之前会派发出beforeAdd事件:

Events.trigger(composite, 'beforeAdd', { object: object });
复制代码

在添加之后会派发出afterAdd事件:

Events.trigger(composite, 'afterAdd', { object: object });
复制代码

我们通过刚体、复合体或者约束上的type属性判断添加的类型, 执行不同的添加方法.

switch (obj.type) {
    case 'body':
        if (obj.parent !== obj) {
            Common.warn('Composite.add: skipped adding a compound body part (you must add its parent instead)');
            break;
        }
        Composite.addBody(composite, obj);
        break;
    case 'constraint':
        Composite.addConstraint(composite, obj);
        break;
    case 'composite':
        Composite.addComposite(composite, obj);
        break;
    case 'mouseConstraint':
        Composite.addConstraint(composite, obj.constraint);
        break;
}
复制代码

同时也支持我们手动添加不同类型的物体.
具有以下的添加方法:

  • Composite.addComposite = function(compositeA, compositeB) 添加composite
  • Composite.addBody = function(composite, body) 添加body
  • Composite.addConstraint = function(composite, constraint) 添加约束

这里就不细讲了, 添加方法都是大同小异.

4、Composite.remove

函数: Composite.remove = function(composite, object, deep)
参数:

  • composite 需要删除的复合体的父级
  • object 需要从目标复合体删除的物体
  • deep 是否递归删除

在删除之前会派发beforeRemove事件:

Events.trigger(composite, 'beforeRemove', { object: object });
复制代码

在删除之后会派发afterRemove事件:

Events.trigger(composite, 'afterRemove', { object: object });
复制代码

我们通过刚体、复合体或者约束上的type属性判断删除的类型, 执行不同的添加方法.
add方法类似.
具有以下的删除方法:

  • Composite.removeComposite = function(compositeA, compositeB, deep) 删除composite
  • Composite.removeCompositeAt = function(composite, position) 删除第几个composite
  • Composite.removeBody = function(composite, body, deep) 删除body
  • Composite.removeBodyAt = function(composite, position) 删除第几个body
  • Composite.removeConstraint = function(composite, constraint, deep) 删除constraint
  • Composite.removeConstraintAt = function(composite, position) 删除第几个constraint

这里就不细讲了, 删除方法都是大同小异.

5、Composite.clear

函数: Composite.clear = function(composite, keepStatic, deep)
参数:

  • composite 需要清除的目标复合体
  • keepStatic 是否保持刚体状态
  • deep 是否深度清除

从给定的复合体中删除指定的刚体、约束和复合体.

keepStatic为true的时候, 静态刚体不会被清除.
deep为true时, 子级的composite也会被清除.

举个例子🌰:
清除我们上面创建的圆.

/** 清除 **/
Composite.clear(_composite, false, true);
复制代码

很简单吧.一般切换场景时我们就会用到了.

6、Composite.allBodies

函数: Composite.allBodies = function(composite)
参数:

  • composite 目标复合体

该方法会返回目标复合体中的所有刚体.
递归遍历出所有的子刚体:

var bodies = [].concat(composite.bodies);
for (var i = 0; i < composite.composites.length; i++)
    bodies = bodies.concat(Composite.allBodies(composite.composites[i]));
return bodies;
复制代码

7、Composite.allConstraints

函数: Composite.allConstraints = function(composite)
参数:

  • composite 目标复合体

该方法会返回目标复合体中的所有约束.
递归遍历出所有的子约束:

var constraints = [].concat(composite.constraints);
for (var i = 0; i < composite.composites.length; i++)
    constraints = constraints.concat(Composite.allConstraints(composite.composites[i]));
return constraints;
复制代码

8、Composite.allComposites

函数: Composite.allComposites = function(composite)
参数:

  • composite 目标复合体

该方法会返回目标复合体中的所有复合体.
递归遍历出所有的子复合体:

var composites = [].concat(composite.composites);
for (var i = 0; i < composite.composites.length; i++)
    composites = composites.concat(Composite.allComposites(composite.composites[i]));
return composites;
复制代码

9、Composite.get

函数: Composite.get = function(composite, id, type)
参数:

  • composite 目标复合体
  • id 需要查找的物体id
  • type 需要查找的物体type

该方法用于查找指定id的对应type的物体(刚体、约束、复合体);
其实即是通过上面的三个方法得到全部的刚体、约束、复合体, 然后遍历得到指定的物体.

举个例子🌰:

/** 创建一个圆形 **/
var _cricle = Bodies.circle(50, 50, 50, {
    id: 11,
    mass: 0.1,
    isStatic: true
}, 80);
Composite.add(_composite, _cricle);
/** 查找 **/
const cc = Composite.get(_composite,11,"body");
console.log(cc);//获得刚体_cricle
复制代码

10、Composite.move

函数: Composite.move = function(compositeA, objects, compositeB)
参数:

  • compositeA 从哪里来
  • objects 目标对象
  • compositeB 到哪里去

该方法用于将一个对象(刚体、约束、复合体)从A复合体移动到B复合体.
其实相当于先删除再添加.

Composite.remove(compositeA, objects);
Composite.add(compositeB, objects);
复制代码

举个例子🌰:
还是刚才那个圆, 我们再创建一个复合体A, 将圆移入里面.

const _compositeA = Composite.create({
    label: "testA",
    isModified: true
});
Composite.add(world, _compositeA);
Composite.move(_composite,_cricle,_compositeA);
console.log(_composite);//没有了圆
console.log(_compositeA);//圆变成他的子刚体
复制代码

移动(改变父级)不会改变原来刚体的属性

11、Composite.rebase

函数: Composite.rebase = function(composite)
参数:

  • composite 目标复合体

递归的为目标复合体中的所有对象分配id.

 var objects = Composite.allBodies(composite)
            .concat(Composite.allConstraints(composite))
            .concat(Composite.allComposites(composite));
for (var i = 0; i < objects.length; i++) {
    objects[i].id = Common.nextId();
}
复制代码

这个方法只是拿来防止id重复的.

12、Composite.translate

函数: Composite.translate = function(composite, translation, recursive)
参数:

  • composite 目标复合体
  • translation 相对位置
  • recursive 是否要递归所有的子级中的

这个方法可以用来平移复合体中所有的刚体.

举个例子🌰:
将刚才我们的圆移动到位置{ x: 200, y: 100 }

Composite.translate(_compositeA,{x: 200,y: 100},false);
复制代码

13、 Composite.rotate

函数: Composite.rotate = function(composite, rotation, point, recursive)
参数:

  • composite 目标复合体
  • rotation 角度 弧度制
  • point 相对位置
  • recursive 是否要递归所有的子级中的

这个方法用来旋转目标复合体中的所有刚体.

举个例子🌰:
在上面例子的基础上我们再添加一个梯形. 并旋转_compositeA中的所有刚体.

var _trapezoid = Bodies.trapezoid(100, 100, 50, 50, 0.2, {
    isStatic: true,
});
Composite.add(_compositeA, _trapezoid);
Composite.rotate(_compositeA, 30 * Math.PI / 180, {
    x: 100,
    y: 100
}, false);
复制代码

14、Composite.scale

函数: Composite.scale = function(composite, scaleX, scaleY, point, recursive)
参数:

  • composite 目标复合体
  • scaleX X轴缩放
  • scaleY Y轴缩放
  • point 相对位置
  • recursive 是否要递归所有的子级中的

这个方法用来缩放目标复合体中的所有刚体.
举个例子🌰:
继续上面的例子, 把我们的梯形和圆都缩小0.5.

Composite.scale(_compositeA, 0.5, 0.5, {
    x: 100,
    y: 100
}, false);
复制代码

15、Composite.bounds

函数: Composite.bounds = function(composite)
参数:

  • composite 目标复合体

返回复合体的边界值, 包括最大和最小值.

举个例子🌰:
获取我们上面例子中的_compositeA的边界值.

const bounds = Composite.bounds(_compositeA);
console.log("bounds",bounds);
/** 返回
    max: {x: 177.42971950691484, y: 184.14508662801853}
    min: {x: 83.15616393417599, y: 83.77374476575727}
**/
复制代码

至此, 我们就讲完了Composite中的所有方法.

我们可以用这些方法做什么呢?

  • 复合体中刚体、约束、复合体的增删改查.
  • 复合体中刚体、约束、复合体的平移、旋转、缩放

matterJs中, 对于composite也同样有个工厂方法, 为我们提供了一些复合材料.
接下来我们就来讲下composite的工厂--- Composites模块.

三、Matter.Composites模块

matterJs中, 该模块用于搭建一些特殊的复合材料.

1、Composites.stack

函数: Composites.stack = function(xx, yy, columns, rows, columnGap, rowGap, callback)
参数:

  • xx 该复合体的x坐标位置
  • yy 该复合体的y坐标位置
  • columns 列数
  • rows 行数
  • columnGap 列间距
  • rowGap 行间距
  • callback 回调函数

回调函数callback具有6个参数, (x, y, column, row, lastBody, i)
x, y, column, row, lastBody -- 上一个刚体, i -- 第几个刚体

这个方法用来创建一个网格布局的刚体堆叠在一起复合体.

我们来看个例子🌰:
在世界中的{x: 100,y: 100}位置创建一个3*4, 相邻间距为10的网格布局的复合体.

var stack = Composites.stack(100, 100, 3, 4, 10, 10, function (x, y,col,row,lastbody,i) {
        return Bodies.rectangle(x, y, 40, 40,{isStatic: true});
    });
    Composite.add(world, stack);
复制代码

得到的结果如图:

WechatIMG253.png

2、 Composites.chain

函数: Composites.chain = function(composite, xOffsetA, yOffsetA, xOffsetB, yOffsetB, options)
参数:

  • composite 目标复合体
  • xOffsetA A物体连接点x方向的偏移量
  • yOffsetA A物体连接点y方向的偏移量
  • xOffsetB B物体连接点x方向的偏移量
  • yOffsetB B物体连接点y方向的偏移量
  • options 约束的配置

这个方法用来创建类似链子的复合体.
其中, xOffsetA, yOffsetA, xOffsetB, yOffsetB是两个刚体进行约束的点的位置.
约束我们会在「下一章」讲解.

举个例子🌰:
我们创建一条锁链. 使用Body.nextGroup(true)防止锁链之间碰撞.
刚体的nextGroup方法我们在「物理世界中的刚体」一文中说过.

var group = Body.nextGroup(true);
var chain = Composites.stack(0, 50, 8, 1, 10, 10, function(x, y) {
    return Bodies.rectangle(x, y, 50, 20, { collisionFilter: { group: group,isStatic: true } });
});
Composite.add(world, chain);
Composites.chain(chain, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2, render: { type: 'line' } });
复制代码

我们会得到这样子的一条锁链:

WechatIMG254.png 链式的复合体能帮助我们做到很多操作, 包括搭建桥梁, 搭建绳索等.

3、Composites.mesh

函数: Composites.mesh = function(composite, columns, rows, crossBrace, options)
参数:

  • composite 目标复合体
  • columns 列数
  • rows 行数
  • crossBrace 是否允许交叉
  • options 配置

这个方法可以把复合体中所有的刚体约束链接起来.
crossBracetrue时允许交叉相连, 否则不允许.

举个例子🌰:
把我们之前的网格堆叠复合体链接约束.

var stack = Composites.stack(100, 100, 3, 4, 10, 10, function (x, y,col,row,lastbody,i) {
    return Bodies.rectangle(x, y, 40, 40,{isStatic: true});
});
var stack1 = Composites.stack(300, 100, 3, 4, 10, 10, function (x, y,col,row,lastbody,i) {
    return Bodies.rectangle(x, y, 40, 40,{isStatic: true});
});
Composite.add(world, [stack,stack1]);
Composites.mesh(stack,3,4,true);
Composites.mesh(stack1,3,4,false);
复制代码

我们渲染出来可以看到这样子的复合体:

WechatIMG257.png 左边是可以交叉的, 右边则不是.

4、Composites.pyramid

函数: Composites.pyramid = function(xx, yy, columns, rows, columnGap, rowGap, callback)
参数:

  • xx 该复合体的x坐标位置
  • yy 该复合体的y坐标位置
  • columns 列数
  • rows 行数
  • columnGap 列间距
  • rowGap 行间距
  • callback 回调函数

这个方法可以用来创建类似金字塔堆叠的复合体. 和stack很像. 举个例子🌰:

var pyramid = Composites.pyramid(100, 100, 5, 4, 0, 0, function (x, y, col, row, lastbody, i) {
    return Bodies.rectangle(x, y, 40, 40, {
        isStatic: true
    });
});
Composite.add(world,pyramid);
复制代码

我们可以得到一个这样子的金字塔的复合体:

WechatIMG258.png 我们可以看到, 行数少了一个.

5、其他

Matter.Composites模块还提供了牛顿摆球、小车、和球网三个复合材料.
他们的实现也是通过刚体、约束和复合体拼合而成的, 这里就不多讲了, 可以自行查看官网实例.

四、总结

本章节我们介绍了matterJs中的复合体.
了解到如何去创建一个复合体, 同时也了解了如何对一个复合体去进行操作.

  • 增删改查
  • 平移、旋转、缩放

同时我们也学习了Matter.Composites提供给的一些巧妙的特殊的复合体.

  • 堆叠
  • 链式
  • 金字塔
  • ...

学习其方法, 我们还可以自定义一些我们自己的特殊复合体. 这样就可以打造更加丰富复杂的物理场景了.

我们在这一章了解到「约束」的概念, 「下一章」我们将着重介绍物理世界中物体与物体之间的约束.

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

文章分类
前端
文章标签