文章基于
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’ | 类型标识 |
parent | null | 父级复合体, 可以是world或者其他复合体 |
isModified | false | 复合体是否被修改 |
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);
复制代码
得到的结果如图:
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' } });
复制代码
我们会得到这样子的一条锁链:
链式的复合体能帮助我们做到很多操作, 包括搭建桥梁, 搭建绳索等.
3、Composites.mesh
函数: Composites.mesh = function(composite, columns, rows, crossBrace, options)
参数:
- composite 目标复合体
- columns 列数
- rows 行数
- crossBrace 是否允许交叉
- options 配置
这个方法可以把复合体中所有的刚体约束链接起来.
crossBrace
为true
时允许交叉相连, 否则不允许.
举个例子?:
把我们之前的网格堆叠复合体链接约束.
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);
复制代码
我们渲染出来可以看到这样子的复合体:
左边是可以交叉的, 右边则不是.
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);
复制代码
我们可以得到一个这样子的金字塔的复合体:
我们可以看到, 行数少了一个.
5、其他
Matter.Composites
模块还提供了牛顿摆球、小车、和球网三个复合材料.
他们的实现也是通过刚体、约束和复合体拼合而成的, 这里就不多讲了, 可以自行查看官网实例.
四、总结
本章节我们介绍了matterJs
中的复合体.
了解到如何去创建一个复合体, 同时也了解了如何对一个复合体去进行操作.
- 增删改查
- 平移、旋转、缩放
同时我们也学习了Matter.Composites
提供给的一些巧妙的特殊的复合体.
- 堆叠
- 链式
- 网
- 金字塔
- …
学习其方法, 我们还可以自定义一些我们自己的特殊复合体.
这样就可以打造更加丰富复杂的物理场景了.
我们在这一章了解到「约束」的概念, 「下一章」我们将着重介绍物理世界中物体与物体之间的约束.