上一课中我们了解了如何去创建一个box2d的世界,如果在这个世界中添加刚体,如何通debug模式查看效果。
现在我们来学习一个让人激动的跳起来的酷东西 —— “关节”。
顾名思义,我们可以通过关节来建立各式各样的物理系统,链条、齿轮、绳索、甚至是通过关节来建立一个物理系统,等等。
听起来是不是很激动人心?是的,我们现在马上来看看box2d到底提供了哪些关节类吧:
Distance Joint
Rope Joint
Revolute Joint
Prismatic Joint
Pulley Joint
Gear Joint
Line Joint
Weld Joint
Mouse Joint
五花八门的关节类让我们眼花缭乱,光看类名也无法完全体会到这些关节类之间的差异。还是通过具体的实现来一个一个看效果吧
no.1 , Distance Joint
距离关节,顾名思义,通过该关节连接的两个刚体之间的距离是恒定不变的。
_box2 = createBox(new Point(SWF_HALF_WIDTH+50, 50), 10, 10, false, 1, 0.3);
createJoint();
private function createJoint():void
{
_distanceJointDef = new b2DistanceJointDef();
_distanceJointDef.Initialize(_box1,_box2,_box1.GetWorldCenter(),_box2.GetWorldCenter());
_world.CreateJoint(_distanceJointDef);
}
no.2 , Rope Joint
绳索关节,绳索关节跟距离关节非常类似,都是把两个刚体的距离限定为一个距离值,唯一不同的地方在于绳索关节中两个刚体的距离不会被限定成这个距离值,还可以小于他。你可以把Distance Joint想象成一个刚体的绳索,而Rope Joint则是一个可以伸缩的绳索。
另外需要注意的是:最新的box2d 2.1a 中似乎已经移除了关于RopeJoint的定义,如果你想要使用它,你可以在这里下载到相关的类:http://blog.allanbishop.com/box2d-2-1a-rope-joint-ported-to-as3/
存放位置为Dynamic\Joints。其中b2Joint.as需要覆盖原先的。
_box2 = createBox(new Point(SWF_HALF_WIDTH+50, 50), 10, 10, false, 1, 0.3);
createJoint();
private function createJoint():void
{
_ropeJointDef = new b2RopeJointDef();
_ropeJointDef.bodyA = _box1;
_ropeJointDef.bodyB = _box2;
_ropeJointDef.localAnchorA = new b2Vec2(0,0);
_ropeJointDef.localAnchorB = new b2Vec2(0,0);
_ropeJointDef.maxLength = 100/PIXELS_TO_METER;
_ropeJointDef.collideConnected = true;
_world.CreateJoint(_ropeJointDef);
}
no.3 Revolute Joint
转动关节,这是一个非常实用的关节类。当我们通过这个关节连接的两个刚体中有一个staticBody,有一个是dynamicBody时,dynamicBody会绕着staticBody进行旋转,就好像把一张纸钉在墙上。两个dynamic bodies也可以通过它来连接,那么这两个刚体就会互相旋转。此外,你还可以对它们设置扭矩。
_box2 = createBox(new Point(SWF_HALF_WIDTH+50, 50), 10, 10, false, 1, 0.3);
createJoint();
private function createJoint():void
{
_revoluteJointDef = new b2RevoluteJointDef();
_revoluteJointDef.Initialize(_box1,_box2,_box2.GetWorldCenter());
_revoluteJointDef.maxMotorTorque = 1;
_revoluteJointDef.enableMotor = true;
_world.CreateJoint(_revoluteJointDef);
}
no.4 Prismatic Joint
移动关节,允许两个刚体沿着某个轴相对移动,类似于传送带。需要注意的是移动关节阻止相对旋转。
createJoint();
private function createJoint():void
{
_prismaticJointDef = new b2PrismaticJointDef();
_prismaticJointDef.Initialize(_box1,_groundBody,_box1.GetWorldCenter(),new b2Vec2(1,0));
_prismaticJointDef.lowerTranslation = -5;
_prismaticJointDef.upperTranslation = 5;
_prismaticJointDef.enableLimit = true;
_prismaticJointDef.maxMotorForce = 1;
_prismaticJointDef.motorSpeed = 1;
_prismaticJointDef.enableMotor = true;
_world.CreateJoint(_prismaticJointDef);
}
no.5 Pulley Joint
滑轮关节,两个刚体被连接起来,当一个落下,另一个物体便抬起来。你可以通过设置一个ratio值来决定两段距离之间的换算关系。
createJoint();
private function createJoint():void
{
var anchor1:b2Vec2 = _box1.GetWorldCenter();
var anchor2:b2Vec2 = _box2.GetWorldCenter();
var groundAnchor1:b2Vec2 = new b2Vec2(anchor1.x,anchor1.y-100/PIXELS_TO_METER);
var groundAnchor2:b2Vec2 = new b2Vec2(anchor2.x , anchor2.y - 100/PIXELS_TO_METER);
var ratio:Number = 1;
_pullyJointDef = new b2PulleyJointDef();
_pullyJointDef.Initialize(_box1,_box2,groundAnchor1,groundAnchor2,anchor1,anchor2,ratio);
_pullyJointDef.maxLengthA = 150/PIXELS_TO_METER;
_pullyJointDef.maxLengthB = 150/PIXELS_TO_METER;
_world.CreateJoint(_pullyJointDef);
}
no.6 Gear Joint
齿轮关节,齿轮关节需要两个刚体通过旋转关节或者平移关节连接起来。同样的,你也可以给他设定ratio值。
_box2 = createBox(new Point(SWF_HALF_WIDTH+50, 200), 10, 10, true, 1, 0.3);
createJoint();
private function createJoint():void
{
_revoluteJointDef = new b2RevoluteJointDef();
_revoluteJointDef.Initialize(_groundBody,_box1,_box1.GetWorldCenter());
_revoluteJointDef.lowerAngle = -5 * b2Settings.b2_pi;
_revoluteJointDef.upperAngle = 5 * b2Settings.b2_pi;
_revoluteJointDef.enableLimit = true;
_revoluteJointDef.maxMotorTorque = 10;
_revoluteJointDef.motorSpeed = 1;
_revoluteJointDef.enableMotor = true;
_revoluteJoint = _world.CreateJoint(_revoluteJointDef) as b2RevoluteJoint;
_prismaticJointDef = new b2PrismaticJointDef();
_prismaticJointDef.Initialize(_groundBody,_box2,_box2.GetWorldCenter(),new b2Vec2(1,0));
_prismaticJointDef.lowerTranslation = -50;
_prismaticJointDef.upperTranslation = 50;
_prismaticJointDef.enableLimit = true;
_prismaticJointDef.maxMotorForce = 1;
_prismaticJointDef.motorSpeed = 1;
_prismaticJointDef.enableMotor = true;
_prismaticJoint = _world.CreateJoint(_prismaticJointDef) as b2PrismaticJoint;
_gearJointDef = new b2GearJointDef();
_gearJointDef.joint1 = _revoluteJoint;
_gearJointDef.joint2 = _prismaticJoint;
_gearJointDef.bodyA = _box1;
_gearJointDef.bodyB = _box2;
_gearJointDef.ratio = 2*b2Settings.b2_pi;
_world.CreateJoint(_gearJointDef);
}
no.7 Line Joint
线性关节,跟移动关节类似,但没有旋转方面的约束。一般都用来模拟汽车的车轮。
createJoint();
private function createJoint():void
{
_lineJointDef = new b2LineJointDef();
_lineJointDef.Initialize(_groundBody,_box1,_box1.GetWorldCenter(),new b2Vec2(1,0));
_lineJointDef.lowerTranslation = -5;
_lineJointDef.upperTranslation = 5;
_lineJointDef.enableLimit = true;
_lineJointDef.maxMotorForce = 10;
_lineJointDef.motorSpeed = 10;
_lineJointDef.enableMotor = true;
_world.CreateJoint(_lineJointDef);
}
no.8 Weld Joint
焊接节点,这个最容易了。。就是把两个刚体焊接在一起,可以用来创建各种结构。
_box2 = createBox(new Point(SWF_HALF_WIDTH+50, 200), 10, 10, true, 1, 0.3);
createJoint();
private function createJoint():void
{
_weldJointDef = new b2WeldJointDef();
_weldJointDef.Initialize(_box1,_box2,_box1.GetWorldCenter());
_world.CreateJoint(_weldJointDef);
}
no.9 Mouse Joint
鼠标节点,顾名思义,用来和box2d世界做鼠标交互的节点。我们马上来看看怎么用~
这同时也是九个节点中最复杂的。
首先创建监听
_box2 = createBox(new Point(SWF_HALF_WIDTH+50, 200), 10, 10, true, 1, 0.3);
stage.addEventListener(MouseEvent.MOUSE_DOWN , onDragHandler);
stage.addEventListener(MouseEvent.MOUSE_UP , onDropHandler);
监听函数
{
var body:b2Body = getBodyAtMouse();
if( body )
{
_mouseJointDef = new b2MouseJointDef();
_mouseJointDef.bodyA = _world.GetGroundBody();
_mouseJointDef.bodyB = body;
_mouseJointDef.target.Set(mouseX/PIXELS_TO_METER,mouseY/PIXELS_TO_METER);
_mouseJointDef.maxForce = 3000;
_mouseJoint = _world.CreateJoint(_mouseJointDef) as b2MouseJoint;
}
}
protected function onDropHandler(event:MouseEvent):void
{
if(_mouseJoint)
{
_world.DestroyJoint(_mouseJoint);
_mouseJoint = null;
}
}
private function getBodyAtMouse(includeStatic:Boolean=false):b2Body
{
var mouseXWorldPhys:Number = mouseX/PIXELS_TO_METER;
var mouseYWorldPhys:Number = mouseY/PIXELS_TO_METER;
_mousePVec.Set(mouseXWorldPhys,mouseYWorldPhys);
var aabb:b2AABB = new b2AABB();
aabb.lowerBound.Set(mouseXWorldPhys-0.001,mouseXWorldPhys+0.001);
aabb.upperBound.Set(mouseYWorldPhys-0.001,mouseYWorldPhys+0.001);
var body:b2Body = null;
var fixture:b2Fixture ;
function GetBodyCallBack(fixture:b2Fixture):Boolean
{
var shape:b2Shape = fixture.GetShape();
if( fixture.GetBody().GetType() != b2Body.b2_staticBody || includeStatic )
{
var inside:Boolean = shape.TestPoint(fixture.GetBody().GetTransform(),_mousePVec);
if( inside )
{
body = fixture.GetBody();
return false;
}
}
return true;
}
world.QueryAABB(GetBodyCallBack,aabb);
return body;
}
刷新函数
{
var timeStep:Number = 1 / 30;
var velocityIterations:int = 6;
var positionIterations:int = 2;
if(_mouseJoint)
{
var xpos:Number = mouseX/PIXELS_TO_METER;
var ypos:Number = mouseY/PIXELS_TO_METER;
var v2:b2Vec2 = new b2Vec2(xpos,ypos);
_mouseJoint.SetTarget(v2);
}
_world.Step(timeStep,velocityIterations,positionIterations);
_world.ClearForces();
_world.DrawDebugData();
}
OK,大功告成,你可以把你的小盒子扔来扔去了……
这次我们只是大致了解了这些节点,接下来我们要更加深入的去了解和熟悉他们。
附上鼠标节点的源码:
2 {
3 import Box2D.Collision.Shapes.b2PolygonShape;
4 import Box2D.Collision.Shapes.b2Shape;
5 import Box2D.Collision.b2AABB;
6 import Box2D.Common.Math.b2Vec2;
7 import Box2D.Common.b2Settings;
8 import Box2D.Dynamics.Joints.b2DistanceJointDef;
9 import Box2D.Dynamics.Joints.b2GearJointDef;
10 import Box2D.Dynamics.Joints.b2Joint;
11 import Box2D.Dynamics.Joints.b2LineJointDef;
12 import Box2D.Dynamics.Joints.b2MouseJoint;
13 import Box2D.Dynamics.Joints.b2MouseJointDef;
14 import Box2D.Dynamics.Joints.b2PrismaticJoint;
15 import Box2D.Dynamics.Joints.b2PrismaticJointDef;
16 import Box2D.Dynamics.Joints.b2PulleyJointDef;
17 import Box2D.Dynamics.Joints.b2RevoluteJoint;
18 import Box2D.Dynamics.Joints.b2RevoluteJointDef;
19 import Box2D.Dynamics.Joints.b2RopeJointDef;
20 import Box2D.Dynamics.b2Body;
21 import Box2D.Dynamics.b2BodyDef;
22 import Box2D.Dynamics.b2DebugDraw;
23 import Box2D.Dynamics.b2Fixture;
24 import Box2D.Dynamics.b2FixtureDef;
25 import Box2D.Dynamics.b2World;
26
27 import flash.display.Sprite;
28 import flash.events.Event;
29 import flash.events.MouseEvent;
30 import flash.geom.Point;
31
32 [SWF(frameRate=60,width=800,height=600)]
33 public class Box2dClass_2 extends Sprite
34 {
35 private const PIXELS_TO_METER:int = 30;
36 private const SWF_WIDTH:int = 800;
37 private const SWF_HEIGHT:int = 600;
38 private const SWF_HALF_WIDTH:int = SWF_WIDTH>>1;
39 private const SWF_HALF_HEIGHT:int = SWF_HEIGHT>>1;
40
41 private var _world:b2World;
42 private var _groundDef:b2BodyDef;
43 private var _groundBody:b2Body;
44 private var _groundBox:b2PolygonShape;
45 private var _groundFixtureDef:b2FixtureDef;
46 private var _debugSprite:Sprite;
47 private var _debugDraw:b2DebugDraw;
48
49 private var _box1:b2Body;
50 private var _box2:b2Body;
51 private var _distanceJointDef:b2DistanceJointDef;
52 private var _box3:b2Body;
53 private var _ropeJointDef:b2RopeJointDef;
54 private var _revoluteJointDef:b2RevoluteJointDef;
55 private var _prismaticJointDef:b2PrismaticJointDef;
56 private var _pullyJointDef:b2PulleyJointDef;
57 private var _revoluteJoint:b2RevoluteJoint;
58 private var _gearJointDef:b2GearJointDef;
59 private var _prismaticJoint:b2Joint;
60 private var _lineJointDef:b2LineJointDef;
61
62 private var _mousePVec:b2Vec2 = new b2Vec2();
63 private var _mouseJointDef:b2MouseJointDef;
64 private var _mouseJoint:b2MouseJoint;
65
66 public function Box2dClass_2()
67 {
68 stage.color = 0x333333;
69 createWorld();
70 createDebug();
71 createGround();
72 _box1 = createBox(new Point(SWF_HALF_WIDTH, 200), 10, 10, true, 1, 0.3);
73 _box2 = createBox(new Point(SWF_HALF_WIDTH+50, 200), 10, 10, true, 1, 0.3);
74 createJoint();
75
76 stage.addEventListener(Event.ENTER_FRAME, update);
77 stage.addEventListener(MouseEvent.MOUSE_DOWN , onDragHandler);
78 stage.addEventListener(MouseEvent.MOUSE_UP , onDropHandler);
79 }
80
81 protected function onDragHandler(event:MouseEvent):void
82 {
83 var body:b2Body = getBodyAtMouse();
84 if( body )
85 {
86 _mouseJointDef = new b2MouseJointDef();
87 _mouseJointDef.bodyA = _world.GetGroundBody();
88 _mouseJointDef.bodyB = body;
89 _mouseJointDef.target.Set(mouseX/PIXELS_TO_METER,mouseY/PIXELS_TO_METER);
90 _mouseJointDef.maxForce = 3000;
91 _mouseJoint = _world.CreateJoint(_mouseJointDef) as b2MouseJoint;
92 }
93 }
94
95 protected function onDropHandler(event:MouseEvent):void
96 {
97 if(_mouseJoint){
98 _world.DestroyJoint(_mouseJoint);
99 _mouseJoint = null;
100 }
101 }
102
103 private function getBodyAtMouse(includeStatic:Boolean=false):b2Body
104 {
105 var mouseXWorldPhys:Number = mouseX/PIXELS_TO_METER;
106 var mouseYWorldPhys:Number = mouseY/PIXELS_TO_METER;
107 _mousePVec.Set(mouseXWorldPhys,mouseYWorldPhys);
108 var aabb:b2AABB = new b2AABB();
109 aabb.lowerBound.Set(mouseXWorldPhys-0.001,mouseXWorldPhys+0.001);
110 aabb.upperBound.Set(mouseYWorldPhys-0.001,mouseYWorldPhys+0.001);
111 var body:b2Body = null;
112 var fixture:b2Fixture ;
113
114 function GetBodyCallBack(fixture:b2Fixture):Boolean
115 {
116
117 var shape:b2Shape = fixture.GetShape();
118 if( fixture.GetBody().GetType() != b2Body.b2_staticBody || includeStatic )
119 {
120 var inside:Boolean = shape.TestPoint(fixture.GetBody().GetTransform(),_mousePVec);
121 if( inside )
122 {
123 body = fixture.GetBody();
124 return false;
125 }
126 }
127 return true;
128 }
129
130 _world.QueryAABB(GetBodyCallBack,aabb);
131 return body;
132 }
133
134 private function createJoint():void
135 {
136
137 }
138
139 private function createWorld():void
140 {
141 _world = new b2World(new b2Vec2(0,10),true );
142 }
143
144 private function createDebug():void
145 {
146 _debugSprite = new Sprite();
147 addChild(_debugSprite);
148
149 _debugDraw = new b2DebugDraw();
150 _debugDraw.SetSprite(_debugSprite);
151 _debugDraw.SetDrawScale(PIXELS_TO_METER);
152 _debugDraw.SetLineThickness(1);
153 _debugDraw.SetAlpha(1);
154 _debugDraw.SetFillAlpha(0.4);
155 _debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
156 _world.SetDebugDraw(_debugDraw);
157 }
158
159 private function createGround():void
160 {
161 _groundDef = new b2BodyDef();
162 _groundDef.position.Set(SWF_HALF_WIDTH/PIXELS_TO_METER,(SWF_HEIGHT-20)/PIXELS_TO_METER);
163 _groundDef.type = b2Body.b2_staticBody;
164
165 _groundBody = _world.CreateBody(_groundDef);
166
167 _groundBox = new b2PolygonShape();
168 _groundBox.SetAsBox(SWF_HALF_WIDTH/PIXELS_TO_METER,20/PIXELS_TO_METER);
169
170 _groundFixtureDef = new b2FixtureDef();
171 _groundFixtureDef.shape = _groundBox;
172 _groundFixtureDef.density = 0;
173 _groundFixtureDef.friction = 0;
174 _groundFixtureDef.restitution = 0;
175
176 _groundBody.CreateFixture(_groundFixtureDef);
177 }
178
179 private function createBox(position:Point,hw:Number,hh:Number,isDynamic:Boolean,density:Number=0,friction:Number=0,restitution:Number=0):b2Body
180 {
181 var bodyDef:b2BodyDef = new b2BodyDef();
182 if( isDynamic )
183 {
184 bodyDef.type = b2Body.b2_dynamicBody;
185 }
186 else
187 {
188 bodyDef.type = b2Body.b2_staticBody;
189 }
190 bodyDef.position.Set(position.x/PIXELS_TO_METER,position.y/PIXELS_TO_METER);
191
192 var body:b2Body = _world.CreateBody(bodyDef);
193
194 var shape:b2PolygonShape = new b2PolygonShape();
195 shape.SetAsBox(hw/PIXELS_TO_METER,hh/PIXELS_TO_METER);
196
197 var fixtureDef:b2FixtureDef = new b2FixtureDef();
198 fixtureDef.shape = shape;
199 fixtureDef.density = density;
200 fixtureDef.friction = friction;
201 fixtureDef.restitution = restitution;
202
203 body.CreateFixture(fixtureDef);
204
205 return body;
206 }
207
208 protected function update(event:Event):void
209 {
210 var timeStep:Number = 1 / 30;
211 var velocityIterations:int = 6;
212 var positionIterations:int = 2;
213
214 if(_mouseJoint)
215 {
216 var xpos:Number = mouseX/PIXELS_TO_METER;
217 var ypos:Number = mouseY/PIXELS_TO_METER;
218 var v2:b2Vec2 = new b2Vec2(xpos,ypos);
219 _mouseJoint.SetTarget(v2);
220 }
221
222 _world.Step(timeStep,velocityIterations,positionIterations);
223 _world.ClearForces();
224 _world.DrawDebugData();
225 }
226 }
227 }