├── 6-practice ├── 6-2-add-body │ ├── 6-2-add-body │ │ ├── box.png │ │ ├── index.html │ │ └── js │ │ │ └── test.js │ └── 6-2-add-body.md ├── 6-3-add-bound │ ├── 6-3-add-bound │ │ ├── box.png │ │ ├── index.html │ │ └── js │ │ │ └── test.js │ └── 6-3-add-bound.md ├── 6-5-handle-contact │ ├── 6-5-handle-contact │ │ ├── box.png │ │ ├── index.html │ │ └── js │ │ │ └── test.js │ └── 6-5-handle-contact.md ├── 6-4-mouse-operate-body │ ├── 6-4-mouse-operate-body │ │ ├── box.png │ │ ├── index.html │ │ └── js │ │ │ └── test.js │ └── 6-4-mouse-operate-body.md ├── README.md └── 6-1-create-and-init-world │ ├── 6-1-create-and-init-world │ ├── index.html │ └── js │ │ └── test.js │ └── 6-1-create-and-init-world.md ├── 7-api ├── 7-1-common-apis.md ├── README.md ├── 7-2-collisions-apis.md └── 7-3-dynamics-apis.md ├── 1-basic ├── README.md ├── 1-2-graphic-engine.md ├── 1-1-physical-engine.md └── 1-3-hello-box2d.md ├── 3-body ├── 3-3-cicle-shape.md ├── 3-2-box-shape.md ├── README.md ├── 3-4-poly-shape.md ├── 3-1-shape.md └── 3-5-shape-to-body.md ├── 5-operation ├── README.md ├── 5-2-get-contact-list.md ├── 5-1-mouse-get-body.md ├── 5-4-set-body-attributes.md ├── 5-3-get-body-attributes.md └── 5-5-body-with-image.md ├── 4-joint ├── 4-3-prismatic-joint.md ├── 4-2-revolute-joint.md ├── 4-4-pulley-joint.md ├── README.md ├── 4-1-distance-joint.md └── 4-5-gear-joint.md ├── 2-world └── README.md └── README.md /6-practice/6-2-add-body/6-2-add-body/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godbasin/box2djs-tutorial/HEAD/6-practice/6-2-add-body/6-2-add-body/box.png -------------------------------------------------------------------------------- /6-practice/6-3-add-bound/6-3-add-bound/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godbasin/box2djs-tutorial/HEAD/6-practice/6-3-add-bound/6-3-add-bound/box.png -------------------------------------------------------------------------------- /6-practice/6-5-handle-contact/6-5-handle-contact/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godbasin/box2djs-tutorial/HEAD/6-practice/6-5-handle-contact/6-5-handle-contact/box.png -------------------------------------------------------------------------------- /6-practice/6-4-mouse-operate-body/6-4-mouse-operate-body/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godbasin/box2djs-tutorial/HEAD/6-practice/6-4-mouse-operate-body/6-4-mouse-operate-body/box.png -------------------------------------------------------------------------------- /7-api/7-1-common-apis.md: -------------------------------------------------------------------------------- 1 | ## common 2 | ### math 3 | - `b2Vec2`:二维向量 4 | - `b2Mat22`:一个由两个b2Vec2组成的2*2方阵,可直接由两个b2Vec2(col1、col2)构造或者由一个角度值构造 5 | - `b2Math`:基本数学运算,如向量和矩阵的计算、产生随机数等 6 | 7 | ### bsSettings 8 | - `bsSettings`:基础系统配置,如pi的值、每秒的时间、每米的大小、睡眠时间等等 -------------------------------------------------------------------------------- /1-basic/README.md: -------------------------------------------------------------------------------- 1 | ## 基本概念 2 | ### 总览 3 | 1. 基本概念 4 | [1.1 有关物理引擎](https://github.com/godbasin/box2djs-tutorial/tree/master/1-basic/1-1-physical-engine.md) 5 | [1.2 有关图像引擎](https://github.com/godbasin/box2djs-tutorial/tree/master/1-basic/1-2-graphic-engine.md) 6 | [1.3 有关Box2D](https://github.com/godbasin/box2djs-tutorial/tree/master/1-basic/1-3-hello-box2d.md) 7 | -------------------------------------------------------------------------------- /3-body/3-3-cicle-shape.md: -------------------------------------------------------------------------------- 1 | ## 圆形 2 | ### b2CircleDef 3 | 使用基类b2CircleDef创建一个圆形形状,并且设置其属性。 4 | 5 | - `b2CircleDef` 6 | - 继承于b2ShapeDef 7 | - type为e_circleShape 8 | - radius来表示半径值 9 | 10 | ``` javascript 11 | var Shape = new b2CircleDef(); //创建一个圆形Shape,然后设置有关Shape的属性 12 | Shape.radius = 20; //设置圆形的半径 13 | Shape.localPosition.Set(0, 0); //设置圆形的偏移量 14 | Shape.density = 1.0; //设置圆形的密度 15 | Shape.restitution = .3; //设置圆形的弹性 16 | Shape.friction = 1; //设置圆形的摩擦因子 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /3-body/3-2-box-shape.md: -------------------------------------------------------------------------------- 1 | ## 矩形 2 | ### b2BoxDef 3 | 使用基类b2BoxDef创建一个矩形形状,并且设置其大小、密度、弹性、摩擦力等属性。 4 | 5 | - `b2BoxDef` 6 | - 继承于b2ShapeDef 7 | - type为e_ boxShape 8 | - extents来表示区域值 9 | 10 | ``` javascript 11 | var Shape = new b2BoxDef(); //创建一个形状Shape,然后设置有关Shape的属性 12 | Shape.extents.Set(1200, 5); //设置矩形高、宽, 13 | Shape.density = 0; //设置矩形的密度 14 | Shape.restitution = .3; //设置矩形的弹性 15 | Shape.friction = 1; //设置矩形的摩擦因子,可以设置为0-1之间任意一个数,0表示光滑,1表示强摩擦 16 | ``` 17 | 18 | 备注:如果将密度设置为0或者null,那么该形状是静止,即不可被移动的地面或者墙体等。 19 | 20 | 21 | -------------------------------------------------------------------------------- /5-operation/README.md: -------------------------------------------------------------------------------- 1 | ## 操作(operation) 2 | ### 总览 3 | 5. 操作(operation) 4 | [5.1 鼠标获取刚体](https://github.com/godbasin/box2djs-tutorial/tree/master/5-operation/5-1-mouse-get-body.md) 5 | [5.2 获取参与碰撞的刚体](https://github.com/godbasin/box2djs-tutorial/tree/master/5-operation/5-2-get-contact-list.md) 6 | [5.3 获取刚体的各属性](https://github.com/godbasin/box2djs-tutorial/tree/master/5-operation/5-3-get-body-attributes.md) 7 | [5.4 为刚体设置属性](https://github.com/godbasin/box2djs-tutorial/tree/master/5-operation/5-4-set-body-attributes.md) 8 | [5.5 绘制功能](https://github.com/godbasin/box2djs-tutorial/tree/master/5-operation/5-5-body-with-image.md) 9 | 10 | -------------------------------------------------------------------------------- /6-practice/README.md: -------------------------------------------------------------------------------- 1 | ## 创建一个物理世界吧 2 | ### 总览 3 | 6. 创建一个物理世界吧 4 | [6.1 创建世界并初始化](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-1-create-and-init-world/6-1-create-and-init-world.md) 5 | [6.3 添加刚体](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-2-add-body/6-2-add-body.md) 6 | [6.4 添加边界](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-3-add-bound/6-3-add-bound.md) 7 | [6.4 鼠标操作刚体](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-4-mouse-operate-body/6-4-mouse-operate-body.md) 8 | [6.5 处理碰撞刚体](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-5-handle-contact/6-5-handle-contact.md) -------------------------------------------------------------------------------- /7-api/README.md: -------------------------------------------------------------------------------- 1 | ## Box2D api 2 | ### Box2D分类 3 | Box2D所有的api可参照[Box2D_api](http://www.kyucon.com/doc/box2d/)。 4 | 5 | 可发现,Box2D具体分为三大类: 6 | - 碰撞类(collision):碰撞模块定义了形状,broad-phase算法,碰撞的功能/查询 7 | - 基础类(common):包括基础系统配置、数据类型、基础数学运算 8 | - 动力学类(dynamics):包括模拟物理世界,刚体(body),接触管理(contacts),以及关节(joint) 9 | 10 | 具体的说明是个人整理的,有些参照网上资源,可能不完整且有偏差。 11 | 12 | ### 总览 13 | 6. api 14 | [6.1 碰撞类(collision)](https://github.com/godbasin/box2djs-tutorial/tree/master/6-api/6-1-common-apis.md) 15 | [6.2 基础类(common)](https://github.com/godbasin/box2djs-tutorial/tree/master/6-api/6-2-collisions-apis.md) 16 | [6.3 动力学类(dynamics)](https://github.com/godbasin/box2djs-tutorial/tree/master/6-api/6-3-dynamics-apis.md) -------------------------------------------------------------------------------- /4-joint/4-3-prismatic-joint.md: -------------------------------------------------------------------------------- 1 | ## 移动关节(prismatic-joint) 2 | ### 说明 3 | 移动关节允许两个物体沿指定轴相对移动,它会阻止相对旋转,简而言之,就是关节对物体的影响只体现在轴向方向。因此,移动关节只有一个自由度。 4 | 5 | 移动关节类似旋转关节,只是将旋转角度换成了平移。关节中的两个刚体的相对运动只能发生在其轴向上。 6 | 7 | ### b2PrismaticJointDef 8 | 首先创建两个物体,将其放入世界里,然后使用基类b2PrismaticJointDef创建一个移动关节,设置其各个参数,再将其放入到世界里即可。 9 | 10 | ``` javascript 11 | var jointDefPrismatic = new b2PrismaticJointDef(); //创建一个移动关节jointDefPrismatic 12 | jointDefPrismatic.anchorPoint.Set(700, 565); //一般选择两个锚点的中心点 13 | jointDefPrismatic.axis.Set(1, 0); //两物体只沿轴向方向有相对运动 14 | jointDefPrismatic.body1 = Body1; //移动关节的一端连接到Body1上 15 | jointDefPrismatic.body2 = Body2; //移动关节的另一端连接到Body2上 16 | var jointPrismatic= world.CreateJoint(jointDefPrismatic); //将设置好参数的移动关节放到世界里 17 | ``` -------------------------------------------------------------------------------- /4-joint/4-2-revolute-joint.md: -------------------------------------------------------------------------------- 1 | ## 旋转关节(revolute-joint) 2 | ### 说明 3 | 一个旋转关节会强制两个物体共享一个锚点,即铰接点。旋转关节只有一个自由度:两个物体的相对旋转。 4 | 5 | 旋转关节即相当于将两个物体用钉子钉在一起,两个物体都可以绕这颗钉子旋转。当然,如果其中一个物体为地面的话,就相当于将物体钉在地面上,物体都可以绕这颗钉子旋转。 6 | 7 | ### b2RevoluteJointDef 8 | 首先创建两个物体,然后将它们放在世界里,使用基类b2RevoluteJointDef创建一个旋转关节,设定铰接点,及旋转关节连接的是哪两个物体之后即可将其放入到世界里。 9 | 10 | ``` javascript 11 | var jointDefRevolute = new b2RevoluteJointDef(); //创建一个旋转关节jointDefRevolute 12 | jointDefRevolute.anchorPoint.Set(450, 450); //设定铰接点坐标 13 | jointDefRevolute.body1 = Body1; //设定旋转关节一端连接Body1 14 | jointDefRevolute.body2 = Body2; //设定旋转关节另一端连接Body2 15 | var jointRevolute= world.CreateJoint(jointDefRevolute); //将旋转关节放入世界中 16 | jointRevolute.SetMotorSpeed(100); //设置旋转关节马达的速度 17 | ``` -------------------------------------------------------------------------------- /3-body/README.md: -------------------------------------------------------------------------------- 1 | ## 形状-刚体 2 | ### 形状-刚体-关节 3 | 创建了box2d世界后,我们需要创建一些刚体和关节添加进去,比如游戏中的边界、元素等,使得世界更丰富一些。 4 | 5 | 接下来就创造一些基本形状,然后用这些形状组成刚体,刚体又可以进一步组合成关节,换言之,世界即是由刚体和关节组成的世界。 6 | 7 | box2d中创建刚体、关节的过程类似于堆积木,由形状构成刚体,由刚体构成关节,稍微不同的地方是,这里的形状可以多次使用,其过程: 8 | `形状 -> 刚体 -> 关节` 9 | 10 | 此处需要注意的是,box2d只能创建顶点数不超过8的凸多边形。 11 | 12 | ### 总览 13 | 3. 形状(shape)-刚体(body) 14 | [3.1 形状](https://github.com/godbasin/box2djs-tutorial/tree/master/3-body/3-1-shape.md) 15 | [3.2 矩形](https://github.com/godbasin/box2djs-tutorial/tree/master/3-body/3-2-box-shape.md) 16 | [3.3 圆形](https://github.com/godbasin/box2djs-tutorial/tree/master/3-body/3-3-cicle-shape.md) 17 | [3.4 凸多边形](https://github.com/godbasin/box2djs-tutorial/tree/master/3-body/3-4-poly-shape.md) 18 | [3.5 由形状到刚体](https://github.com/godbasin/box2djs-tutorial/tree/master/3-body/3-5-shape-to-body.md) 19 | -------------------------------------------------------------------------------- /6-practice/6-1-create-and-init-world/6-1-create-and-init-world/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
7 | ```
8 |
9 | 2. 设置形状定义的userData为图片id
10 |
11 | ``` javascript
12 | boxSd.userData = document.getElementById('box');
13 | ```
14 |
15 | 3. 在绘制世界时,将图片信息绘制进去
16 |
17 | ``` javascript
18 | // 绘画世界
19 | function drawWorld(world, context) {
20 | for (var b = world.m_bodyList; b != null; b = b.m_next) {
21 | for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
22 | if (s.GetUserData() != undefined) {
23 | // 使用数据包括图片
24 | var img = s.GetUserData();
25 |
26 | // 图片的长和宽
27 | var x = s.GetPosition().x;
28 | var y = s.GetPosition().y;
29 | var topleftX = - $(img).width() / 2;
30 | var topleftY = - $(img).height() / 2;
31 |
32 | context.save();
33 | context.translate(x, y);
34 | context.rotate(s.GetBody().GetRotation());
35 | context.drawImage(img, topleftX, topleftY);
36 | context.restore();
37 | }
38 | drawShape(s, context);
39 | }
40 | }
41 | }
42 | ```
43 |
44 | 注意,以上的方法只试用于圆形和矩形,多边形待验证。
45 |
46 | ### 绘制刚体形状
47 | 有时候我们需要将刚体和关节的形状绘制出来:
48 |
49 | ``` javascript
50 | // 从draw_world.js里面引用的绘画功能
51 | function drawShape(shape, context) {
52 | context.strokeStyle = '#003300';
53 | context.beginPath();
54 | switch (shape.m_type) {
55 | // 绘制圆
56 | case b2Shape.e_circleShape:
57 | var circle = shape;
58 | var pos = circle.m_position;
59 | var r = circle.m_radius;
60 | var segments = 16.0;
61 | var theta = 0.0;
62 | var dtheta = 2.0 * Math.PI / segments;
63 | // 画圆圈
64 | context.moveTo(pos.x + r, pos.y);
65 | for (var i = 0; i < segments; i++) {
66 | var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
67 | var v = b2Math.AddVV(pos, d);
68 | context.lineTo(v.x, v.y);
69 | theta += dtheta;
70 | }
71 | context.lineTo(pos.x + r, pos.y);
72 |
73 | // 画半径
74 | context.moveTo(pos.x, pos.y);
75 | var ax = circle.m_R.col1;
76 | var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
77 | context.lineTo(pos2.x, pos2.y);
78 | break;
79 | // 绘制多边形
80 | case b2Shape.e_polyShape:
81 | var poly = shape;
82 | var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
83 | context.moveTo(tV.x, tV.y);
84 | for (var i = 0; i < poly.m_vertexCount; i++) {
85 | var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
86 | context.lineTo(v.x, v.y);
87 | }
88 | context.lineTo(tV.x, tV.y);
89 | break;
90 | }
91 | context.stroke();
92 | }
93 | ```
94 |
95 | ## 绘制关节
96 | ### 绘制关节
97 | - 在绘制世界时加入关节的判断和绘制调用
98 |
99 | ``` javascript
100 | function drawWorld(world, context) {
101 | for (var j = world.m_jointList; j; j = j.m_next) {
102 | // 绘制关节
103 | drawJoint(j, context);
104 | }
105 | for (var b = world.m_bodyList; b; b = b.m_next) {
106 | for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
107 | // 绘制刚体形状
108 | drawShape(s, context);
109 | }
110 | }
111 | }
112 | ```
113 |
114 | - 绘制关节
115 |
116 | ``` javascript
117 | function drawJoint(joint, context) {
118 | var b1 = joint.m_body1;
119 | var b2 = joint.m_body2;
120 | var x1 = b1.m_position;
121 | var x2 = b2.m_position;
122 | var p1 = joint.GetAnchor1();
123 | var p2 = joint.GetAnchor2();
124 | context.strokeStyle ='#00eeee';
125 | context.beginPath();
126 | switch (joint.m_type) {
127 | // 绘制距离关节
128 | case b2Joint.e_distanceJoint:
129 | context.moveTo(p1.x, p1.y);
130 | context.lineTo(p2.x, p2.y);
131 | break;
132 |
133 | case b2Joint.e_pulleyJoint:
134 | // TODO
135 | break;
136 |
137 | default:
138 | if (b1 == world.m_groundBody) {
139 | context.moveTo(p1.x, p1.y);
140 | context.lineTo(x2.x, x2.y);
141 | }
142 | else if (b2 == world.m_groundBody) {
143 | context.moveTo(p1.x, p1.y);
144 | context.lineTo(x1.x, x1.y);
145 | }
146 | else {
147 | context.moveTo(x1.x, x1.y);
148 | context.lineTo(p1.x, p1.y);
149 | context.lineTo(x2.x, x2.y);
150 | context.lineTo(p2.x, p2.y);
151 | }
152 | break;
153 | }
154 | context.stroke();
155 | }
156 | ```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 本文为Box2D教程的目录总览。
2 |
3 | ## 前言
4 | ### 触碰Box2D
5 | Box2D其实是我在刚接触前端时的第一个项目,与它的结实主要是在学校参加教授的项目,在师兄师姐们的帮助下认识这个物理引擎。
6 |
7 | ### 教程说明
8 | 在以前端作为职业两年的这个时候,才发现Box2D相关的教程和说明很少,便产生了整理一份相关的说明和教程的想法。
9 |
10 | Box2Djs早已停止了维护,但我依然认为它是一个很棒很棒的库。
11 |
12 | 该教程中有些内容来自于当时的项目研究和调查,其中也有不少当年一起参与项目的师兄师姐们的一些整理,非常感谢大家的努力。
13 |
14 | [博客教程地址: https://godbasin.github.io/2017/02/17/box2d-tutorial-0-catalog](https://godbasin.github.io/2017/02/17/box2d-tutorial-0-catalog)
15 |
16 | ## 教程目录
17 | ### [1. 基本概念](https://github.com/godbasin/box2djs-tutorial/tree/master/1-basic)
18 | [1.1 有关物理引擎](https://github.com/godbasin/box2djs-tutorial/tree/master/1-basic/1-1-physical-engine.md)
19 | [1.2 有关图像引擎](https://github.com/godbasin/box2djs-tutorial/tree/master/1-basic/1-2-graphic-engine.md)
20 | [1.3 有关Box2D](https://github.com/godbasin/box2djs-tutorial/tree/master/1-basic/1-3-hello-box2d.md)
21 |
22 | ### [2. 物理世界(world)](https://github.com/godbasin/box2djs-tutorial/tree/master/2-world)
23 |
24 | ### [3. 形状(shape)-刚体(body)](https://github.com/godbasin/box2djs-tutorial/tree/master/3-body)
25 | [3.1 形状](https://github.com/godbasin/box2djs-tutorial/tree/master/3-body/3-1-shape.md)
26 | [3.2 矩形](https://github.com/godbasin/box2djs-tutorial/tree/master/3-body/3-2-box-shape.md)
27 | [3.3 圆形](https://github.com/godbasin/box2djs-tutorial/tree/master/3-body/3-3-cicle-shape.md)
28 | [3.4 凸多边形](https://github.com/godbasin/box2djs-tutorial/tree/master/3-body/3-4-poly-shape.md)
29 | [3.5 由形状到刚体](https://github.com/godbasin/box2djs-tutorial/tree/master/3-body/3-5-shape-to-body.md)
30 |
31 | ### [4. 关节(joint)](https://github.com/godbasin/box2djs-tutorial/tree/master/4-joint)
32 | [4.1 距离关节(distance-joint)](https://github.com/godbasin/box2djs-tutorial/tree/master/4-joint/4-1-distance-joint.md)
33 | [4.2 旋转关节(revolute-joint)](https://github.com/godbasin/box2djs-tutorial/tree/master/4-joint/4-2-revolute-joint.md)
34 | [4.3 移动关节(prismatic-joint)](https://github.com/godbasin/box2djs-tutorial/tree/master/4-joint/4-3-prismatic-joint.md)
35 | [4.4 滑轮关节(pulley-joint)](https://github.com/godbasin/box2djs-tutorial/tree/master/4-joint/4-4-pulley-joint.md)
36 | [4.5 齿轮关节(gear-joint)](https://github.com/godbasin/box2djs-tutorial/tree/master/4-joint/4-5-gear-joint.md)
37 |
38 | ### [5. 操作(operation)](https://github.com/godbasin/box2djs-tutorial/tree/master/5-operation)
39 | [5.1 鼠标获取刚体](https://github.com/godbasin/box2djs-tutorial/tree/master/5-operation/5-1-mouse-get-body.md)
40 | [5.2 获取参与碰撞的刚体](https://github.com/godbasin/box2djs-tutorial/tree/master/5-operation/5-2-get-contact-list.md)
41 | [5.3 获取刚体的各属性](https://github.com/godbasin/box2djs-tutorial/tree/master/5-operation/5-3-get-body-attributes.md)
42 | [5.4 为刚体设置属性](https://github.com/godbasin/box2djs-tutorial/tree/master/5-operation/5-4-set-body-attributes.md)
43 | [5.5 绘制功能](https://github.com/godbasin/box2djs-tutorial/tree/master/5-operation/5-5-body-with-image.md)
44 |
45 | ### [6. 创建一个物理世界吧](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice)
46 | [6.1 创建世界并初始化](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-1-create-and-init-world/6-1-create-and-init-world.md)
47 | [6.3 添加刚体](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-2-add-body/6-2-add-body.md)
48 | [6.4 添加边界](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-3-add-bound/6-3-add-bound.md)
49 | [6.4 鼠标操作刚体](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-4-mouse-operate-body/6-4-mouse-operate-body.md)
50 | [6.5 处理碰撞刚体](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-5-handle-contact/6-5-handle-contact.md)
51 |
52 | ### [7. api](https://github.com/godbasin/box2djs-tutorial/tree/master/7-api)
53 | [7.1 碰撞类(collision)](https://github.com/godbasin/box2djs-tutorial/tree/master/7-api/7-1-common-apis.md)
54 | [7.2 基础类(common)](https://github.com/godbasin/box2djs-tutorial/tree/master/7-api/7-2-collisions-apis.md)
55 | [7.3 动力学类(dynamics)](https://github.com/godbasin/box2djs-tutorial/tree/master/7-api/7-3-dynamics-apis.md)
56 |
57 | 以上内容可能有缺失或者错误,但还是希望能帮助到大家。
58 |
59 | ### 说明
60 | [box2djs官网](http://box2d-js.sourceforge.net/)
61 | [box2d教程](http://box2d.org/manual.pdf)
62 | [box2d中文教程](http://ss.sysu.edu.cn/~pml/se347/2012fall/info/box2d%E4%B8%AD%E6%96%87%E6%95%99%E7%A8%8B.pdf)
63 | [box2djs_api在线版](http://old7pzwup.bkt.clouddn.com/box2Dapi/index.html?Box2D/Collision/b2AABB.html&Box2D/Collision/class-list.html)
64 | [box2djs_api.zip下载](http://old7pzwup.bkt.clouddn.com/box2Dapi.zip)
65 | [box2d-js_0.1.0.zip下载](http://old7pzwup.bkt.clouddn.com/box2d-js_0.1.0.zip)
66 |
67 | ### 版权许可
68 | 只要保持原作者署名和非商用,您可以自由地阅读、分享、修改本教程。
--------------------------------------------------------------------------------
/6-practice/6-2-add-body/6-2-add-body/js/test.js:
--------------------------------------------------------------------------------
1 | var canvas;
2 | var ctx;
3 | var canvasWidth;
4 | var canvasHeight;
5 |
6 | var world;
7 |
8 | // 我们将创建世界封装至createWorld函数内
9 | function createWorld() {
10 | // 世界的大小
11 | var worldAABB = new b2AABB();
12 | worldAABB.minVertex.Set(-4000, -4000);
13 | worldAABB.maxVertex.Set(4000, 4000);
14 |
15 | //定义重力
16 | var gravity = new b2Vec2(0, 300);
17 |
18 | // 是否休眠
19 | var doSleep = false;
20 |
21 | // 最终创建世界
22 | var world = new b2World(worldAABB, gravity, doSleep);
23 |
24 | return world;
25 | }
26 |
27 | //绘画功能
28 | function drawWorld(world, context) {
29 | for (var j = world.m_jointList; j; j = j.m_next) {
30 | // 绘制关节
31 | // drawJoint(j, context);
32 | }
33 | for (var b = world.m_bodyList; b != null; b = b.m_next) {
34 | for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
35 | if (s.GetUserData() != undefined) {
36 | // 使用数据包括图片
37 | var img = s.GetUserData();
38 |
39 | // 图片的长和宽
40 | var x = s.GetPosition().x;
41 | var y = s.GetPosition().y;
42 | var topleftX = - img.clientWidth / 2;
43 | var topleftY = - img.clientHeight / 2;
44 |
45 | context.save();
46 | context.translate(x, y);
47 | context.rotate(s.GetBody().GetRotation());
48 | context.drawImage(img, topleftX, topleftY);
49 | context.restore();
50 | }
51 | drawShape(s, context);
52 | }
53 | }
54 | }
55 |
56 | // 创建圆形刚体
57 | function createBall(world, x, y, r) {
58 | // 创建圆形定义
59 | var ballSd = new b2CircleDef();
60 | ballSd.density = 1.0; // 设置密度
61 | ballSd.radius = 20; // 设置半径
62 | ballSd.restitution = 1.0; // 设置弹性
63 | ballSd.friction = 0; // 设置摩擦因子
64 | var ballBd = new b2BodyDef(); // 创建刚体定义
65 | ballBd.AddShape(ballSd); // 添加形状
66 | ballBd.position.Set(x || 0, y || 0); // 设置位置
67 | return world.CreateBody(ballBd); // 创建并返回刚体
68 | }
69 |
70 | // 创建矩形刚体
71 | function createBox(world, x, y, width, height, custom) {
72 | var boxSd = new b2BoxDef(); //创建一个形状Shape,然后设置有关Shape的属性
73 | boxSd.extents.Set(width || 1200, height || 5); //设置矩形高、宽
74 | boxSd.density = 1.0;
75 | if (custom === 'fixed') boxSd.density = 0.0; //设置矩形的密度
76 | else boxSd.userData = custom;
77 | boxSd.restitution = .3; //设置矩形的弹性
78 | boxSd.friction = 1; //设置矩形的摩擦因子,可以设置为0-1之间任意一个数,0表示光滑,1表示强摩擦
79 |
80 | var boxBd = new b2BodyDef(); // 创建刚体定义
81 | boxBd.AddShape(boxSd); // 添加形状
82 | boxBd.position.Set(x || 10, y || 10); // 设置位置
83 | return world.CreateBody(boxBd) // 创建并返回刚体
84 | }
85 |
86 | // 定义step函数,用于游戏的迭代运行
87 | function step() {
88 | // 模拟世界
89 | world.Step(1.0 / 60, 1);
90 | // 清除画布
91 | ctx.clearRect(0, 0, canvasWidth, canvasHeight);
92 | // 重新绘制
93 | drawWorld(world, ctx);
94 |
95 | // 再次刷新
96 | setTimeout(step, 10);
97 | console.log('step...');
98 | }
99 |
100 | // 从draw_world.js里面引用的绘画功能
101 | function drawShape(shape, context) {
102 | context.strokeStyle = '#003300';
103 | context.beginPath();
104 | switch (shape.m_type) {
105 | // 绘制圆
106 | case b2Shape.e_circleShape:
107 | var circle = shape;
108 | var pos = circle.m_position;
109 | var r = circle.m_radius;
110 | var segments = 16.0;
111 | var theta = 0.0;
112 | var dtheta = 2.0 * Math.PI / segments;
113 | // 画圆圈
114 | context.moveTo(pos.x + r, pos.y);
115 | for (var i = 0; i < segments; i++) {
116 | var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
117 | var v = b2Math.AddVV(pos, d);
118 | context.lineTo(v.x, v.y);
119 | theta += dtheta;
120 | }
121 | context.lineTo(pos.x + r, pos.y);
122 |
123 | // 画半径
124 | context.moveTo(pos.x, pos.y);
125 | var ax = circle.m_R.col1;
126 | var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
127 | context.lineTo(pos2.x, pos2.y);
128 | break;
129 | // 绘制多边形
130 | case b2Shape.e_polyShape:
131 | var poly = shape;
132 | var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
133 | context.moveTo(tV.x, tV.y);
134 | for (var i = 0; i < poly.m_vertexCount; i++) {
135 | var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
136 | context.lineTo(v.x, v.y);
137 | }
138 | context.lineTo(tV.x, tV.y);
139 | break;
140 | }
141 | context.stroke();
142 | }
143 |
144 | window.onload = function () {
145 | canvas = document.getElementById('canvas');
146 | ctx = canvas.getContext('2d');
147 | canvasWidth = parseInt(canvas.width);
148 | canvasHeight = parseInt(canvas.height);
149 | // 启动游戏
150 | world = createWorld();
151 | var ball1 = createBall(world, 100, 20, 20);
152 | var ball2 = createBall(world, 300, 60, 10);
153 | var box1 = createBox(world, 100, 200, 25, 30, 'fixed');
154 | var box2 = createBox(world, 200, 50, 20, 20);
155 | var box3 = createBox(world, 400, 80, 20, 20, document.getElementById('box'));
156 | step();
157 | };
158 |
--------------------------------------------------------------------------------
/6-practice/6-3-add-bound/6-3-add-bound/js/test.js:
--------------------------------------------------------------------------------
1 | var canvas;
2 | var ctx;
3 | var canvasWidth;
4 | var canvasHeight;
5 |
6 | var world;
7 |
8 | // 我们将创建世界封装至createWorld函数内
9 | function createWorld() {
10 | // 世界的大小
11 | var worldAABB = new b2AABB();
12 | worldAABB.minVertex.Set(-4000, -4000);
13 | worldAABB.maxVertex.Set(4000, 4000);
14 |
15 | //定义重力
16 | var gravity = new b2Vec2(0, 300);
17 |
18 | // 是否休眠
19 | var doSleep = false;
20 |
21 | // 最终创建世界
22 | var world = new b2World(worldAABB, gravity, doSleep);
23 |
24 | return world;
25 | }
26 |
27 | //绘画功能
28 | function drawWorld(world, context) {
29 | for (var j = world.m_jointList; j; j = j.m_next) {
30 | // 绘制关节
31 | // drawJoint(j, context);
32 | }
33 | for (var b = world.m_bodyList; b != null; b = b.m_next) {
34 | for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
35 | if (s.GetUserData() != undefined) {
36 | // 使用数据包括图片
37 | var img = s.GetUserData();
38 |
39 | // 图片的长和宽
40 | var x = s.GetPosition().x;
41 | var y = s.GetPosition().y;
42 | var topleftX = -img.clientWidth / 2;
43 | var topleftY = -img.clientHeight / 2;
44 |
45 | context.save();
46 | context.translate(x, y);
47 | context.rotate(s.GetBody().GetRotation());
48 | context.drawImage(img, topleftX, topleftY);
49 | context.restore();
50 | }
51 | drawShape(s, context);
52 | }
53 | }
54 | }
55 |
56 | // 创建圆形刚体
57 | function createBall(world, x, y, r, custom) {
58 | // 创建圆形定义
59 | var ballSd = new b2CircleDef();
60 | ballSd.density = 1.0; // 设置密度
61 | if (custom === 'fixed') ballSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
62 | else ballSd.userData = custom; // 若传入其他,则视为图片数据
63 | ballSd.radius = 20; // 设置半径
64 | ballSd.restitution = 1.0; // 设置弹性
65 | ballSd.friction = 0; // 设置摩擦因子
66 | var ballBd = new b2BodyDef(); // 创建刚体定义
67 | ballBd.AddShape(ballSd); // 添加形状
68 | ballBd.position.Set(x || 0, y || 0); // 设置位置
69 | return world.CreateBody(ballBd); // 创建并返回刚体
70 | }
71 |
72 | // 创建矩形刚体
73 | function createBox(world, x, y, width, height, custom) {
74 | var boxSd = new b2BoxDef(); // 创建一个形状Shape,然后设置有关Shape的属性
75 | boxSd.extents.Set(width || 1200, height || 5); // 设置矩形高、宽
76 | boxSd.density = 1.0; // 设置矩形的密度
77 | if (custom === 'fixed') boxSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
78 | else boxSd.userData = custom; // 若传入其他,则视为图片数据
79 | boxSd.restitution = .3; // 设置矩形的弹性
80 | boxSd.friction = 1; // 设置矩形的摩擦因子,可以设置为0-1之间任意一个数,0表示光滑,1表示强摩擦
81 |
82 | var boxBd = new b2BodyDef(); // 创建刚体定义
83 | boxBd.AddShape(boxSd); // 添加形状
84 | boxBd.position.Set(x || 10, y || 10); // 设置位置
85 | return world.CreateBody(boxBd) // 创建并返回刚体
86 | }
87 |
88 | // 定义step函数,用于游戏的迭代运行
89 | function step() {
90 | // 模拟世界
91 | world.Step(1.0 / 60, 1);
92 | // 清除画布
93 | ctx.clearRect(0, 0, canvasWidth, canvasHeight);
94 | // 重新绘制
95 | drawWorld(world, ctx);
96 |
97 | // 再次刷新
98 | setTimeout(step, 10);
99 | console.log('step...');
100 | }
101 |
102 | // 从draw_world.js里面引用的绘画功能
103 | function drawShape(shape, context) {
104 | context.strokeStyle = '#003300';
105 | context.beginPath();
106 | switch (shape.m_type) {
107 | // 绘制圆
108 | case b2Shape.e_circleShape:
109 | var circle = shape;
110 | var pos = circle.m_position;
111 | var r = circle.m_radius;
112 | var segments = 16.0;
113 | var theta = 0.0;
114 | var dtheta = 2.0 * Math.PI / segments;
115 | // 画圆圈
116 | context.moveTo(pos.x + r, pos.y);
117 | for (var i = 0; i < segments; i++) {
118 | var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
119 | var v = b2Math.AddVV(pos, d);
120 | context.lineTo(v.x, v.y);
121 | theta += dtheta;
122 | }
123 | context.lineTo(pos.x + r, pos.y);
124 |
125 | // 画半径
126 | context.moveTo(pos.x, pos.y);
127 | var ax = circle.m_R.col1;
128 | var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
129 | context.lineTo(pos2.x, pos2.y);
130 | break;
131 | // 绘制多边形
132 | case b2Shape.e_polyShape:
133 | var poly = shape;
134 | var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
135 | context.moveTo(tV.x, tV.y);
136 | for (var i = 0; i < poly.m_vertexCount; i++) {
137 | var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
138 | context.lineTo(v.x, v.y);
139 | }
140 | context.lineTo(tV.x, tV.y);
141 | break;
142 | }
143 | context.stroke();
144 | }
145 |
146 | window.onload = function() {
147 | canvas = document.getElementById('canvas');
148 | ctx = canvas.getContext('2d');
149 | canvasWidth = parseInt(canvas.width);
150 | canvasHeight = parseInt(canvas.height);
151 | // 启动游戏
152 | world = createWorld();
153 | var ball1 = createBall(world, 100, 20, 20);
154 | var ball2 = createBall(world, 300, 60, 10);
155 | var box1 = createBox(world, 100, 200, 25, 30, 'fixed');
156 | var box2 = createBox(world, 200, 50, 20, 20);
157 | var box3 = createBox(world, 400, 80, 20, 20, document.getElementById('box'));
158 | var wallLeft = createBox(world, 0, 0, 10, 600, 'fixed');
159 | var wallRight = createBox(world, 1290, 0, 10, 400, 'fixed');
160 | var ground = createBox(world, 30, 595, 1200, 5, 'fixed');
161 | step();
162 | };
--------------------------------------------------------------------------------
/6-practice/6-3-add-bound/6-3-add-bound.md:
--------------------------------------------------------------------------------
1 | ## 添加固定刚体
2 | ### 密度为0的刚体
3 | 要创建边界,如地板、墙壁等,即添加固定的刚体,这时候我们只需要把刚体的密度设置为0就可以实现。
4 |
5 | 为此,我们需要调整创建刚体的函数:
6 |
7 | ``` javascript
8 | // 创建圆形刚体
9 | function createBall(world, x, y, r, custom) {
10 | // 创建圆形定义
11 | var ballSd = new b2CircleDef();
12 | ballSd.density = 1.0; // 设置密度
13 | if (custom === 'fixed') ballSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
14 | else ballSd.userData = custom; // 若传入其他,则视为图片数据
15 | ballSd.radius = 20; // 设置半径
16 | ballSd.restitution = 1.0; // 设置弹性
17 | ballSd.friction = 0; // 设置摩擦因子
18 | var ballBd = new b2BodyDef(); // 创建刚体定义
19 | ballBd.AddShape(ballSd); // 添加形状
20 | ballBd.position.Set(x || 0, y || 0); // 设置位置
21 | return world.CreateBody(ballBd); // 创建并返回刚体
22 | }
23 |
24 | // 创建矩形刚体
25 | function createBox(world, x, y, width, height, custom) {
26 | var boxSd = new b2BoxDef(); // 创建一个形状Shape,然后设置有关Shape的属性
27 | boxSd.extents.Set(width || 1200, height || 5); // 设置矩形高、宽
28 | boxSd.density = 1.0; // 设置矩形的密度
29 | if (custom === 'fixed') boxSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
30 | else boxSd.userData = custom; // 若传入其他,则视为图片数据
31 | boxSd.restitution = .3; // 设置矩形的弹性
32 | boxSd.friction = 1; // 设置矩形的摩擦因子,可以设置为0-1之间任意一个数,0表示光滑,1表示强摩擦
33 |
34 | var boxBd = new b2BodyDef(); // 创建刚体定义
35 | boxBd.AddShape(boxSd); // 添加形状
36 | boxBd.position.Set(x || 10, y || 10); // 设置位置
37 | return world.CreateBody(boxBd) // 创建并返回刚体
38 | }
39 | ```
40 |
41 | ### 添加边界
42 | 利用上面创建刚体的函数,我们可以往世界中添加边界:
43 |
44 | ``` javascript
45 | var wallLeft = createBox(world, 0, 0, 10, 600, 'fixed');
46 | var wallRight = createBox(world, 1290, 0, 10, 400, 'fixed');
47 | var ground = createBox(world, 30, 595, 1200, 5, 'fixed');
48 | ```
49 |
50 | ### 添加固定物
51 | 同时我们也可以添加固定物体:
52 |
53 | ``` javascript
54 | var box1 = createBox(world, 100, 200, 25, 30, 'fixed');
55 | ```
56 |
57 | ### 完整代码
58 | ``` javascript
59 | var canvas;
60 | var ctx;
61 | var canvasWidth;
62 | var canvasHeight;
63 |
64 | var world;
65 |
66 | // 我们将创建世界封装至createWorld函数内
67 | function createWorld() {
68 | // 世界的大小
69 | var worldAABB = new b2AABB();
70 | worldAABB.minVertex.Set(-4000, -4000);
71 | worldAABB.maxVertex.Set(4000, 4000);
72 |
73 | //定义重力
74 | var gravity = new b2Vec2(0, 300);
75 |
76 | // 是否休眠
77 | var doSleep = false;
78 |
79 | // 最终创建世界
80 | var world = new b2World(worldAABB, gravity, doSleep);
81 |
82 | return world;
83 | }
84 |
85 | //绘画功能
86 | function drawWorld(world, context) {
87 | for (var j = world.m_jointList; j; j = j.m_next) {
88 | // 绘制关节
89 | // drawJoint(j, context);
90 | }
91 | for (var b = world.m_bodyList; b != null; b = b.m_next) {
92 | for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
93 | if (s.GetUserData() != undefined) {
94 | // 使用数据包括图片
95 | var img = s.GetUserData();
96 |
97 | // 图片的长和宽
98 | var x = s.GetPosition().x;
99 | var y = s.GetPosition().y;
100 | var topleftX = -img.clientWidth / 2;
101 | var topleftY = -img.clientHeight / 2;
102 |
103 | context.save();
104 | context.translate(x, y);
105 | context.rotate(s.GetBody().GetRotation());
106 | context.drawImage(img, topleftX, topleftY);
107 | context.restore();
108 | }
109 | drawShape(s, context);
110 | }
111 | }
112 | }
113 |
114 | // 创建圆形刚体
115 | function createBall(world, x, y, r, custom) {
116 | // 创建圆形定义
117 | var ballSd = new b2CircleDef();
118 | ballSd.density = 1.0; // 设置密度
119 | if (custom === 'fixed') ballSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
120 | else ballSd.userData = custom; // 若传入其他,则视为图片数据
121 | ballSd.radius = 20; // 设置半径
122 | ballSd.restitution = 1.0; // 设置弹性
123 | ballSd.friction = 0; // 设置摩擦因子
124 | var ballBd = new b2BodyDef(); // 创建刚体定义
125 | ballBd.AddShape(ballSd); // 添加形状
126 | ballBd.position.Set(x || 0, y || 0); // 设置位置
127 | return world.CreateBody(ballBd); // 创建并返回刚体
128 | }
129 |
130 | // 创建矩形刚体
131 | function createBox(world, x, y, width, height, custom) {
132 | var boxSd = new b2BoxDef(); // 创建一个形状Shape,然后设置有关Shape的属性
133 | boxSd.extents.Set(width || 1200, height || 5); // 设置矩形高、宽
134 | boxSd.density = 1.0; // 设置矩形的密度
135 | if (custom === 'fixed') boxSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
136 | else boxSd.userData = custom; // 若传入其他,则视为图片数据
137 | boxSd.restitution = .3; // 设置矩形的弹性
138 | boxSd.friction = 1; // 设置矩形的摩擦因子,可以设置为0-1之间任意一个数,0表示光滑,1表示强摩擦
139 |
140 | var boxBd = new b2BodyDef(); // 创建刚体定义
141 | boxBd.AddShape(boxSd); // 添加形状
142 | boxBd.position.Set(x || 10, y || 10); // 设置位置
143 | return world.CreateBody(boxBd) // 创建并返回刚体
144 | }
145 |
146 | // 定义step函数,用于游戏的迭代运行
147 | function step() {
148 | // 模拟世界
149 | world.Step(1.0 / 60, 1);
150 | // 清除画布
151 | ctx.clearRect(0, 0, canvasWidth, canvasHeight);
152 | // 重新绘制
153 | drawWorld(world, ctx);
154 |
155 | // 再次刷新
156 | setTimeout(step, 10);
157 | console.log('step...');
158 | }
159 |
160 | // 从draw_world.js里面引用的绘画功能
161 | function drawShape(shape, context) {
162 | context.strokeStyle = '#003300';
163 | context.beginPath();
164 | switch (shape.m_type) {
165 | // 绘制圆
166 | case b2Shape.e_circleShape:
167 | var circle = shape;
168 | var pos = circle.m_position;
169 | var r = circle.m_radius;
170 | var segments = 16.0;
171 | var theta = 0.0;
172 | var dtheta = 2.0 * Math.PI / segments;
173 | // 画圆圈
174 | context.moveTo(pos.x + r, pos.y);
175 | for (var i = 0; i < segments; i++) {
176 | var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
177 | var v = b2Math.AddVV(pos, d);
178 | context.lineTo(v.x, v.y);
179 | theta += dtheta;
180 | }
181 | context.lineTo(pos.x + r, pos.y);
182 |
183 | // 画半径
184 | context.moveTo(pos.x, pos.y);
185 | var ax = circle.m_R.col1;
186 | var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
187 | context.lineTo(pos2.x, pos2.y);
188 | break;
189 | // 绘制多边形
190 | case b2Shape.e_polyShape:
191 | var poly = shape;
192 | var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
193 | context.moveTo(tV.x, tV.y);
194 | for (var i = 0; i < poly.m_vertexCount; i++) {
195 | var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
196 | context.lineTo(v.x, v.y);
197 | }
198 | context.lineTo(tV.x, tV.y);
199 | break;
200 | }
201 | context.stroke();
202 | }
203 |
204 | window.onload = function() {
205 | canvas = document.getElementById('canvas');
206 | ctx = canvas.getContext('2d');
207 | canvasWidth = parseInt(canvas.width);
208 | canvasHeight = parseInt(canvas.height);
209 | // 启动游戏
210 | world = createWorld();
211 | var ball1 = createBall(world, 100, 20, 20);
212 | var ball2 = createBall(world, 300, 60, 10);
213 | var box1 = createBox(world, 100, 200, 25, 30, 'fixed');
214 | var box2 = createBox(world, 200, 50, 20, 20);
215 | var box3 = createBox(world, 400, 80, 20, 20, document.getElementById('box'));
216 | var wallLeft = createBox(world, 0, 0, 10, 600, 'fixed');
217 | var wallRight = createBox(world, 1290, 0, 10, 400, 'fixed');
218 | var ground = createBox(world, 30, 595, 1200, 5, 'fixed');
219 | step();
220 | };
221 | ```
222 |
223 | [此处查看项目代码(仅包含src部分)](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-3-add-bound/6-3-add-bound)
224 | [此处查看页面效果](http://old7pzwup.bkt.clouddn.com/6-3-add-bound/index.html)
225 |
226 |
227 | ### [返回总目录](https://github.com/godbasin/box2djs-tutorial)
--------------------------------------------------------------------------------
/6-practice/6-4-mouse-operate-body/6-4-mouse-operate-body/js/test.js:
--------------------------------------------------------------------------------
1 | var canvas;
2 | var ctx;
3 | var canvasWidth;
4 | var canvasHeight;
5 |
6 | var world;
7 |
8 | // 我们将创建世界封装至createWorld函数内
9 | function createWorld() {
10 | // 世界的大小
11 | var worldAABB = new b2AABB();
12 | worldAABB.minVertex.Set(-4000, -4000);
13 | worldAABB.maxVertex.Set(4000, 4000);
14 |
15 | //定义重力
16 | var gravity = new b2Vec2(0, 300);
17 |
18 | // 是否休眠
19 | var doSleep = false;
20 |
21 | // 最终创建世界
22 | var world = new b2World(worldAABB, gravity, doSleep);
23 |
24 | return world;
25 | }
26 |
27 | //绘画功能
28 | function drawWorld(world, context) {
29 | for (var j = world.m_jointList; j; j = j.m_next) {
30 | // 绘制关节
31 | // drawJoint(j, context);
32 | }
33 | for (var b = world.m_bodyList; b != null; b = b.m_next) {
34 | for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
35 | if (s.GetUserData() != undefined) {
36 | // 使用数据包括图片
37 | var img = s.GetUserData();
38 |
39 | // 图片的长和宽
40 | var x = s.GetPosition().x;
41 | var y = s.GetPosition().y;
42 | var topleftX = -img.clientWidth / 2;
43 | var topleftY = -img.clientHeight / 2;
44 |
45 | context.save();
46 | context.translate(x, y);
47 | context.rotate(s.GetBody().GetRotation());
48 | context.drawImage(img, topleftX, topleftY);
49 | context.restore();
50 | }
51 | drawShape(s, context);
52 | }
53 | }
54 | }
55 |
56 | // 创建圆形刚体
57 | function createBall(world, x, y, r, custom) {
58 | // 创建圆形定义
59 | var ballSd = new b2CircleDef();
60 | ballSd.density = 1.0; // 设置密度
61 | if (custom === 'fixed') ballSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
62 | else ballSd.userData = custom; // 若传入其他,则视为图片数据
63 | ballSd.radius = 20; // 设置半径
64 | ballSd.restitution = 1.0; // 设置弹性
65 | ballSd.friction = 0; // 设置摩擦因子
66 | var ballBd = new b2BodyDef(); // 创建刚体定义
67 | ballBd.AddShape(ballSd); // 添加形状
68 | ballBd.position.Set(x || 0, y || 0); // 设置位置
69 | return world.CreateBody(ballBd); // 创建并返回刚体
70 | }
71 |
72 | // 创建矩形刚体
73 | function createBox(world, x, y, width, height, custom) {
74 | var boxSd = new b2BoxDef(); // 创建一个形状Shape,然后设置有关Shape的属性
75 | boxSd.extents.Set(width || 1200, height || 5); // 设置矩形高、宽
76 | boxSd.density = 1.0; // 设置矩形的密度
77 | if (custom === 'fixed') boxSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
78 | else boxSd.userData = custom; // 若传入其他,则视为图片数据
79 | boxSd.restitution = .3; // 设置矩形的弹性
80 | boxSd.friction = 1; // 设置矩形的摩擦因子,可以设置为0-1之间任意一个数,0表示光滑,1表示强摩擦
81 |
82 | var boxBd = new b2BodyDef(); // 创建刚体定义
83 | boxBd.AddShape(boxSd); // 添加形状
84 | boxBd.position.Set(x || 10, y || 10); // 设置位置
85 | return world.CreateBody(boxBd) // 创建并返回刚体
86 | }
87 |
88 | // 定义step函数,用于游戏的迭代运行
89 | function step() {
90 | // 模拟世界
91 | world.Step(1.0 / 60, 1);
92 | // 清除画布
93 | ctx.clearRect(0, 0, canvasWidth, canvasHeight);
94 | // 重新绘制
95 | drawWorld(world, ctx);
96 |
97 | // 再次刷新
98 | setTimeout(step, 10);
99 | }
100 |
101 | // 从draw_world.js里面引用的绘画功能
102 | function drawShape(shape, context) {
103 | context.strokeStyle = '#003300';
104 | context.beginPath();
105 | switch (shape.m_type) {
106 | // 绘制圆
107 | case b2Shape.e_circleShape:
108 | var circle = shape;
109 | var pos = circle.m_position;
110 | var r = circle.m_radius;
111 | var segments = 16.0;
112 | var theta = 0.0;
113 | var dtheta = 2.0 * Math.PI / segments;
114 | // 画圆圈
115 | context.moveTo(pos.x + r, pos.y);
116 | for (var i = 0; i < segments; i++) {
117 | var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
118 | var v = b2Math.AddVV(pos, d);
119 | context.lineTo(v.x, v.y);
120 | theta += dtheta;
121 | }
122 | context.lineTo(pos.x + r, pos.y);
123 |
124 | // 画半径
125 | context.moveTo(pos.x, pos.y);
126 | var ax = circle.m_R.col1;
127 | var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
128 | context.lineTo(pos2.x, pos2.y);
129 | break;
130 | // 绘制多边形
131 | case b2Shape.e_polyShape:
132 | var poly = shape;
133 | var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
134 | context.moveTo(tV.x, tV.y);
135 | for (var i = 0; i < poly.m_vertexCount; i++) {
136 | var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
137 | context.lineTo(v.x, v.y);
138 | }
139 | context.lineTo(tV.x, tV.y);
140 | break;
141 | }
142 | context.stroke();
143 | }
144 |
145 | function GetBodyAtPosition(x, y) {
146 | // 首先创建一个近似于点的小区域
147 | var mousePVec = new b2Vec2(x, y);
148 | // 利用b2Vec2定义一个矢量,用来保存鼠标点击的点
149 | var aabb = new b2AABB();
150 | // 利用b2AABB创建一个环境
151 | aabb.minVertex.Set(mousePVec.x - 0.001, mousePVec.y - 0.001);
152 | aabb.maxVertex.Set(mousePVec.x + 0.001, mousePVec.y + 0.001);
153 | // 设置aabb的左上角及右下角坐标,这里是以鼠标点击位置为中心创建了一个长、宽均为0.002的矩形区域
154 |
155 | // 然后查询与指定区域有重叠的刚体
156 | var k_maxCount = 10; // 设定所要查找形状的数量,注意合理设置其大小,过大会影响运行速度
157 | var shapes = new Array(); // 保存查找到的与已知边界盒相交的形状
158 | var count = world.Query(aabb, shapes, k_maxCount); // 在世界中查找与边界盒相交的maxCount个形状,并返回边界盒区域内实际包含的形状的个数
159 |
160 | var findBody = null; // 首先设定没有找到物体
161 | for (var i = 0; i < count; ++i) {
162 | if (shapes[i].GetBody().IsStatic() == false)
163 | // 条件假定查找到的形状不是静态刚体,比如墙
164 | {
165 | var tShape = shapes[i]; // 将查找到的形状赋给tShape变量
166 | var inside = tShape.GetBody(); // 将tShape对应的刚体赋给inside
167 | if (inside) // 如果inside这个刚体存在
168 | {
169 | // 那么返回这个刚体,并跳出遍历
170 | findBody = tShape.GetBody();
171 | break;
172 | }
173 | }
174 | }
175 | return findBody;
176 | }
177 |
178 | // 获取鼠标坐标
179 | function getMousePos(event) {
180 | var e = event || window.event;
181 | var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
182 | var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
183 | var x = e.pageX || e.clientX + scrollX;
184 | var y = e.pageY || e.clientY + scrollY;
185 | return { 'x': x, 'y': y };
186 | }
187 |
188 | // 处理鼠标移动
189 | function handleMousedown(e) {
190 | var e = e || window.event;
191 | // 获取鼠标x坐标
192 | var newMouse = getMousePos(e);
193 | var selectBody = GetBodyAtPosition(newMouse.x - canvas.offsetLeft, newMouse.y - canvas.offsetTop); // 选择刚体
194 | if (selectBody) {
195 | // 若有选中刚体,则处理
196 | var LinearVelocity = new b2Vec2(500, -200); // 定义一个向量
197 | selectBody.WakeUp(); // 激活休眠状态
198 | selectBody.SetLinearVelocity(LinearVelocity); //给定一个速度向量
199 | } else {
200 | // 若无,则随机生成一个矩形添加
201 | var width = parseInt(Math.random() * 50);
202 | var height = parseInt(Math.random() * 50);
203 | createBox(world, newMouse.x, newMouse.y, width, height);
204 | }
205 | }
206 |
207 | document.addEventListener('mousedown', handleMousedown, false);
208 |
209 | window.onload = function () {
210 | canvas = document.getElementById('canvas');
211 | ctx = canvas.getContext('2d');
212 | canvasWidth = parseInt(canvas.width);
213 | canvasHeight = parseInt(canvas.height);
214 | // 启动游戏
215 | world = createWorld();
216 | var ball1 = createBall(world, 100, 20, 20);
217 | var ball2 = createBall(world, 300, 60, 10);
218 | var box1 = createBox(world, 100, 200, 25, 30, 'fixed');
219 | var box2 = createBox(world, 200, 50, 20, 20);
220 | var box3 = createBox(world, 400, 80, 20, 20, document.getElementById('box'));
221 | var wallLeft = createBox(world, 0, 0, 10, 600, 'fixed');
222 | var wallRight = createBox(world, 1290, 0, 10, 400, 'fixed');
223 | var ground = createBox(world, 30, 595, 1200, 5, 'fixed');
224 | step();
225 | };
--------------------------------------------------------------------------------
/6-practice/6-5-handle-contact/6-5-handle-contact/js/test.js:
--------------------------------------------------------------------------------
1 | var canvas;
2 | var ctx;
3 | var canvasWidth;
4 | var canvasHeight;
5 |
6 | var world;
7 | var box, wallLeft, wallRight, ground;
8 |
9 | // 我们将创建世界封装至createWorld函数内
10 | function createWorld() {
11 | // 世界的大小
12 | var worldAABB = new b2AABB();
13 | worldAABB.minVertex.Set(-4000, -4000);
14 | worldAABB.maxVertex.Set(4000, 4000);
15 |
16 | //定义重力
17 | var gravity = new b2Vec2(0, 300);
18 |
19 | // 是否休眠
20 | var doSleep = false;
21 |
22 | // 最终创建世界
23 | var world = new b2World(worldAABB, gravity, doSleep);
24 |
25 | return world;
26 | }
27 |
28 | //绘画功能
29 | function drawWorld(world, context) {
30 | for (var j = world.m_jointList; j; j = j.m_next) {
31 | // 绘制关节
32 | // drawJoint(j, context);
33 | }
34 | for (var b = world.m_bodyList; b != null; b = b.m_next) {
35 | for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
36 | if (s.GetUserData() != undefined) {
37 | // 使用数据包括图片
38 | var img = s.GetUserData();
39 |
40 | // 图片的长和宽
41 | var x = s.GetPosition().x;
42 | var y = s.GetPosition().y;
43 | var topleftX = -img.clientWidth / 2;
44 | var topleftY = -img.clientHeight / 2;
45 |
46 | context.save();
47 | context.translate(x, y);
48 | context.rotate(s.GetBody().GetRotation());
49 | context.drawImage(img, topleftX, topleftY);
50 | context.restore();
51 | }
52 | drawShape(s, context);
53 | }
54 | }
55 | }
56 |
57 | // 创建圆形刚体
58 | function createBall(world, x, y, r, custom) {
59 | // 创建圆形定义
60 | var ballSd = new b2CircleDef();
61 | ballSd.density = 1.0; // 设置密度
62 | if (custom === 'fixed') ballSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
63 | else ballSd.userData = custom; // 若传入其他,则视为图片数据
64 | ballSd.radius = 20; // 设置半径
65 | ballSd.restitution = 1.0; // 设置弹性
66 | ballSd.friction = 0; // 设置摩擦因子
67 | var ballBd = new b2BodyDef(); // 创建刚体定义
68 | ballBd.AddShape(ballSd); // 添加形状
69 | ballBd.position.Set(x || 0, y || 0); // 设置位置
70 | return world.CreateBody(ballBd); // 创建并返回刚体
71 | }
72 |
73 | // 创建矩形刚体
74 | function createBox(world, x, y, width, height, custom) {
75 | var boxSd = new b2BoxDef(); // 创建一个形状Shape,然后设置有关Shape的属性
76 | boxSd.extents.Set(width || 1200, height || 5); // 设置矩形高、宽
77 | boxSd.density = 1.0; // 设置矩形的密度
78 | if (custom === 'fixed') boxSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
79 | else boxSd.userData = custom; // 若传入其他,则视为图片数据
80 | boxSd.restitution = .3; // 设置矩形的弹性
81 | boxSd.friction = 1; // 设置矩形的摩擦因子,可以设置为0-1之间任意一个数,0表示光滑,1表示强摩擦
82 |
83 | var boxBd = new b2BodyDef(); // 创建刚体定义
84 | boxBd.AddShape(boxSd); // 添加形状
85 | boxBd.position.Set(x || 10, y || 10); // 设置位置
86 | return world.CreateBody(boxBd) // 创建并返回刚体
87 | }
88 |
89 | // 定义step函数,用于游戏的迭代运行
90 | function step() {
91 | // 模拟世界
92 | world.Step(1.0 / 60, 1);
93 | checkContact();
94 | // 清除画布
95 | ctx.clearRect(0, 0, canvasWidth, canvasHeight);
96 | // 重新绘制
97 | drawWorld(world, ctx);
98 |
99 | // 再次刷新
100 | setTimeout(step, 10);
101 | }
102 |
103 | // 从draw_world.js里面引用的绘画功能
104 | function drawShape(shape, context) {
105 | context.strokeStyle = '#003300';
106 | context.beginPath();
107 | switch (shape.m_type) {
108 | // 绘制圆
109 | case b2Shape.e_circleShape:
110 | var circle = shape;
111 | var pos = circle.m_position;
112 | var r = circle.m_radius;
113 | var segments = 16.0;
114 | var theta = 0.0;
115 | var dtheta = 2.0 * Math.PI / segments;
116 | // 画圆圈
117 | context.moveTo(pos.x + r, pos.y);
118 | for (var i = 0; i < segments; i++) {
119 | var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
120 | var v = b2Math.AddVV(pos, d);
121 | context.lineTo(v.x, v.y);
122 | theta += dtheta;
123 | }
124 | context.lineTo(pos.x + r, pos.y);
125 |
126 | // 画半径
127 | context.moveTo(pos.x, pos.y);
128 | var ax = circle.m_R.col1;
129 | var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
130 | context.lineTo(pos2.x, pos2.y);
131 | break;
132 | // 绘制多边形
133 | case b2Shape.e_polyShape:
134 | var poly = shape;
135 | var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
136 | context.moveTo(tV.x, tV.y);
137 | for (var i = 0; i < poly.m_vertexCount; i++) {
138 | var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
139 | context.lineTo(v.x, v.y);
140 | }
141 | context.lineTo(tV.x, tV.y);
142 | break;
143 | }
144 | context.stroke();
145 | }
146 |
147 | // 检查是否有碰撞
148 | function checkContact(){
149 | for (var cn = world.GetContactList(); cn != null; cn = cn.GetNext()) {
150 | var body1 = cn.GetShape1().GetBody();
151 | var body2 = cn.GetShape2().GetBody();
152 |
153 | // 若与箱子碰撞,则销毁该刚体
154 |
155 | if(body1 === box && body2.IsStatic() == false ){
156 | world.DestroyBody(body2);
157 | }
158 |
159 | if(body2 === box && body1.IsStatic() == false ){
160 | world.DestroyBody(body1);
161 | }
162 | }
163 | }
164 |
165 | function GetBodyAtPosition(x, y) {
166 | // 首先创建一个近似于点的小区域
167 | var mousePVec = new b2Vec2(x, y);
168 | // 利用b2Vec2定义一个矢量,用来保存鼠标点击的点
169 | var aabb = new b2AABB();
170 | // 利用b2AABB创建一个环境
171 | aabb.minVertex.Set(mousePVec.x - 0.001, mousePVec.y - 0.001);
172 | aabb.maxVertex.Set(mousePVec.x + 0.001, mousePVec.y + 0.001);
173 | // 设置aabb的左上角及右下角坐标,这里是以鼠标点击位置为中心创建了一个长、宽均为0.002的矩形区域
174 |
175 | // 然后查询与指定区域有重叠的刚体
176 | var k_maxCount = 10; // 设定所要查找形状的数量,注意合理设置其大小,过大会影响运行速度
177 | var shapes = new Array(); // 保存查找到的与已知边界盒相交的形状
178 | var count = world.Query(aabb, shapes, k_maxCount); // 在世界中查找与边界盒相交的maxCount个形状,并返回边界盒区域内实际包含的形状的个数
179 |
180 | var findBody = null; // 首先设定没有找到物体
181 | for (var i = 0; i < count; ++i) {
182 | if (shapes[i].GetBody().IsStatic() == false)
183 | // 条件假定查找到的形状不是静态刚体,比如墙
184 | {
185 | var tShape = shapes[i]; // 将查找到的形状赋给tShape变量
186 | var inside = tShape.GetBody(); // 将tShape对应的刚体赋给inside
187 | if (inside) // 如果inside这个刚体存在
188 | {
189 | // 那么返回这个刚体,并跳出遍历
190 | findBody = tShape.GetBody();
191 | break;
192 | }
193 | }
194 | }
195 | return findBody;
196 | }
197 |
198 | // 获取鼠标坐标
199 | function getMousePos(event) {
200 | var e = event || window.event;
201 | var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
202 | var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
203 | var x = e.pageX || e.clientX + scrollX;
204 | var y = e.pageY || e.clientY + scrollY;
205 | return { 'x': x, 'y': y };
206 | }
207 |
208 | // 处理鼠标移动
209 | function handleMousedown(e) {
210 | var e = e || window.event;
211 | // 获取鼠标x坐标
212 | var newMouse = getMousePos(e);
213 | var selectBody = GetBodyAtPosition(newMouse.x - canvas.offsetLeft, newMouse.y - canvas.offsetTop); // 选择刚体
214 | if (selectBody) {
215 | // 若有选中刚体,则处理
216 | var LinearVelocity = new b2Vec2(500, -200); // 定义一个向量
217 | selectBody.WakeUp(); // 激活休眠状态
218 | selectBody.SetLinearVelocity(LinearVelocity); //给定一个速度向量
219 | } else {
220 | // 若无,则随机生成一个矩形添加
221 | var width = parseInt(Math.random() * 50);
222 | var height = parseInt(Math.random() * 50);
223 | createBox(world, newMouse.x, newMouse.y, width, height);
224 | }
225 | }
226 |
227 | document.addEventListener('mousedown', handleMousedown, false);
228 |
229 | window.onload = function () {
230 | canvas = document.getElementById('canvas');
231 | ctx = canvas.getContext('2d');
232 | canvasWidth = parseInt(canvas.width);
233 | canvasHeight = parseInt(canvas.height);
234 | // 启动游戏
235 | world = createWorld();
236 | createBall(world, 100, 20, 20);
237 | createBall(world, 300, 60, 10);
238 | createBox(world, 100, 200, 25, 30, 'fixed');
239 | createBox(world, 200, 50, 20, 20);
240 | box = createBox(world, 400, 80, 20, 20, document.getElementById('box'));
241 | wallLeft = createBox(world, 0, 0, 10, 600, 'fixed');
242 | wallRight = createBox(world, 1290, 0, 10, 400, 'fixed');
243 | ground = createBox(world, 30, 595, 1200, 5, 'fixed');
244 | step();
245 | };
--------------------------------------------------------------------------------
/6-practice/6-5-handle-contact/6-5-handle-contact.md:
--------------------------------------------------------------------------------
1 | ## 处理碰撞刚体
2 | ### 检查刚体的碰撞
3 | 我们可以通过`world.GetContactList()`获取碰撞刚体列表:
4 |
5 | ``` javascript
6 | // 检查是否有碰撞
7 | function checkContact(){
8 | for (var cn = world.GetContactList(); cn != null; cn = cn.GetNext()) {
9 | var body1 = cn.GetShape1().GetBody();
10 | var body2 = cn.GetShape2().GetBody();
11 |
12 | // 处理碰撞刚体
13 | }
14 | }
15 | ```
16 |
17 | ### 处理碰撞刚体
18 | 这里我们设定与箱子(带图片的刚体)发生碰撞则销毁刚体。
19 |
20 | ``` javascript
21 | // 若与箱子碰撞,切不是静态刚体,则销毁该刚体
22 | if(body1 === box && body2.IsStatic() == false ){
23 | world.DestroyBody(body2);
24 | }
25 | if(body2 === box && body1.IsStatic() == false ){
26 | world.DestroyBody(body1);
27 | }
28 | ```
29 |
30 | ### 在step中添加检测
31 | 我们需要在每次模拟世界中进行检测:
32 |
33 | ``` javascript
34 | // 定义step函数,用于游戏的迭代运行
35 | function step() {
36 | ...
37 | // 添加检测
38 | checkContact();
39 | ...
40 | }
41 | ```
42 |
43 | ### 完整代码
44 | ``` javascript
45 | var canvas;
46 | var ctx;
47 | var canvasWidth;
48 | var canvasHeight;
49 |
50 | var world;
51 | var box, wallLeft, wallRight, ground;
52 |
53 | // 我们将创建世界封装至createWorld函数内
54 | function createWorld() {
55 | // 世界的大小
56 | var worldAABB = new b2AABB();
57 | worldAABB.minVertex.Set(-4000, -4000);
58 | worldAABB.maxVertex.Set(4000, 4000);
59 |
60 | //定义重力
61 | var gravity = new b2Vec2(0, 300);
62 |
63 | // 是否休眠
64 | var doSleep = false;
65 |
66 | // 最终创建世界
67 | var world = new b2World(worldAABB, gravity, doSleep);
68 |
69 | return world;
70 | }
71 |
72 | //绘画功能
73 | function drawWorld(world, context) {
74 | for (var j = world.m_jointList; j; j = j.m_next) {
75 | // 绘制关节
76 | // drawJoint(j, context);
77 | }
78 | for (var b = world.m_bodyList; b != null; b = b.m_next) {
79 | for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
80 | if (s.GetUserData() != undefined) {
81 | // 使用数据包括图片
82 | var img = s.GetUserData();
83 |
84 | // 图片的长和宽
85 | var x = s.GetPosition().x;
86 | var y = s.GetPosition().y;
87 | var topleftX = -img.clientWidth / 2;
88 | var topleftY = -img.clientHeight / 2;
89 |
90 | context.save();
91 | context.translate(x, y);
92 | context.rotate(s.GetBody().GetRotation());
93 | context.drawImage(img, topleftX, topleftY);
94 | context.restore();
95 | }
96 | drawShape(s, context);
97 | }
98 | }
99 | }
100 |
101 | // 创建圆形刚体
102 | function createBall(world, x, y, r, custom) {
103 | // 创建圆形定义
104 | var ballSd = new b2CircleDef();
105 | ballSd.density = 1.0; // 设置密度
106 | if (custom === 'fixed') ballSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
107 | else ballSd.userData = custom; // 若传入其他,则视为图片数据
108 | ballSd.radius = 20; // 设置半径
109 | ballSd.restitution = 1.0; // 设置弹性
110 | ballSd.friction = 0; // 设置摩擦因子
111 | var ballBd = new b2BodyDef(); // 创建刚体定义
112 | ballBd.AddShape(ballSd); // 添加形状
113 | ballBd.position.Set(x || 0, y || 0); // 设置位置
114 | return world.CreateBody(ballBd); // 创建并返回刚体
115 | }
116 |
117 | // 创建矩形刚体
118 | function createBox(world, x, y, width, height, custom) {
119 | var boxSd = new b2BoxDef(); // 创建一个形状Shape,然后设置有关Shape的属性
120 | boxSd.extents.Set(width || 1200, height || 5); // 设置矩形高、宽
121 | boxSd.density = 1.0; // 设置矩形的密度
122 | if (custom === 'fixed') boxSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
123 | else boxSd.userData = custom; // 若传入其他,则视为图片数据
124 | boxSd.restitution = .3; // 设置矩形的弹性
125 | boxSd.friction = 1; // 设置矩形的摩擦因子,可以设置为0-1之间任意一个数,0表示光滑,1表示强摩擦
126 |
127 | var boxBd = new b2BodyDef(); // 创建刚体定义
128 | boxBd.AddShape(boxSd); // 添加形状
129 | boxBd.position.Set(x || 10, y || 10); // 设置位置
130 | return world.CreateBody(boxBd) // 创建并返回刚体
131 | }
132 |
133 | // 定义step函数,用于游戏的迭代运行
134 | function step() {
135 | // 模拟世界
136 | world.Step(1.0 / 60, 1);
137 | checkContact();
138 | // 清除画布
139 | ctx.clearRect(0, 0, canvasWidth, canvasHeight);
140 | // 重新绘制
141 | drawWorld(world, ctx);
142 |
143 | // 再次刷新
144 | setTimeout(step, 10);
145 | }
146 |
147 | // 从draw_world.js里面引用的绘画功能
148 | function drawShape(shape, context) {
149 | context.strokeStyle = '#003300';
150 | context.beginPath();
151 | switch (shape.m_type) {
152 | // 绘制圆
153 | case b2Shape.e_circleShape:
154 | var circle = shape;
155 | var pos = circle.m_position;
156 | var r = circle.m_radius;
157 | var segments = 16.0;
158 | var theta = 0.0;
159 | var dtheta = 2.0 * Math.PI / segments;
160 | // 画圆圈
161 | context.moveTo(pos.x + r, pos.y);
162 | for (var i = 0; i < segments; i++) {
163 | var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
164 | var v = b2Math.AddVV(pos, d);
165 | context.lineTo(v.x, v.y);
166 | theta += dtheta;
167 | }
168 | context.lineTo(pos.x + r, pos.y);
169 |
170 | // 画半径
171 | context.moveTo(pos.x, pos.y);
172 | var ax = circle.m_R.col1;
173 | var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
174 | context.lineTo(pos2.x, pos2.y);
175 | break;
176 | // 绘制多边形
177 | case b2Shape.e_polyShape:
178 | var poly = shape;
179 | var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
180 | context.moveTo(tV.x, tV.y);
181 | for (var i = 0; i < poly.m_vertexCount; i++) {
182 | var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
183 | context.lineTo(v.x, v.y);
184 | }
185 | context.lineTo(tV.x, tV.y);
186 | break;
187 | }
188 | context.stroke();
189 | }
190 |
191 | // 检查是否有碰撞
192 | function checkContact(){
193 | for (var cn = world.GetContactList(); cn != null; cn = cn.GetNext()) {
194 | var body1 = cn.GetShape1().GetBody();
195 | var body2 = cn.GetShape2().GetBody();
196 |
197 | // 若与箱子碰撞,则销毁该刚体
198 |
199 | if(body1 === box && body2.IsStatic() == false ){
200 | world.DestroyBody(body2);
201 | }
202 |
203 | if(body2 === box && body1.IsStatic() == false ){
204 | world.DestroyBody(body1);
205 | }
206 | }
207 | }
208 |
209 | function GetBodyAtPosition(x, y) {
210 | // 首先创建一个近似于点的小区域
211 | var mousePVec = new b2Vec2(x, y);
212 | // 利用b2Vec2定义一个矢量,用来保存鼠标点击的点
213 | var aabb = new b2AABB();
214 | // 利用b2AABB创建一个环境
215 | aabb.minVertex.Set(mousePVec.x - 0.001, mousePVec.y - 0.001);
216 | aabb.maxVertex.Set(mousePVec.x + 0.001, mousePVec.y + 0.001);
217 | // 设置aabb的左上角及右下角坐标,这里是以鼠标点击位置为中心创建了一个长、宽均为0.002的矩形区域
218 |
219 | // 然后查询与指定区域有重叠的刚体
220 | var k_maxCount = 10; // 设定所要查找形状的数量,注意合理设置其大小,过大会影响运行速度
221 | var shapes = new Array(); // 保存查找到的与已知边界盒相交的形状
222 | var count = world.Query(aabb, shapes, k_maxCount); // 在世界中查找与边界盒相交的maxCount个形状,并返回边界盒区域内实际包含的形状的个数
223 |
224 | var findBody = null; // 首先设定没有找到物体
225 | for (var i = 0; i < count; ++i) {
226 | if (shapes[i].GetBody().IsStatic() == false)
227 | // 条件假定查找到的形状不是静态刚体,比如墙
228 | {
229 | var tShape = shapes[i]; // 将查找到的形状赋给tShape变量
230 | var inside = tShape.GetBody(); // 将tShape对应的刚体赋给inside
231 | if (inside) // 如果inside这个刚体存在
232 | {
233 | // 那么返回这个刚体,并跳出遍历
234 | findBody = tShape.GetBody();
235 | break;
236 | }
237 | }
238 | }
239 | return findBody;
240 | }
241 |
242 | // 获取鼠标坐标
243 | function getMousePos(event) {
244 | var e = event || window.event;
245 | var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
246 | var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
247 | var x = e.pageX || e.clientX + scrollX;
248 | var y = e.pageY || e.clientY + scrollY;
249 | return { 'x': x, 'y': y };
250 | }
251 |
252 | // 处理鼠标移动
253 | function handleMousedown(e) {
254 | var e = e || window.event;
255 | // 获取鼠标x坐标
256 | var newMouse = getMousePos(e);
257 | var selectBody = GetBodyAtPosition(newMouse.x - canvas.offsetLeft, newMouse.y - canvas.offsetTop); // 选择刚体
258 | if (selectBody) {
259 | // 若有选中刚体,则处理
260 | var LinearVelocity = new b2Vec2(500, -200); // 定义一个向量
261 | selectBody.WakeUp(); // 激活休眠状态
262 | selectBody.SetLinearVelocity(LinearVelocity); //给定一个速度向量
263 | } else {
264 | // 若无,则随机生成一个矩形添加
265 | var width = parseInt(Math.random() * 50);
266 | var height = parseInt(Math.random() * 50);
267 | createBox(world, newMouse.x, newMouse.y, width, height);
268 | }
269 | }
270 |
271 | document.addEventListener('mousedown', handleMousedown, false);
272 |
273 | window.onload = function () {
274 | canvas = document.getElementById('canvas');
275 | ctx = canvas.getContext('2d');
276 | canvasWidth = parseInt(canvas.width);
277 | canvasHeight = parseInt(canvas.height);
278 | // 启动游戏
279 | world = createWorld();
280 | createBall(world, 100, 20, 20);
281 | createBall(world, 300, 60, 10);
282 | createBox(world, 100, 200, 25, 30, 'fixed');
283 | createBox(world, 200, 50, 20, 20);
284 | box = createBox(world, 400, 80, 20, 20, document.getElementById('box'));
285 | wallLeft = createBox(world, 0, 0, 10, 600, 'fixed');
286 | wallRight = createBox(world, 1290, 0, 10, 400, 'fixed');
287 | ground = createBox(world, 30, 595, 1200, 5, 'fixed');
288 | step();
289 | };
290 | ```
291 |
292 | [此处查看项目代码(仅包含src部分)](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-5-handle-contact/6-5-handle-contact)
293 | [此处查看页面效果](http://old7pzwup.bkt.clouddn.com/6-5-handle-contact/index.html)
294 |
295 |
296 | ### [返回总目录](https://github.com/godbasin/box2djs-tutorial)
--------------------------------------------------------------------------------
/6-practice/6-2-add-body/6-2-add-body.md:
--------------------------------------------------------------------------------
1 | ## 添加刚体
2 | ### 创建刚体
3 | 前面我们说过,box2d中创建刚体、关节的过程类似于堆积木,由形状构成刚体,由刚体构成关节,稍微不同的地方是,这里的形状可以多次使用,其过程:
4 | `形状 -> 刚体 -> 关节`
5 |
6 | 这里我们首先创建各种形状,然后创建对应的刚体。
7 |
8 | - 创建圆形
9 |
10 | ``` javascript
11 | function createBall(world, x, y, r) {
12 | // 创建圆形定义
13 | var ballSd = new b2CircleDef();
14 | ballSd.density = 1.0; // 设置密度
15 | ballSd.radius = 20; // 设置半径
16 | ballSd.restitution = 1.0; // 设置弹性
17 | ballSd.friction = 0; // 设置摩擦因子
18 | var ballBd = new b2BodyDef(); // 创建刚体定义
19 | ballBd.AddShape(ballSd); // 添加形状
20 | ballBd.position.Set(x || 0,y || 0); // 设置位置
21 | return world.CreateBody(ballBd); // 创建并返回刚体
22 | }
23 | ```
24 |
25 | - 创建矩形
26 |
27 | ``` javascript
28 | // 创建矩形刚体
29 | function createBox(world, x, y, width, height, userData) {
30 | var boxSd = new b2BoxDef(); // 创建一个形状Shape,然后设置有关Shape的属性
31 | boxSd.extents.Set(width || 1200, height || 5); // 设置矩形高、宽
32 | boxSd.density = 1.0; // 设置矩形的密度
33 | boxSd.userData = userData; // 传入图片数据
34 | boxSd.restitution = .3; //设置矩形的弹性
35 | boxSd.friction = 1; //设置矩形的摩擦因子,可以设置为0-1之间任意一个数,0表示光滑,1表示强摩擦
36 |
37 | var boxBd = new b2BodyDef(); // 创建刚体定义
38 | boxBd.AddShape(boxSd); // 添加形状
39 | boxBd.position.Set(x || 10, y || 10); // 设置位置
40 | return world.CreateBody(boxBd) // 创建并返回刚体
41 | }
42 | ```
43 |
44 | ### 添加刚体
45 | 利用上面创建刚体的函数,我们可以往世界中添加刚体:
46 |
47 | ``` javascript
48 | var ball1 = createBall(world, 100, 20, 20);
49 | var ball2 = createBall(world, 300, 60, 10);
50 | var box1 = createBox(world, 100, 200, 25, 30, true);
51 | var box2 = createBox(world, 200, 50, 20, 20);
52 | ```
53 |
54 | ### 绘制刚体
55 | 我们可以通过添加绘制刚体的函数,在绘制世界时调用:
56 |
57 | ``` javascript
58 | // 从draw_world.js里面引用的绘画功能
59 | function drawShape(shape, context) {
60 | context.strokeStyle = '#003300';
61 | context.beginPath();
62 | switch (shape.m_type) {
63 | // 绘制圆
64 | case b2Shape.e_circleShape:
65 | var circle = shape;
66 | var pos = circle.m_position;
67 | var r = circle.m_radius;
68 | var segments = 16.0;
69 | var theta = 0.0;
70 | var dtheta = 2.0 * Math.PI / segments;
71 | // 画圆圈
72 | context.moveTo(pos.x + r, pos.y);
73 | for (var i = 0; i < segments; i++) {
74 | var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
75 | var v = b2Math.AddVV(pos, d);
76 | context.lineTo(v.x, v.y);
77 | theta += dtheta;
78 | }
79 | context.lineTo(pos.x + r, pos.y);
80 |
81 | // 画半径
82 | context.moveTo(pos.x, pos.y);
83 | var ax = circle.m_R.col1;
84 | var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
85 | context.lineTo(pos2.x, pos2.y);
86 | break;
87 | // 绘制多边形
88 | case b2Shape.e_polyShape:
89 | var poly = shape;
90 | var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
91 | context.moveTo(tV.x, tV.y);
92 | for (var i = 0; i < poly.m_vertexCount; i++) {
93 | var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
94 | context.lineTo(v.x, v.y);
95 | }
96 | context.lineTo(tV.x, tV.y);
97 | break;
98 | }
99 | context.stroke();
100 | }
101 | ```
102 |
103 | 我们也可以在其中一个刚体里将图片与刚体结合:
104 |
105 | ``` javascript
106 | //绘画功能
107 | function drawWorld(world, context) {
108 | for (var j = world.m_jointList; j; j = j.m_next) {
109 | // 绘制关节
110 | // drawJoint(j, context);
111 | }
112 | for (var b = world.m_bodyList; b != null; b = b.m_next) {
113 | for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
114 | if (s.GetUserData() != undefined) {
115 | // 使用数据包括图片
116 | var img = s.GetUserData();
117 |
118 | // 图片的长和宽
119 | var x = s.GetPosition().x;
120 | var y = s.GetPosition().y;
121 | var topleftX = - img.clientWidth / 2;
122 | var topleftY = - img.clientHeight / 2;
123 |
124 | context.save();
125 | context.translate(x, y);
126 | context.rotate(s.GetBody().GetRotation());
127 | context.drawImage(img, topleftX, topleftY);
128 | context.restore();
129 | }
130 | drawShape(s, context);
131 | }
132 | }
133 | }
134 | ```
135 |
136 | ### 完整代码
137 | ``` javascript
138 | var canvas;
139 | var ctx;
140 | var canvasWidth;
141 | var canvasHeight;
142 |
143 | var world;
144 |
145 | // 我们将创建世界封装至createWorld函数内
146 | function createWorld() {
147 | // 世界的大小
148 | var worldAABB = new b2AABB();
149 | worldAABB.minVertex.Set(-4000, -4000);
150 | worldAABB.maxVertex.Set(4000, 4000);
151 |
152 | //定义重力
153 | var gravity = new b2Vec2(0, 300);
154 |
155 | // 是否休眠
156 | var doSleep = false;
157 |
158 | // 最终创建世界
159 | var world = new b2World(worldAABB, gravity, doSleep);
160 |
161 | return world;
162 | }
163 |
164 | //绘画功能
165 | function drawWorld(world, context) {
166 | for (var j = world.m_jointList; j; j = j.m_next) {
167 | // 绘制关节
168 | // drawJoint(j, context);
169 | }
170 | for (var b = world.m_bodyList; b != null; b = b.m_next) {
171 | for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
172 | if (s.GetUserData() != undefined) {
173 | // 使用数据包括图片
174 | var img = s.GetUserData();
175 |
176 | // 图片的长和宽
177 | var x = s.GetPosition().x;
178 | var y = s.GetPosition().y;
179 | var topleftX = -img.clientWidth / 2;
180 | var topleftY = -img.clientHeight / 2;
181 |
182 | context.save();
183 | context.translate(x, y);
184 | context.rotate(s.GetBody().GetRotation());
185 | context.drawImage(img, topleftX, topleftY);
186 | context.restore();
187 | }
188 | drawShape(s, context);
189 | }
190 | }
191 | }
192 |
193 | // 创建圆形刚体
194 | function createBall(world, x, y, r) {
195 | // 创建圆形定义
196 | var ballSd = new b2CircleDef();
197 | ballSd.density = 1.0; // 设置密度
198 | ballSd.radius = 20; // 设置半径
199 | ballSd.restitution = 1.0; // 设置弹性
200 | ballSd.friction = 0; // 设置摩擦因子
201 | var ballBd = new b2BodyDef(); // 创建刚体定义
202 | ballBd.AddShape(ballSd); // 添加形状
203 | ballBd.position.Set(x || 0, y || 0); // 设置位置
204 | return world.CreateBody(ballBd); // 创建并返回刚体
205 | }
206 |
207 | // 创建矩形刚体
208 | function createBox(world, x, y, width, height, userData) {
209 | var boxSd = new b2BoxDef(); //创建一个形状Shape,然后设置有关Shape的属性
210 | boxSd.extents.Set(width || 1200, height || 5); //设置矩形高、宽
211 | boxSd.density = 1.0;
212 | boxSd.userData = userData;
213 | boxSd.restitution = .3; //设置矩形的弹性
214 | boxSd.friction = 1; //设置矩形的摩擦因子,可以设置为0-1之间任意一个数,0表示光滑,1表示强摩擦
215 |
216 | var boxBd = new b2BodyDef(); // 创建刚体定义
217 | boxBd.AddShape(boxSd); // 添加形状
218 | boxBd.position.Set(x || 10, y || 10); // 设置位置
219 | return world.CreateBody(boxBd) // 创建并返回刚体
220 | }
221 |
222 | // 定义step函数,用于游戏的迭代运行
223 | function step() {
224 | // 模拟世界
225 | world.Step(1.0 / 60, 1);
226 | // 清除画布
227 | ctx.clearRect(0, 0, canvasWidth, canvasHeight);
228 | // 重新绘制
229 | drawWorld(world, ctx);
230 |
231 | // 再次刷新
232 | setTimeout(step, 10);
233 | console.log('step...');
234 | }
235 |
236 | // 从draw_world.js里面引用的绘画功能
237 | function drawShape(shape, context) {
238 | context.strokeStyle = '#003300';
239 | context.beginPath();
240 | switch (shape.m_type) {
241 | // 绘制圆
242 | case b2Shape.e_circleShape:
243 | var circle = shape;
244 | var pos = circle.m_position;
245 | var r = circle.m_radius;
246 | var segments = 16.0;
247 | var theta = 0.0;
248 | var dtheta = 2.0 * Math.PI / segments;
249 | // 画圆圈
250 | context.moveTo(pos.x + r, pos.y);
251 | for (var i = 0; i < segments; i++) {
252 | var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
253 | var v = b2Math.AddVV(pos, d);
254 | context.lineTo(v.x, v.y);
255 | theta += dtheta;
256 | }
257 | context.lineTo(pos.x + r, pos.y);
258 |
259 | // 画半径
260 | context.moveTo(pos.x, pos.y);
261 | var ax = circle.m_R.col1;
262 | var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
263 | context.lineTo(pos2.x, pos2.y);
264 | break;
265 | // 绘制多边形
266 | case b2Shape.e_polyShape:
267 | var poly = shape;
268 | var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
269 | context.moveTo(tV.x, tV.y);
270 | for (var i = 0; i < poly.m_vertexCount; i++) {
271 | var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
272 | context.lineTo(v.x, v.y);
273 | }
274 | context.lineTo(tV.x, tV.y);
275 | break;
276 | }
277 | context.stroke();
278 | }
279 |
280 | window.onload = function() {
281 | canvas = document.getElementById('canvas');
282 | ctx = canvas.getContext('2d');
283 | canvasWidth = parseInt(canvas.width);
284 | canvasHeight = parseInt(canvas.height);
285 | // 启动游戏
286 | world = createWorld();
287 | var ball1 = createBall(world, 100, 20, 20);
288 | var ball2 = createBall(world, 300, 60, 10);
289 | var box1 = createBox(world, 200, 50, 20, 20);
290 | var box2 = createBox(world, 400, 80, 20, 20, document.getElementById('box'));
291 | step();
292 | };
293 | ```
294 |
295 | [此处查看项目代码(仅包含src部分)](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-2-add-body/6-2-add-body)
296 | [此处查看页面效果](http://old7pzwup.bkt.clouddn.com/6-2-add-body/index.html)
297 |
298 |
299 | ### [返回总目录](https://github.com/godbasin/box2djs-tutorial)
--------------------------------------------------------------------------------
/6-practice/6-4-mouse-operate-body/6-4-mouse-operate-body.md:
--------------------------------------------------------------------------------
1 | ## 鼠标操作刚体
2 | ### 获取所在位置刚体
3 | 首先,我们需要通过一个位置坐标(x, y)来获取该坐标下的刚体,若无则返回null。
4 |
5 | 前面我们也提到过,我们可以通过画一个较小的矩形,然后判断是否有相交来获取刚体:
6 |
7 | ``` javascript
8 | function GetBodyAtPosition(x, y) {
9 | // 首先创建一个近似于点的小区域
10 | var mousePVec = new b2Vec2(x, y);
11 | // 利用b2Vec2定义一个矢量,用来保存鼠标点击的点
12 | var aabb = new b2AABB();
13 | // 利用b2AABB创建一个环境
14 | aabb.minVertex.Set(mousePVec.x - 0.001, mousePVec.y - 0.001);
15 | aabb.maxVertex.Set(mousePVec.x + 0.001, mousePVec.y + 0.001);
16 | // 设置aabb的左上角及右下角坐标,这里是以鼠标点击位置为中心创建了一个长、宽均为0.002的矩形区域
17 |
18 | // 然后查询与指定区域有重叠的刚体
19 | var k_maxCount = 10; // 设定所要查找形状的数量,注意合理设置其大小,过大会影响运行速度
20 | var shapes = new Array(); // 保存查找到的与已知边界盒相交的形状
21 | var count = world.Query(aabb, shapes, k_maxCount); // 在世界中查找与边界盒相交的maxCount个形状,并返回边界盒区域内实际包含的形状的个数
22 |
23 | var findBody = null; // 首先设定没有找到物体
24 | for (var i = 0; i < count; ++i) {
25 | if (shapes[i].GetBody().IsStatic() == false)
26 | // 条件假定查找到的形状不是静态刚体,比如墙
27 | {
28 | var tShape = shapes[i]; // 将查找到的形状赋给tShape变量
29 | var inside = tShape.GetBody(); // 将tShape对应的刚体赋给inside
30 | if (inside) // 如果inside这个刚体存在
31 | {
32 | // 那么返回这个刚体,并跳出遍历
33 | findBody = tShape.GetBody();
34 | break;
35 | }
36 | }
37 | }
38 | return findBody;
39 | }
40 | ```
41 |
42 | ### 添加鼠标点击事件
43 | 这里我们使用鼠标点击(mousedown)事件,去触发刚体的状态改变。
44 |
45 | - 添加事件监听
46 |
47 | ``` javascript
48 | document.addEventListener('mousedown', handleMousedown, false);
49 | ```
50 |
51 | - 获取鼠标位置
52 |
53 | ``` javascript
54 | // 获取鼠标坐标
55 | function getMousePos(event) {
56 | var e = event || window.event;
57 | var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
58 | var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
59 | var x = e.pageX || e.clientX + scrollX;
60 | var y = e.pageY || e.clientY + scrollY;
61 | return { 'x': x, 'y': y };
62 | }
63 | ```
64 |
65 | - 处理鼠标移动
66 |
67 | ``` javascript
68 | // 处理鼠标移动
69 | function handleMousedown(e) {
70 | var e = e || window.event;
71 | // 获取鼠标x坐标
72 | var newMouse = getMousePos(e);
73 | var selectBody = GetBodyAtPosition(newMouse.x - canvas.offsetLeft, newMouse.y - canvas.offsetTop); // 选择刚体
74 | if (selectBody) {
75 | // 处理选中的刚体
76 | } else {
77 | // 未选中时处理
78 | }
79 | }
80 | ```
81 |
82 | ### 操作刚体
83 | - 给选中刚体添加速度向量
84 |
85 | ``` javascript
86 | // 若有选中刚体,则处理
87 | var LinearVelocity = new b2Vec2(500, -200); // 定义一个向量
88 | selectBody.WakeUp(); // 激活休眠状态
89 | selectBody.SetLinearVelocity(LinearVelocity); //给定一个速度向量
90 | ```
91 |
92 | - 未选中刚体,则新建添加一个矩形刚体
93 |
94 | ``` javascript
95 | // 若无,则随机生成一个矩形添加
96 | var width = parseInt(Math.random() * 50);
97 | var height = parseInt(Math.random() * 50);
98 | createBox(world, newMouse.x, newMouse.y, width, height)
99 | ```
100 |
101 | ### 完整代码
102 | ``` javascript
103 | var canvas;
104 | var ctx;
105 | var canvasWidth;
106 | var canvasHeight;
107 |
108 | var world;
109 |
110 | // 我们将创建世界封装至createWorld函数内
111 | function createWorld() {
112 | // 世界的大小
113 | var worldAABB = new b2AABB();
114 | worldAABB.minVertex.Set(-4000, -4000);
115 | worldAABB.maxVertex.Set(4000, 4000);
116 |
117 | //定义重力
118 | var gravity = new b2Vec2(0, 300);
119 |
120 | // 是否休眠
121 | var doSleep = false;
122 |
123 | // 最终创建世界
124 | var world = new b2World(worldAABB, gravity, doSleep);
125 |
126 | return world;
127 | }
128 |
129 | //绘画功能
130 | function drawWorld(world, context) {
131 | for (var j = world.m_jointList; j; j = j.m_next) {
132 | // 绘制关节
133 | // drawJoint(j, context);
134 | }
135 | for (var b = world.m_bodyList; b != null; b = b.m_next) {
136 | for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
137 | if (s.GetUserData() != undefined) {
138 | // 使用数据包括图片
139 | var img = s.GetUserData();
140 |
141 | // 图片的长和宽
142 | var x = s.GetPosition().x;
143 | var y = s.GetPosition().y;
144 | var topleftX = -img.clientWidth / 2;
145 | var topleftY = -img.clientHeight / 2;
146 |
147 | context.save();
148 | context.translate(x, y);
149 | context.rotate(s.GetBody().GetRotation());
150 | context.drawImage(img, topleftX, topleftY);
151 | context.restore();
152 | }
153 | drawShape(s, context);
154 | }
155 | }
156 | }
157 |
158 | // 创建圆形刚体
159 | function createBall(world, x, y, r, custom) {
160 | // 创建圆形定义
161 | var ballSd = new b2CircleDef();
162 | ballSd.density = 1.0; // 设置密度
163 | if (custom === 'fixed') ballSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
164 | else ballSd.userData = custom; // 若传入其他,则视为图片数据
165 | ballSd.radius = 20; // 设置半径
166 | ballSd.restitution = 1.0; // 设置弹性
167 | ballSd.friction = 0; // 设置摩擦因子
168 | var ballBd = new b2BodyDef(); // 创建刚体定义
169 | ballBd.AddShape(ballSd); // 添加形状
170 | ballBd.position.Set(x || 0, y || 0); // 设置位置
171 | return world.CreateBody(ballBd); // 创建并返回刚体
172 | }
173 |
174 | // 创建矩形刚体
175 | function createBox(world, x, y, width, height, custom) {
176 | var boxSd = new b2BoxDef(); // 创建一个形状Shape,然后设置有关Shape的属性
177 | boxSd.extents.Set(width || 1200, height || 5); // 设置矩形高、宽
178 | boxSd.density = 1.0; // 设置矩形的密度
179 | if (custom === 'fixed') boxSd.density = 0.0; // 若传入'fixed',则需固定,此时设置密度为0
180 | else boxSd.userData = custom; // 若传入其他,则视为图片数据
181 | boxSd.restitution = .3; // 设置矩形的弹性
182 | boxSd.friction = 1; // 设置矩形的摩擦因子,可以设置为0-1之间任意一个数,0表示光滑,1表示强摩擦
183 |
184 | var boxBd = new b2BodyDef(); // 创建刚体定义
185 | boxBd.AddShape(boxSd); // 添加形状
186 | boxBd.position.Set(x || 10, y || 10); // 设置位置
187 | return world.CreateBody(boxBd) // 创建并返回刚体
188 | }
189 |
190 | // 定义step函数,用于游戏的迭代运行
191 | function step() {
192 | // 模拟世界
193 | world.Step(1.0 / 60, 1);
194 | // 清除画布
195 | ctx.clearRect(0, 0, canvasWidth, canvasHeight);
196 | // 重新绘制
197 | drawWorld(world, ctx);
198 |
199 | // 再次刷新
200 | setTimeout(step, 10);
201 | }
202 |
203 | // 从draw_world.js里面引用的绘画功能
204 | function drawShape(shape, context) {
205 | context.strokeStyle = '#003300';
206 | context.beginPath();
207 | switch (shape.m_type) {
208 | // 绘制圆
209 | case b2Shape.e_circleShape:
210 | var circle = shape;
211 | var pos = circle.m_position;
212 | var r = circle.m_radius;
213 | var segments = 16.0;
214 | var theta = 0.0;
215 | var dtheta = 2.0 * Math.PI / segments;
216 | // 画圆圈
217 | context.moveTo(pos.x + r, pos.y);
218 | for (var i = 0; i < segments; i++) {
219 | var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
220 | var v = b2Math.AddVV(pos, d);
221 | context.lineTo(v.x, v.y);
222 | theta += dtheta;
223 | }
224 | context.lineTo(pos.x + r, pos.y);
225 |
226 | // 画半径
227 | context.moveTo(pos.x, pos.y);
228 | var ax = circle.m_R.col1;
229 | var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
230 | context.lineTo(pos2.x, pos2.y);
231 | break;
232 | // 绘制多边形
233 | case b2Shape.e_polyShape:
234 | var poly = shape;
235 | var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
236 | context.moveTo(tV.x, tV.y);
237 | for (var i = 0; i < poly.m_vertexCount; i++) {
238 | var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
239 | context.lineTo(v.x, v.y);
240 | }
241 | context.lineTo(tV.x, tV.y);
242 | break;
243 | }
244 | context.stroke();
245 | }
246 |
247 | function GetBodyAtPosition(x, y) {
248 | // 首先创建一个近似于点的小区域
249 | var mousePVec = new b2Vec2(x, y);
250 | // 利用b2Vec2定义一个矢量,用来保存鼠标点击的点
251 | var aabb = new b2AABB();
252 | // 利用b2AABB创建一个环境
253 | aabb.minVertex.Set(mousePVec.x - 0.001, mousePVec.y - 0.001);
254 | aabb.maxVertex.Set(mousePVec.x + 0.001, mousePVec.y + 0.001);
255 | // 设置aabb的左上角及右下角坐标,这里是以鼠标点击位置为中心创建了一个长、宽均为0.002的矩形区域
256 |
257 | // 然后查询与指定区域有重叠的刚体
258 | var k_maxCount = 10; // 设定所要查找形状的数量,注意合理设置其大小,过大会影响运行速度
259 | var shapes = new Array(); // 保存查找到的与已知边界盒相交的形状
260 | var count = world.Query(aabb, shapes, k_maxCount); // 在世界中查找与边界盒相交的maxCount个形状,并返回边界盒区域内实际包含的形状的个数
261 |
262 | var findBody = null; // 首先设定没有找到物体
263 | for (var i = 0; i < count; ++i) {
264 | if (shapes[i].GetBody().IsStatic() == false)
265 | // 条件假定查找到的形状不是静态刚体,比如墙
266 | {
267 | var tShape = shapes[i]; // 将查找到的形状赋给tShape变量
268 | var inside = tShape.GetBody(); // 将tShape对应的刚体赋给inside
269 | if (inside) // 如果inside这个刚体存在
270 | {
271 | // 那么返回这个刚体,并跳出遍历
272 | findBody = tShape.GetBody();
273 | break;
274 | }
275 | }
276 | }
277 | return findBody;
278 | }
279 |
280 | // 获取鼠标坐标
281 | function getMousePos(event) {
282 | var e = event || window.event;
283 | var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
284 | var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
285 | var x = e.pageX || e.clientX + scrollX;
286 | var y = e.pageY || e.clientY + scrollY;
287 | return { 'x': x, 'y': y };
288 | }
289 |
290 | // 处理鼠标移动
291 | function handleMousedown(e) {
292 | var e = e || window.event;
293 | // 获取鼠标x坐标
294 | var newMouse = getMousePos(e);
295 | var selectBody = GetBodyAtPosition(newMouse.x - canvas.offsetLeft, newMouse.y - canvas.offsetTop); // 选择刚体
296 | if (selectBody) {
297 | // 若有选中刚体,则处理
298 | var LinearVelocity = new b2Vec2(500, -200); // 定义一个向量
299 | selectBody.WakeUp(); // 激活休眠状态
300 | selectBody.SetLinearVelocity(LinearVelocity); //给定一个速度向量
301 | } else {
302 | // 若无,则随机生成一个矩形添加
303 | var width = parseInt(Math.random() * 50);
304 | var height = parseInt(Math.random() * 50);
305 | createBox(world, newMouse.x, newMouse.y, width, height);
306 | }
307 | }
308 |
309 | document.addEventListener('mousedown', handleMousedown, false);
310 |
311 | window.onload = function () {
312 | canvas = document.getElementById('canvas');
313 | ctx = canvas.getContext('2d');
314 | canvasWidth = parseInt(canvas.width);
315 | canvasHeight = parseInt(canvas.height);
316 | // 启动游戏
317 | world = createWorld();
318 | var ball1 = createBall(world, 100, 20, 20);
319 | var ball2 = createBall(world, 300, 60, 10);
320 | var box1 = createBox(world, 100, 200, 25, 30, 'fixed');
321 | var box2 = createBox(world, 200, 50, 20, 20);
322 | var box3 = createBox(world, 400, 80, 20, 20, document.getElementById('box'));
323 | var wallLeft = createBox(world, 0, 0, 10, 600, 'fixed');
324 | var wallRight = createBox(world, 1290, 0, 10, 400, 'fixed');
325 | var ground = createBox(world, 30, 595, 1200, 5, 'fixed');
326 | step();
327 | };
328 | ```
329 |
330 | [此处查看项目代码(仅包含src部分)](https://github.com/godbasin/box2djs-tutorial/tree/master/6-practice/6-4-mouse-operate-body/6-4-mouse-operate-body)
331 | [此处查看页面效果](http://old7pzwup.bkt.clouddn.com/6-4-mouse-operate-body/index.html)
332 |
333 |
334 | ### [返回总目录](https://github.com/godbasin/box2djs-tutorial)
--------------------------------------------------------------------------------