[box2d] Box2d学习笔记 - Class.01 - 2012.10.5
怎么样创建一个box2d的世界。
step.1 :
创建一个世界。
var1 : (gravity:b2Vec2) , 即重力。
var2 : (doSleep:Boolean) , 用来管理世界中的物体是否可以休眠。
step.2 :
创建一个地面,把地面看成一个巨大的四边形刚体。所以我们现在要做的也相对于创建一个四边形刚体。
首先创建一个地面的definition,即b2BodyDef。
_groundDef.position.Set(SWF_HALF_WIDTH/PIXELS_TO_METER,(SWF_HEIGHT-20)/PIXELS_TO_METER);
_groundDef.type = b2Body.b2_staticBody;
b2BodyDef是用来存放刚体在这个世界中的所有数据。
这里要注意一下box2d的特性,1:所有object的注册点都在中心。2:box2d中的单位是米。
所以如果我们想将地面放置在舞台底部往上20个像素的地方。我们需要这样处理
_groundDef.position.Set(SWF_HALF_WIDTH/PIXELS_TO_METER,(SWF_HEIGHT-20)/PIXELS_TO_METER);
其中SWF_HALF_WIDTH即舞台宽度的一半,SWF_HEIGHT即舞台高度,PIXEL_TO_METER是一个比例尺,用来描述多少个像素对应1米(一般是30)。
type是刚体类型。分为三种:staticBody , kinematicBody , dynamicBody 。由于地面是静止的刚体,所以我们给他设置为staticBody。
至于kinematicBody和dynamicBody的区别我们以后再研究。
然后创建地面刚体,即b2Body。
b2Body就是刚体啦。
这里我们可以注意到,box2d采取的是工厂模式。即通过world完成所有创建刚体的工作。
接着我们需要创建这个刚体的形状,这里我们使用b2PolygonShape。当然还有一些其他的形状,我们以后再去了解。
_groundBox.SetAsBox(SWF_HALF_WIDTH/PIXELS_TO_METER,20/PIXELS_TO_METER);
b2PolygonShape就是这个刚体的形状啦。
通常要创建四边形的话会用到SetAsBox()或者SetAsOrientedBox(),其中后者可以指定注册点和角度。
另外需要注意的是,在这个方法中传递的宽度值和高度值都是半宽和半高。当然还需要转换到以米为单位。
我们刚才创建的形状只是这个刚体的definition的一部分。最终我们需要创建的是我们这个地面刚体的definition。
_groundFixtureDef.shape = _groundBox;
_groundFixtureDef.density = 1;
_groundFixtureDef.friction = 1;
_groundFixtureDef.restitution = 0;
_groundBody.CreateFixture(_groundFixtureDef);
b2FixtureDef与之前的b2BodyDef的区别在于。
b2BodyDef是用来存放刚体在世界中的数据,比如position(x,y) , angle 等等属性。
b2FixtureDef则是用来存放刚体自身的数据,比如shape(polygonShape,等等),density,friction等等属性。
这样我们就已经完成了创建地面刚体的整个过程。
接下来我们需要让他们动起来。
step.3:
private function update(e:Event):void
{
var timeStep:Number = 1 / 30;
var velocityIterations:int = 6;
var positionIterations:int = 2;
_world.Step(timeStep,velocityIterations,positionIterations);
_world.ClearForces();
}
我们通过b2World的step方法来刷新整个世界。
step方法要传递三个参数,timeStep,velocityIterations, positionIterations。
timeStep,时步。通常建议设置为1/60,这样的话你的fps最好也设置到60。但是我发现更好的办法就是把timeStep设置为fps的倒数即可。如果你的fps是30,那么就设置为1/30。
velocityIterations,速度迭代
positionIterations,位置迭代
这两个值通常建议设置为10,更低的迭代值会让你牺牲一些准确性,相反的为你的程序提升一部分性能。
另外在box2d version2.1中我们需要清除力。
_world.ClearForces();
step.4:
现在我们尝试在空中放置一个小小的刚体让他自由下落。
这跟我们刚才创建地面刚体的做法基本上一致,唯一不同的是需要把刚体的种类设置为dynamicBody。
所以我们把创建四边形刚体的所有步骤集合在一个方法内。方便反复调用。
private function createBox(position:Point,hw:Number,hh:Number,isDynamic:Boolean,density:Number=0,friction:Number=0,restitution:Number=0):void
{
var bodyDef:b2BodyDef = new b2BodyDef();
if( isDynamic )
{
bodyDef.type = b2Body.b2_kinematicBody.b2_dynamicBody;
}
else
{
bodyDef.type = b2Body.b2_staticBody;
}
bodyDef.position.Set(position.x/PIXELS_TO_METER,position.y/PIXELS_TO_METER);
var body:b2Body = _world.CreateBody(bodyDef);
var shape:b2PolygonShape = new b2PolygonShape();
shape.SetAsBox(hw/PIXELS_TO_METER,hh/PIXELS_TO_METER);
var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = density;
fixtureDef.friction = friction;
fixtureDef.restitution = restitution;
body.CreateFixture(fixtureDef);
}
同时把刚才创建地面刚体的过程通过我们写好的方法来实现。
这样我们基本上已经完成了一个世界的搭建。
可是当我们编译的时候却看不到任何东西。
什么情况!!
step.5 :
不要急,原来我们刚才创建的世界只是一个虚拟的世界。我们并没有给他付于实质性的ui,所以我们压根什么都看不见。
我们现在没有ui,那该怎么办呢?
没关系,box2d已经提供给我们一个debug模式,可以让我们在没有ui的时候看到我们的世界。
{
_debugSprite = new Sprite();
addChild(_debugSprite);
_debugDraw = new b2DebugDraw();
_debugDraw.SetSprite(_debugSprite);
_debugDraw.SetDrawScale(PIXELS_TO_METER);
_debugDraw.SetLineThickness(1);
_debugDraw.SetAlpha(1);
_debugDraw.SetFillAlpha(0.4);
_debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
_world.SetDebugDraw(_debugDraw);
}
b2DebugDraw中有一些属性可以自定义,来改变debug模式的外观,这个看个人喜好就行啦。
flag可以有以下几种选择。如果你想复用的话,这样即可: b2DebugDraw.e_aabbBit | b2DebugDraw.e_jointBit
b2DebugDraw.e_aabbBit :表示显示刚体的边界盒
b2DebugDraw.e_jointBit :表示显示刚体相连时的节点和连线
b2DebugDraw.e_obbBit :显示凸多边形的边界,不显示圆的边界
b2DebugDraw.e_pairBit:draw broad-phase pairs
b2DebugDraw.e_coreShapeBit:draw core (TOI) shapes
b2DebugDraw.e_shapeBit:显示刚体的形状,不管是何种形状
b2DebugDraw.e_centerOfMassBit:显示重心
另外还需要在帧监听中添加语句
编译一下。
OK,大功告成~
附源码:
2 {
3 import Box2D.Collision.Shapes.b2PolygonShape;
4 import Box2D.Common.Math.b2Vec2;
5 import Box2D.Dynamics.b2Body;
6 import Box2D.Dynamics.b2BodyDef;
7 import Box2D.Dynamics.b2DebugDraw;
8 import Box2D.Dynamics.b2FixtureDef;
9 import Box2D.Dynamics.b2World;
10
11 import flash.display.Sprite;
12 import flash.events.Event;
13 import flash.geom.Point;
14
15 [SWF(frameRate=60,width=800,height=600)]
16 public class Box2dClass_1 extends Sprite
17 {
18 private const PIXELS_TO_METER:int = 30;
19 private const SWF_WIDTH:int = 800;
20 private const SWF_HEIGHT:int = 600;
21 private const SWF_HALF_WIDTH:int = SWF_WIDTH>>1;
22 private const SWF_HALF_HEIGHT:int = SWF_HEIGHT>>1;
23
24 private var _world:b2World;
25 private var _groundDef:b2BodyDef;
26 private var _groundBody:b2Body;
27 private var _groundBox:b2PolygonShape;
28 private var _groundFixtureDef:b2FixtureDef;
29 private var _debugSprite:Sprite;
30 private var _debugDraw:b2DebugDraw;
31
32 public function Box2dClass_1()
33 {
34 stage.color = 0x333333;
35 createWorld();
36 createGround();
38 createBox(new Point(SWF_HALF_WIDTH, 4), 10, 10, true, 1, 0.3);
39 createDebug();
40 stage.addEventListener(Event.ENTER_FRAME, update);
41 }
42
43 private function createWorld():void
44 {
45 _world = new b2World(new b2Vec2(0,10),true );
46 }
47
48 private function createDebug():void
49 {
50 _debugSprite = new Sprite();
51 addChild(_debugSprite);
52
53 _debugDraw = new b2DebugDraw();
54 _debugDraw.SetSprite(_debugSprite);
55 _debugDraw.SetDrawScale(PIXELS_TO_METER);
56 _debugDraw.SetLineThickness(1);
57 _debugDraw.SetAlpha(1);
58 _debugDraw.SetFillAlpha(0.4);
59 _debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
60 _world.SetDebugDraw(_debugDraw);
61 }
62
63 private function createGround():void
64 {
65 _groundDef = new b2BodyDef();
66 _groundDef.position.Set(SWF_HALF_WIDTH/PIXELS_TO_METER,(SWF_HEIGHT-20)/PIXELS_TO_METER);
67 _groundDef.type = b2Body.b2_staticBody;
68
69 _groundBody = _world.CreateBody(_groundDef);
70
71 _groundBox = new b2PolygonShape();
72 _groundBox.SetAsBox(SWF_HALF_WIDTH/PIXELS_TO_METER,20/PIXELS_TO_METER);
73
74 _groundFixtureDef = new b2FixtureDef();
75 _groundFixtureDef.shape = _groundBox;
76 _groundFixtureDef.density = 0;
77 _groundFixtureDef.friction = 0;
78 _groundFixtureDef.restitution = 0;
79
80 _groundBody.CreateFixture(_groundFixtureDef);
81 }
82
83 private function createBox(position:Point,hw:Number,hh:Number,isDynamic:Boolean,density:Number=0,friction:Number=0,restitution:Number=0):void
84 {
85 var bodyDef:b2BodyDef = new b2BodyDef();
86 if( isDynamic )
87 {
88 bodyDef.type = b2Body.b2_dynamicBody;
89 }
90 else
91 {
92 bodyDef.type = b2Body.b2_staticBody;
93 }
94 bodyDef.position.Set(position.x/PIXELS_TO_METER,position.y/PIXELS_TO_METER);
95
96 var body:b2Body = _world.CreateBody(bodyDef);
97
98 var shape:b2PolygonShape = new b2PolygonShape();
99 shape.SetAsBox(hw/PIXELS_TO_METER,hh/PIXELS_TO_METER);
100
101 var fixtureDef:b2FixtureDef = new b2FixtureDef();
102 fixtureDef.shape = shape;
103 fixtureDef.density = density;
104 fixtureDef.friction = friction;
105 fixtureDef.restitution = restitution;
106
107 body.CreateFixture(fixtureDef);
108
109 }
110
111 protected function update(event:Event):void
112 {
113 var timeStep:Number = 1 / 30;
114 var velocityIterations:int = 6;
115 var positionIterations:int = 2;
116
117 _world.Step(timeStep,velocityIterations,positionIterations);
118 _world.ClearForces();
119 _world.DrawDebugData();
120 }
121 }
122 }
另附上box2d结构图一枚: