Box2D教程1-创建碰撞世界
Box2D是一个比较优秀的二维物理引擎,由C++编写,后由Alchemy转换成AS3。这个教程主要介绍如何创建物理世界、刚体、及其基本形状,即Box2D的Hello world。
Box2D的物理世界通常包括这样几个对象:
world:一个物理世界,所有的刚体都将存在在这个世界里面,这个世界以米为距离单位。尽量贴近真实世界的度量。
body:刚体,存在在物理世界的理想物体,比任何物体都硬,不会发生形变。body对应着一个bodyDef(刚体定义),刚体定义指定了刚体的类型(动态、静态、轨迹运动的)和刚体的位置,world通过刚体定义创建刚体。
fixture:刚体修饰物,描述刚体的一些特征。fixture对应着fixtureDef(修饰物定义),它将形状绑定到刚体上,使刚体具有一些表现特征,如密度、摩擦系数、弹性等等。body通过fixtureDef创建fixture。
shape:一个几何形状,比如圆和多边形。形状是修饰物fixture的一个属性,描述了刚体的碰撞边界。
解释一下b2World, b2Body, b2BodyDef, b2Fixture, b2FixtureDef, shpae之间的关系
1.b2World通过b2BodyDef创建b2Body,没有b2BodyDef,b2Body不知道是什么类型,放在世界什么位置。
2.b2Body通过b2FixtureDef创建b2Fixture,没有b2Fixture,b2Body不知道是什么形状,摩擦、弹性、密度都不知道。shpae提供了碰撞检测的外边框。
下面分析片段代码:
public class Box2DTutorial extends Sprite
{
//屏幕像素单位转换成物理世界的距离单位
private const PIXEL_TO_METER:Number = 30;
//物理世界
private var world:Box2D.Dynamics.b2World;
public function Box2DTutorial()
{
drawBackground();
createWorld();
createWall();
createBall();
createDebugDraw();
addEventListener(Event.ENTER_FRAME, handleEnterFrame);
}
PIXEL_TO_METER实现像素到物理世界米的转换,通常情况下1米等于30像素,即物理世界移动1米,屏幕移动30像素。
这里我们自定义了是个方法createWorld(), createWall(), createBall(), createDebugDraw(), 分别创建世界,墙,球体,和调试绘制。
private function createWorld():void
{
//重力向量
var gravity:b2Vec2 = new b2Vec2(0,9.0);
//是否休眠
var doSleep:Boolean = true;
world = new b2World(gravity,doSleep);
world.SetWarmStarting(true);
}
创建一个世界,我们需要重力向量,我们可以通过(0,0)创建零重力环境,通过(-1,0)创建负重力环境。正常世界的重力为(0,9.8)。doSleep用来设置物体停止移动时是否休眠,节省运算量。SetWarmStarting false设置初始化时刚体不受重力影响,除非受到外力作用之后。
private function createWall():void
{
//1.需要创建的墙刚体
var leftWall:b2Body;
//2.刚体定义
var leftWallBodyDef:b2BodyDef = new b2BodyDef();
//刚体类型和位置
leftWallBodyDef.type = b2Body.b2_staticBody;
//注意刚体的注册中心都是在物体的中心位置
leftWallBodyDef.position.Set(10/PIXEL_TO_METER, stage.stageHeight/2/PIXEL_TO_METER);
//工厂模式创建刚体
leftWall = world.CreateBody(leftWallBodyDef);
//3.刚体修饰物定义
var leftWallFixtureDef:b2FixtureDef = new b2FixtureDef();
//密度
leftWallFixtureDef.density = 1.0;
//摩擦粗糙程度
leftWallFixtureDef.friction = 0.3;
//力度返回程度(弹性)
leftWallFixtureDef.restitution = 1.0;
//4.创建墙形状
var leftWallShape:b2PolygonShape = new b2PolygonShape();
//此处参数为宽和高度的一半值
leftWallShape.SetAsBox(10/PIXEL_TO_METER, stage.stageHeight/2/PIXEL_TO_METER);
//将形状添加到刚体修饰物
leftWallFixtureDef.shape = leftWallShape;
leftWall.CreateFixture(leftWallFixtureDef);
这里主要涉及到了b2Body, b2BodyDef, b2Fixture, b2FixtureDef, b2PolygonShape, 通过几个对象的关系创建一个刚体。
private function createDebugDraw():void
{
//创建一个sprite,可以将测试几何物体放入其中
var debugSprite:Sprite = new Sprite();
addChild(debugSprite);
var debugDraw:b2DebugDraw = new b2DebugDraw();
debugDraw.SetSprite(debugSprite);
//设置边框厚度
debugDraw.SetLineThickness(1.0);
//边框透明度
debugDraw.SetAlpha(1.0);
//填充透明度
debugDraw.SetFillAlpha(0.5);
//设置显示对象
debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
//物理世界缩放
debugDraw.SetDrawScale(PIXEL_TO_METER);
world.SetDebugDraw(debugDraw);
}
此处主要是绘制调试显示对象,没有它,所有的上面创建的都是物理世界的概念,抽象无形,不会以物体的形式显示出来。如果以后你为每个刚体设置了自定义外观,此处就不需要调试显示对象了。
最后handleEnterframe实现每帧更新计算。
完整代码如下:
1 package
2 {
3 import Box2D.Collision.Shapes.b2CircleShape;
4 import Box2D.Collision.Shapes.b2PolygonShape;
5 import Box2D.Common.Math.b2Vec2;
6 import Box2D.Dynamics.b2Body;
7 import Box2D.Dynamics.b2BodyDef;
8 import Box2D.Dynamics.b2DebugDraw;
9 import Box2D.Dynamics.b2FixtureDef;
10 import Box2D.Dynamics.b2World;
11
12 import flash.display.GradientType;
13 import flash.display.Sprite;
14 import flash.events.Event;
15 import flash.geom.Matrix;
16
17 [SWF(width="500",height="300",frameRate="30")]
18 public class Box2DTutorial extends Sprite
19 {
20 //屏幕像素单位转换成物理世界的距离单位
21 private const PIXEL_TO_METER:Number = 30;
22
23 //物理世界
24 private var world:Box2D.Dynamics.b2World;
25 public function Box2DTutorial()
26 {
27 drawBackground();
28 createWorld();
29 createWall();
30 createBall();
31 createDebugDraw();
32 addEventListener(Event.ENTER_FRAME, handleEnterFrame);
33 }
34
35 private function createWorld():void
36 {
37 //重力向量
38 var gravity:b2Vec2 = new b2Vec2(0,9.0);
39 //是否休眠
40 var doSleep:Boolean = true;
41 world = new b2World(gravity,doSleep);
42 //false时初始刚体不受重力影响,除非受力
43 world.SetWarmStarting(true);
44 }
45
46 private function createWall():void
47 {
48 //1.需要创建的墙刚体
49 var leftWall:b2Body;
50 //2.刚体定义
51 var leftWallBodyDef:b2BodyDef = new b2BodyDef();
52 //刚体类型和位置
53 leftWallBodyDef.type = b2Body.b2_staticBody;
54 //注意刚体的注册中心都是在物体的中心位置
55 leftWallBodyDef.position.Set(10/PIXEL_TO_METER, stage.stageHeight/2/PIXEL_TO_METER);
56 //工厂模式创建刚体
57 leftWall = world.CreateBody(leftWallBodyDef);
58
59 //3.刚体修饰物定义
60 var leftWallFixtureDef:b2FixtureDef = new b2FixtureDef();
61 //密度
62 leftWallFixtureDef.density = 1.0;
63 //摩擦粗糙程度
64 leftWallFixtureDef.friction = 0.3;
65 //力度返回程度(弹性)
66 leftWallFixtureDef.restitution = 1.0;
67
68 //4.创建墙形状
69 var leftWallShape:b2PolygonShape = new b2PolygonShape();
70 //此处参数为宽和高度的一半值
71 leftWallShape.SetAsBox(10/PIXEL_TO_METER, stage.stageHeight/2/PIXEL_TO_METER);
72
73 //将形状添加到刚体修饰物
74 leftWallFixtureDef.shape = leftWallShape;
75
76 leftWall.CreateFixture(leftWallFixtureDef);
77
78 //下面创建其他三面墙,共用leftwall的几个变量
79 leftWallBodyDef.position.Set((stage.stageWidth-10)/PIXEL_TO_METER, stage.stageHeight/2/PIXEL_TO_METER);
80 var rightWall:b2Body = world.CreateBody(leftWallBodyDef);
81 rightWall.CreateFixture(leftWallFixtureDef);
82
83 leftWallBodyDef.position.Set( stage.stageWidth/2/PIXEL_TO_METER, (stage.stageHeight-10)/PIXEL_TO_METER);
84 var bottomWall:b2Body = world.CreateBody(leftWallBodyDef);
85 leftWallShape.SetAsBox(stage.stageWidth/2/PIXEL_TO_METER, 10/PIXEL_TO_METER);
86 bottomWall.CreateFixture(leftWallFixtureDef);
87
88
89 leftWallBodyDef.position.Set( stage.stageWidth/2/PIXEL_TO_METER, 10/PIXEL_TO_METER);
90 var topWall:b2Body = world.CreateBody(leftWallBodyDef);
91 topWall.CreateFixture(leftWallFixtureDef);
92 }
93
94 private function createBall():void
95 {
96 for(var i:int = 0; i < 10; i++)
97 {
98 var ballDef:b2BodyDef = new b2BodyDef();
99 ballDef.type = b2Body.b2_dynamicBody;
100
101 var circleShape:b2CircleShape = new b2CircleShape((10+Math.random()*20)/PIXEL_TO_METER);
102 var ballFixtureDef:b2FixtureDef = new b2FixtureDef();
103 ballFixtureDef.shape = circleShape;
104 ballFixtureDef.density = 1.0;
105 ballFixtureDef.restitution = 1.0;
106 ballDef.position.Set(stage.stageWidth/2/PIXEL_TO_METER,20/PIXEL_TO_METER);
107 var ball:b2Body = world.CreateBody(ballDef);
108 ball.CreateFixture(ballFixtureDef);
109
110 }
111 }
112
113 private function createDebugDraw():void
114 {
115 //创建一个sprite,可以将测试几何物体放入其中
116 var debugSprite:Sprite = new Sprite();
117 addChild(debugSprite);
118 var debugDraw:b2DebugDraw = new b2DebugDraw();
119 debugDraw.SetSprite(debugSprite);
120 //设置边框厚度
121 debugDraw.SetLineThickness(1.0);
122 //边框透明度
123 debugDraw.SetAlpha(1.0);
124 //填充透明度
125 debugDraw.SetFillAlpha(0.5);
126 //设置显示对象
127 debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
128 //物理世界缩放
129 debugDraw.SetDrawScale(PIXEL_TO_METER);
130 world.SetDebugDraw(debugDraw);
131 }
132
133 private function handleEnterFrame(evt:Event):void
134 {
135 var timeStep:Number = 1/30;
136 var velocityInterations:int = 10;
137 var positionIterations:int = 10;
138
139 world.Step(timeStep,velocityInterations,positionIterations);
140 //在2.1版本清除力,以提高效率
141 world.ClearForces();
142 //绘制
143 world.DrawDebugData();
144 }
145
146 private function drawBackground():void
147 {
148 var bg:Sprite = new Sprite();
149 var matrix:Matrix = new Matrix();
150 matrix.translate(100,100);
151 bg.graphics.beginGradientFill(GradientType.RADIAL,[0xffffff,0xffaa00],[0.3,0.2],[0,255],matrix);
152 bg.graphics.drawRect(0,0,stage.stageWidth,stage.stage.stageHeight);
153 bg.graphics.endFill();
154 addChild(bg);
155 }
156 }
157 }