Fork me on GitHub

Box2dの学习超级积木小游戏制作

挺早之前看到过的小游戏,超级积木2,在开心网、QQ校友、空间传得挺多的,闲来没事就试着把它做出来,感觉还挺简单的,就仅仅使用了Box2D引擎中的创建刚体而已

 

点击这里下载源码


游戏规则就是把所有的积木摆放在舞台上并保持10S不倒下就算过关了

要制作一个Box2D游戏,最重要的还是了解它的运作机制

1package 
2{
3 import Box2D.Collision.b2AABB;
4 import Box2D.Collision.Shapes.b2PolygonDef;
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.b2World;
10 import flash.display.Sprite;
11 import flash.events.Event;
12 import flash.events.KeyboardEvent;
13 import flash.events.MouseEvent;
14 /**
15 * ...
16 * @description HelloWorld
17 * @author ispooky
18 * @date 2010.11.15
19 *
20 * @see
http://ispooky.cnblogs.com
21
*/
22
23 public class HelloWorldApp extends Sprite
24 {
25 //缩放比例
26 public static const RATIO :int = 30;
27 //时间步
28 public static const ITERATIONS :int = 30;
29 //迭代次数
30 public static const TIME_STEP :Number = 1 / 30;
31
32 //物理空间
33 private static var world :b2World;
34 //Debug绘制容器
35 private static var spriteToDrawOn :Sprite;
36
37 public function HelloWorldApp()
38 {
39 if (stage) init();
40 else addEventListener(Event.ADDED_TO_STAGE, init);
41 }
42
43 private function init(e:Event = null):void
44 {
45 removeEventListener(Event.ADDED_TO_STAGE, init);
46
47 initB2D();
48 stage.addEventListener(MouseEvent.CLICK, onClickHandler);
49 stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownHandler);
50 }
51
52 private function onKeyDownHandler(e:KeyboardEvent):void
53 {
54 if (e.keyCode == 82/*R*/)
55 {
56 /////////////////////////////
57 // 销毁Body
58 /////////////////////////////
59 for (var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next)
60 {
61 if (!bb.IsStatic()) { world.DestroyBody(bb) };
62 }
63 }
64 }
65
66 private function onClickHandler(e:MouseEvent):void
67 {
68 if (world.GetBodyCount() > 30) return;
69 createBoxs();
70 }
71
72 private function initB2D():void
73 {
74 addEventListener(Event.ENTER_FRAME, onLoopHandler, false, 0, true);
75 createWorld();
76 showDebug();
77 createGrounds();
78 createBoxs();
79 }
80
81 private function createBoxs():void
82 {
83 var bodyDef:b2BodyDef;
84 var boxDef:b2PolygonDef;
85 var body:b2Body;
86
87 bodyDef = new b2BodyDef();
88 bodyDef.position.Set(((int(Math.random() * 200) - 100) + 275.0) / RATIO, -100.0 / RATIO);
89 boxDef = new b2PolygonDef();
90 boxDef.SetAsBox(int((Math.random() * 20) + 20) / RATIO / 2, (int(Math.random() * 20) + 20) / RATIO / 2);
91 boxDef.density = 1;
92 boxDef.friction = 0.4;
93 boxDef.restitution = 0.1;
94
95 body = world.CreateBody(bodyDef);
96 body.CreateShape(boxDef);
97 body.SetMassFromShapes();
98 }
99
100 private function createGrounds():void
101 {
102 var bodyDef:b2BodyDef;
103 var boxDef:b2PolygonDef;
104 var body:b2Body;
105
106 //刚体定义
107 bodyDef = new b2BodyDef();
108 //设置位置
109 bodyDef.position.Set(275.0 / RATIO, 390.0 / RATIO);
110 //形状定义
111 boxDef = new b2PolygonDef();
112 //设置高宽
113 boxDef.SetAsBox(550.0 / RATIO / 2, 20.0 / RATIO / 2);
114 //密度,0为静态物
115 boxDef.density = 0;
116 //摩擦力
117 boxDef.friction = 0.3;
118 //弹力
119 boxDef.restitution = 0.2;
120
121 body = world.CreateBody(bodyDef);
122 body.CreateShape(boxDef);
123 //计算质量,因此处为静态物,所以不需计算
124 //body.SetMassFromShapes();
125 }
126
127 private function showDebug():void
128 {
129 spriteToDrawOn = new Sprite();
130 addChild(spriteToDrawOn);
131 var dbgDraw:b2DebugDraw = new b2DebugDraw();
132 //绘制图形
133 dbgDraw.m_sprite = spriteToDrawOn;
134 //缩放比例
135 dbgDraw.m_drawScale = RATIO;
136 //填充线的颜色透明度
137 dbgDraw.m_alpha = 1.0;
138 //填充区域的颜色透明度
139 dbgDraw.m_fillAlpha = 0.5;
140 //线型粗细
141 dbgDraw.m_lineThickness = 1.0;
142 //绘制图形位
143 dbgDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;
144
145 world.SetDebugDraw(dbgDraw);
146 }
147
148 private function createWorld():void
149 {
150 //边界
151 var worldAABB:b2AABB = new b2AABB();
152 worldAABB.lowerBound.Set( -3000 / RATIO, -3000 / RATIO);
153 worldAABB.upperBound.Set(3000 / RATIO, 3000 / RATIO);
154 //重力
155 var gravity:b2Vec2 = new b2Vec2(0, 10);
156 //允许休眠
157 var doSleep:Boolean = true;
158
159 world = new b2World(worldAABB, gravity, doSleep);
160 }
161
162 private function onLoopHandler(e:Event):void
163 {
164 if (world == null) return;
165 //刷新物理空间
166 world.Step(TIME_STEP, ITERATIONS);
167 }
168
169 }
170
171}

代码运行结果截图

 代码运行结果

一般来讲,Box2D 的运作机制就是创建一个物理空间,然后往里边加刚体等东西,最后就是刷新模拟物理空间了。

具体为:

 

创建一个物理空间,区域b2AABB框,重力g值,是否允许休眠的布尔值

创建静态地面刚体:

刚体定义,设置位置,角度,阻尼,贴图等属性

形状定义,高宽,密度,摩擦力,弹力等属性

创建刚体

创建形状

计算质量

……

创建其它刚体,关节

……

刷新模拟物理空间

 

注:

Box2D的单位是米、千克和秒(MKS)。而Flash使用的是像素单位,缩放比例一般设置为1米=30像素,虽然是可以自己任意定义的。

刚体默认原点是在中心,贴图则在左上角

SetAsBox函数接收的是半个宽度和半个高度

静态物体的密度为0

贴图是添加到舞台上的,而不是物理空间

Box2D的角度使用的是弧度制

 

 

接下来所要实现的就是在特定位置生成特定的刚体

1 package 
2 {
3 import Box2D.Collision.b2AABB;
4 import Box2D.Collision.Shapes.b2CircleDef;
5 import Box2D.Collision.Shapes.b2PolygonDef;
6 import Box2D.Common.Math.b2Vec2;
7 import Box2D.Dynamics.b2Body;
8 import Box2D.Dynamics.b2BodyDef;
9 import Box2D.Dynamics.b2DebugDraw;
10 import Box2D.Dynamics.b2World;
11 import flash.display.Sprite;
12 import flash.events.Event;
13 import flash.events.KeyboardEvent;
14 import flash.events.MouseEvent;
15 import flash.system.IME;
16 /**
17 * ...
18 * @description superStacker1
19 * @author ispooky
20 * @date 2010.11.15
21 *
22 * @see
http://ispooky.cnblogs.com
23
*/
24 public class superStacker1 extends Sprite
25 {
26 //缩放比例
27 public static const RATIO :int = 30;
28 //时间步
29 public static const ITERATIONS :int = 30;
30 //迭代次数
31 public static const TIME_STEP :Number = 1 / 30;
32
33 //物理空间
34 private static var world :b2World;
35 //Debug绘制容器
36 private static var spriteToDrawOn :Sprite;
37
38 //提示MC
39 private var tip_mc:Sprite;
40 //刚体类型 1 : 圆形,2 : 三角形, 3 : 矩形
41 private var now_type:int = 1;
42 private var now_width:Number = 40;
43 private var now_height:Number = 40;
44
45 public function superStacker1()
46 {
47 if (stage) init();
48 else addEventListener(Event.ADDED_TO_STAGE, init);
49 }
50
51 private function init(e:Event = null):void
52 {
53 removeEventListener(Event.ADDED_TO_STAGE, init);
54 IME.enabled = false;
55
56 initB2D();
57
58 tip_mc = new Sprite();
59 addChild(tip_mc);
60 stage.addEventListener(MouseEvent.MOUSE_DOWN, onDownHandler);
61 stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownHandler);
62 }
63
64 private function onKeyDownHandler(e:KeyboardEvent):void
65 {
66 if (e.keyCode == 82/*R*/)
67 {
68 for (var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next)
69 {
70 if (!bb.IsStatic()) { world.DestroyBody(bb) };
71 }
72 }
73 }
74
75 private function onDownHandler(e:MouseEvent):void
76 {
77 now_type = int(Math.random() * 3) + 1;
78 tip_mc.graphics.clear();
79 tip_mc.graphics.beginFill(0x900000, 0.6);
80 switch(now_type)
81 {
82 //圆形
83 case 1:
84 tip_mc.graphics.drawCircle(0, 0, now_width * 0.5);
85 break;
86 //三角形
87 case 2:
88 tip_mc.graphics.moveTo( 0, now_height * 0.5);
89 tip_mc.graphics.lineTo(now_width * 0.5, -now_height * 0.5);
90 tip_mc.graphics.lineTo( -now_width * 0.5, -now_height * 0.5);
91 tip_mc.rotation = 180;
92 break;
93 //矩形
94 case 3:
95 tip_mc.graphics.drawRect( -now_width * 0.5, -now_height * 0.5, now_width, now_height);
96 break;
97 }
98 tip_mc.graphics.endFill();
99 tip_mc.x = mouseX;
100 tip_mc.y = mouseY;
101 stage.addEventListener(MouseEvent.MOUSE_MOVE, onMoveHandler);
102 stage.addEventListener(MouseEvent.MOUSE_UP, onUpHandler);
103 }
104
105 private function onUpHandler(e:MouseEvent):void
106 {
107 stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMoveHandler);
108 stage.removeEventListener(MouseEvent.MOUSE_UP, onUpHandler);
109 tip_mc.graphics.clear();
110 createBox(now_type);
111 }
112
113 private function onMoveHandler(e:MouseEvent):void
114 {
115 tip_mc.x = mouseX;
116 tip_mc.y = mouseY;
117 e.updateAfterEvent();
118 }
119
120 private function initB2D():void
121 {
122 addEventListener(Event.ENTER_FRAME, onLoopHandler, false, 0, true);
123 createWorld();
124 showDebug();
125 createGrounds();
126 }
127
128 private function createBox(value:int):void
129 {
130 var bodyDef:b2BodyDef;
131 var boxDef:b2PolygonDef;
132 var circleDef:b2CircleDef;
133 var body:b2Body;
134
135 bodyDef = new b2BodyDef();
136 bodyDef.position.Set(mouseX / RATIO, mouseY / RATIO);
137 switch(value)
138 {
139 case 1:
140 circleDef = new b2CircleDef();
141 circleDef.radius = now_width * 0.5 / RATIO;
142 circleDef.density = 1;
143 circleDef.friction = 0.1;
144 circleDef.restitution = 0.3;
145 body = world.CreateBody(bodyDef);
146 body.CreateShape(circleDef);
147 break;
148 case 2:
149 bodyDef.angle = 180 * (Math.PI / 180);
150 boxDef = new b2PolygonDef();
151 boxDef.vertexCount = 3;
152 boxDef.vertices[0].Set(now_width * 0.5 / RATIO, -now_height * 0.5 / RATIO);
153 boxDef.vertices[1].Set(0, now_height * 0.5 / RATIO);
154 boxDef.vertices[2].Set( -now_width * 0.5 / RATIO, -now_height * 0.5 / RATIO);
155 boxDef.density = 1;
156 boxDef.friction = 0.3;
157 boxDef.restitution = 0.2;
158 body = world.CreateBody(bodyDef);
159 body.CreateShape(boxDef);
160 break;
161 case 3:
162 boxDef = new b2PolygonDef();
163 boxDef.SetAsBox(now_width / RATIO / 2, now_height / RATIO / 2);
164 boxDef.density = 1;
165 boxDef.friction = 0.4;
166 boxDef.restitution = 0.1;
167 body = world.CreateBody(bodyDef);
168 body.CreateShape(boxDef);
169 break;
170 }
171 body.SetMassFromShapes();
172 }
173
174 private function createGrounds():void
175 {
176 var bodyDef:b2BodyDef;
177 var boxDef:b2PolygonDef;
178 var body:b2Body;
179
180 //刚体定义
181 bodyDef = new b2BodyDef();
182 //设置位置
183 bodyDef.position.Set(275.0 / RATIO, 250.0 / RATIO);
184 //形状定义
185 boxDef = new b2PolygonDef();
186 //设置高宽
187 boxDef.SetAsBox(250.0 / RATIO / 2, 20.0 / RATIO / 2);
188 //密度,0为静态物
189 boxDef.density = 0;
190 //摩擦力
191 boxDef.friction = 0.3;
192 //弹力
193 boxDef.restitution = 0.2;
194
195 body = world.CreateBody(bodyDef);
196 body.CreateShape(boxDef);
197 //计算质量,因此处为静态物,所以不需计算
198 //body.SetMassFromShapes();
199 }
200
201 private function showDebug():void
202 {
203 spriteToDrawOn = new Sprite();
204 addChild(spriteToDrawOn);
205 var dbgDraw:b2DebugDraw = new b2DebugDraw();
206 //绘制图形
207 dbgDraw.m_sprite = spriteToDrawOn;
208 //缩放比例
209 dbgDraw.m_drawScale = RATIO;
210 //填充线的颜色透明度
211 dbgDraw.m_alpha = 1.0;
212 //填充区域的颜色透明度
213 dbgDraw.m_fillAlpha = 0.5;
214 //线型粗细
215 dbgDraw.m_lineThickness = 1.0;
216 //绘制图形位
217 dbgDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;
218
219 world.SetDebugDraw(dbgDraw);
220 }
221
222 private function createWorld():void
223 {
224 //边界
225 var worldAABB:b2AABB = new b2AABB();
226 worldAABB.lowerBound.Set( -3000 / RATIO, -3000 / RATIO);
227 worldAABB.upperBound.Set(3000 / RATIO, 3000 / RATIO);
228 //重力
229 var gravity:b2Vec2 = new b2Vec2(0, 10);
230 //允许休眠
231 var doSleep:Boolean = true;
232
233 world = new b2World(worldAABB, gravity, doSleep);
234 }
235
236 private function onLoopHandler(e:Event):void
237 {
238 if (world == null) return;
239 //刷新物理空间
240 world.Step(TIME_STEP, ITERATIONS);
241 }
242
243 }
244
245 }

代码运行结果

在鼠标点击垂直位置出现多边形。

相比较HelloWorld而言,就是在生成刚体的时候设置了刚体定义的位置由鼠标位置确定,放在鼠标弹起事件时操作,同时刚体有了圆形、矩形、三角形三种,还有一个提示图形,没了

 

判断物体是否出界是该游戏是否结束的依据,在Event.Enter_Frame事件中添加以下代码就可以实现了,坐标判断

1 for (var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next)
2 {
3 if (bb.GetPosition().y * RATIO > stage.stageHeight)
4 {
5 tip_txt.text = '提示框:有物体离开舞台' + int(Math.random() * 100000);
6 world.DestroyBody(bb);
7 }
8 }

任何时候都应该把没用的Body销毁掉

 

代码运行结果

再接着,添加滚动提示框跟游戏判断

1 package 
2 {
3 import Box2D.Collision.b2AABB;
4 import Box2D.Collision.Shapes.b2CircleDef;
5 import Box2D.Collision.Shapes.b2PolygonDef;
6 import Box2D.Common.Math.b2Vec2;
7 import Box2D.Dynamics.b2Body;
8 import Box2D.Dynamics.b2BodyDef;
9 import Box2D.Dynamics.b2DebugDraw;
10 import Box2D.Dynamics.b2World;
11 import flash.display.Shape;
12 import flash.display.Sprite;
13 import flash.events.Event;
14 import flash.events.KeyboardEvent;
15 import flash.events.MouseEvent;
16 import flash.system.IME;
17 import flash.text.TextField;
18 /**
19 * ...
20 * @description superStacker3
21 * @author ispooky
22 * @date 2010.11.15
23 *
24 * @see
http://ispooky.cnblogs.com
25
*/
26 public class superStacker3 extends Sprite
27 {
28 //缩放比例
29 public static const RATIO :int = 30;
30 //时间步
31 public static const ITERATIONS :int = 30;
32 //迭代次数
33 public static const TIME_STEP :Number = 1 / 30;
34
35 //物理空间
36 private static var world :b2World;
37 //Debug绘制容器
38 private static var spriteToDrawOn :Sprite;
39
40 //提示MC
41 private var tip_mc:Sprite;
42 //刚体类型 1 : 圆形,2 : 三角形, 3 : 矩形
43 private var now_type:int = 1;
44 private var now_width:Number = 40;
45 private var now_height:Number = 40;
46
47 //剩余物
48 private var remainder_arr:Array;
49 private var remainder_mc:Sprite;
50
51 public var tip_txt:TextField;
52 public function superStacker3()
53 {
54 if (stage) init();
55 else addEventListener(Event.ADDED_TO_STAGE, init);
56 }
57
58 private function init(e:Event = null):void
59 {
60 removeEventListener(Event.ADDED_TO_STAGE, init);
61 IME.enabled = false;
62
63 initB2D();
64 getRemainder();
65 tip_mc = new Sprite();
66 addChild(tip_mc);
67 stage.addEventListener(MouseEvent.MOUSE_DOWN, onDownHandler);
68 stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownHandler);
69 }
70
71 private function getRemainder():void
72 {
73 remainder_arr = getData();
74 if (remainder_mc != null) { removeChild(remainder_mc) };
75 remainder_mc = getScrollTip(remainder_arr);
76 remainder_mc.x = 30;
77 remainder_mc.y = 30;
78 addChild(remainder_mc);
79 }
80
81 private function onKeyDownHandler(e:KeyboardEvent):void
82 {
83 if (e.keyCode == 82/*R*/)
84 {
85 for (var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next)
86 {
87 if (!bb.IsStatic()) { world.DestroyBody(bb) };
88 }
89 getRemainder();
90 tip_txt.text = '提示框:';
91 }
92 }
93
94 private function onDownHandler(e:MouseEvent):void
95 {
96 if (remainder_arr.length <= 0)
97 {
98 tip_txt.text = '所有物体都摆放了,成功过关';
99 return;
100 }
101 now_type = remainder_arr[0].type;
102 tip_mc.graphics.clear();
103 tip_mc.graphics.beginFill(0x900000, 0.6);
104 switch(now_type)
105 {
106 //圆形
107 case 1:
108 tip_mc.graphics.drawCircle(0, 0, remainder_arr[0].width * 0.5);
109 break;
110 //三角形
111 case 2:
112 tip_mc.graphics.moveTo( 0, remainder_arr[0].height * 0.5);
113 tip_mc.graphics.lineTo(remainder_arr[0].width * 0.5, -remainder_arr[0].height * 0.5);
114 tip_mc.graphics.lineTo( -remainder_arr[0].width * 0.5, -remainder_arr[0].height * 0.5);
115 tip_mc.rotation = 180;
116 break;
117 //矩形
118 case 3:
119 tip_mc.graphics.drawRect( -now_width * 0.5, -now_height * 0.5, now_width, now_height);
120 break;
121 }
122 tip_mc.graphics.endFill();
123 tip_mc.x = mouseX;
124 tip_mc.y = mouseY;
125 stage.addEventListener(MouseEvent.MOUSE_MOVE, onMoveHandler);
126 stage.addEventListener(MouseEvent.MOUSE_UP, onUpHandler);
127 }
128
129 private function onUpHandler(e:MouseEvent):void
130 {
131 stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMoveHandler);
132 stage.removeEventListener(MouseEvent.MOUSE_UP, onUpHandler);
133 tip_mc.graphics.clear();
134 createBox(now_type);
135
136 remainder_arr.shift();
137 removeChild(remainder_mc);
138 remainder_mc = getScrollTip(remainder_arr);
139 remainder_mc.x = 30;
140 remainder_mc.y = 30;
141 addChild(remainder_mc);
142 }
143
144 private function onMoveHandler(e:MouseEvent):void
145 {
146 tip_mc.x = mouseX;
147 tip_mc.y = mouseY;
148 e.updateAfterEvent();
149 }
150
151 private function initB2D():void
152 {
153 addEventListener(Event.ENTER_FRAME, onLoopHandler, false, 0, true);
154 createWorld();
155 showDebug();
156 createGrounds();
157 }
158
159 private function createBox(value:int):void
160 {
161 var bodyDef:b2BodyDef;
162 var boxDef:b2PolygonDef;
163 var circleDef:b2CircleDef;
164 var body:b2Body;
165
166 bodyDef = new b2BodyDef();
167 bodyDef.position.Set(mouseX / RATIO, mouseY / RATIO);
168 switch(value)
169 {
170 case 1:
171 circleDef = new b2CircleDef();
172 circleDef.radius = now_width * 0.5 / RATIO;
173 circleDef.density = 1;
174 circleDef.friction = 0.1;
175 circleDef.restitution = 0.3;
176 body = world.CreateBody(bodyDef);
177 body.CreateShape(circleDef);
178 break;
179 case 2:
180 bodyDef.angle = 180 * (Math.PI / 180);
181 boxDef = new b2PolygonDef();
182 boxDef.vertexCount = 3;
183 boxDef.vertices[0].Set(now_width * 0.5 / RATIO, -now_height * 0.5 / RATIO);
184 boxDef.vertices[1].Set(0, now_height * 0.5 / RATIO);
185 boxDef.vertices[2].Set( -now_width * 0.5 / RATIO, -now_height * 0.5 / RATIO);
186 boxDef.density = 1;
187 boxDef.friction = 0.3;
188 boxDef.restitution = 0.2;
189 body = world.CreateBody(bodyDef);
190 body.CreateShape(boxDef);
191 break;
192 case 3:
193 boxDef = new b2PolygonDef();
194 boxDef.SetAsBox(now_width / RATIO / 2, now_height / RATIO / 2);
195 boxDef.density = 1;
196 boxDef.friction = 0.4;
197 boxDef.restitution = 0.1;
198 body = world.CreateBody(bodyDef);
199 body.CreateShape(boxDef);
200 break;
201 }
202 body.SetMassFromShapes();
203 }
204
205 private function createGrounds():void
206 {
207 var bodyDef:b2BodyDef;
208 var boxDef:b2PolygonDef;
209 var body:b2Body;
210
211 //刚体定义
212 bodyDef = new b2BodyDef();
213 //设置位置
214 bodyDef.position.Set(275.0 / RATIO, 250.0 / RATIO);
215 //形状定义
216 boxDef = new b2PolygonDef();
217 //设置高宽
218 boxDef.SetAsBox(250.0 / RATIO / 2, 20.0 / RATIO / 2);
219 //密度,0为静态物
220 boxDef.density = 0;
221 //摩擦力
222 boxDef.friction = 0.3;
223 //弹力
224 boxDef.restitution = 0.2;
225
226 body = world.CreateBody(bodyDef);
227 body.CreateShape(boxDef);
228 //计算质量,因此处为静态物,所以不需计算
229 //body.SetMassFromShapes();
230 }
231
232 private function showDebug():void
233 {
234 spriteToDrawOn = new Sprite();
235 addChild(spriteToDrawOn);
236 var dbgDraw:b2DebugDraw = new b2DebugDraw();
237 //绘制图形
238 dbgDraw.m_sprite = spriteToDrawOn;
239 //缩放比例
240 dbgDraw.m_drawScale = RATIO;
241 //填充线的颜色透明度
242 dbgDraw.m_alpha = 1.0;
243 //填充区域的颜色透明度
244 dbgDraw.m_fillAlpha = 0.5;
245 //线型粗细
246 dbgDraw.m_lineThickness = 1.0;
247 //绘制图形位
248 dbgDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;
249
250 world.SetDebugDraw(dbgDraw);
251 }
252
253 private function createWorld():void
254 {
255 //边界
256 var worldAABB:b2AABB = new b2AABB();
257 worldAABB.lowerBound.Set( -3000 / RATIO, -3000 / RATIO);
258 worldAABB.upperBound.Set(3000 / RATIO, 3000 / RATIO);
259 //重力
260 var gravity:b2Vec2 = new b2Vec2(0, 10);
261 //允许休眠
262 var doSleep:Boolean = true;
263
264 world = new b2World(worldAABB, gravity, doSleep);
265 }
266
267 private function onLoopHandler(e:Event):void
268 {
269 if (world == null) return;
270 //刷新物理空间
271 world.Step(TIME_STEP, ITERATIONS);
272
273 for (var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next)
274 {
275 if (bb.GetPosition().y * RATIO > stage.stageHeight)
276 {
277 tip_txt.text = '有物体掉落,游戏失败,请按R键重置游戏';
278 world.DestroyBody(bb);
279 }
280 }
281 }
282
283 //获取剩余刚体的滚动提示框
284 public function getScrollTip(arr:Array):Sprite
285 {
286 var sp:Sprite = new Sprite();
287 for (var i:int = 0; i < arr.length; i++)
288 {
289 var isp:Shape = new Shape();
290 isp.graphics.beginFill(0x3399CC, 0.4);
291 switch(arr[i].type)
292 {
293 case 1:
294 isp.graphics.drawCircle(0, 0, arr[i].width * 0.5);
295 break;
296 case 2:
297 isp.graphics.moveTo( 0, arr[i].height * 0.5);
298 isp.graphics.lineTo(arr[i].width * 0.5, -arr[i].height * 0.5);
299 isp.graphics.lineTo( -arr[i].width * 0.5, -arr[i].height * 0.5);
300 isp.rotation = 180;
301 break;
302 case 3:
303 isp.graphics.drawRect( -arr[i].width * 0.5, -arr[i].height * 0.5, arr[i].width, arr[i].height);
304 break;
305 }
306 isp.graphics.endFill();
307 isp.x = i * 50;
308 sp.addChild(isp);
309 }
310
311 return sp;
312 }
313
314 //获取剩余刚体的数据
315 public function getData():Array
316 {
317 var arr:Array = [];
318 for (var i:int = 0; i < 10; i++)
319 {
320 var obj:Object = new Object();
321 if (i < 4)
322 {
323 obj.type = 3;
324 }else if (i > 6)
325 {
326 obj.type = 2;
327 }else
328 {
329 obj.type = 1;
330 }
331
332 obj.width = 40;
333 obj.height = 40;
334 arr.push(obj);
335 }
336 return arr;
337 }
338 }
339
340 }

这部分跟Box2D基本没关系,就此省了

 

代码运行结果


游戏大概就这样子了,接下来要处理的就是给刚体贴图,注意因为有贴图,所以在销毁刚体时记得removeChild并设置其为null

1 package 
2 {
3 import Box2D.Collision.b2AABB;
4 import Box2D.Collision.Shapes.b2CircleDef;
5 import Box2D.Collision.Shapes.b2PolygonDef;
6 import Box2D.Common.Math.b2Vec2;
7 import Box2D.Dynamics.b2Body;
8 import Box2D.Dynamics.b2BodyDef;
9 import Box2D.Dynamics.b2DebugDraw;
10 import Box2D.Dynamics.b2World;
11 import flash.display.MovieClip;
12 import flash.display.Shape;
13 import flash.display.Sprite;
14 import flash.events.Event;
15 import flash.events.KeyboardEvent;
16 import flash.events.MouseEvent;
17 import flash.system.IME;
18 import flash.text.TextField;
19 /**
20 * ...
21 * @description superStacker4
22 * @author ispooky
23 * @date 2010.11.15
24 *
25 * @see
http://ispooky.cnblogs.com
26
*/
27 public class superStacker4 extends Sprite
28 {
29 //缩放比例
30 public static const RATIO :int = 30;
31 //时间步
32 public static const ITERATIONS :int = 30;
33 //迭代次数
34 public static const TIME_STEP :Number = 1 / 30;
35
36 //物理空间
37 private static var world :b2World;
38 //Debug绘制容器
39 private static var spriteToDrawOn :Sprite;
40
41 //提示MC
42 private var tip_mc:Sprite;
43 //刚体类型 1 : 圆形,2 : 三角形, 3 : 矩形
44 private var now_type:int = 1;
45 private var now_width:Number = 40;
46 private var now_height:Number = 40;
47
48 //剩余物
49 private var remainder_arr:Array;
50 private var remainder_mc:Sprite;
51
52 public var tip_txt:TextField;
53 public function superStacker4()
54 {
55 if (stage) init();
56 else addEventListener(Event.ADDED_TO_STAGE, init);
57 }
58
59 private function init(e:Event = null):void
60 {
61 removeEventListener(Event.ADDED_TO_STAGE, init);
62 IME.enabled = false;
63
64 initB2D();
65 getRemainder();
66 tip_mc = new Sprite();
67 addChild(tip_mc);
68 stage.addEventListener(MouseEvent.MOUSE_DOWN, onDownHandler);
69 stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownHandler);
70 }
71
72 private function getRemainder():void
73 {
74 remainder_arr = getData();
75 if (remainder_mc != null) { removeChild(remainder_mc) };
76 remainder_mc = getScrollTip(remainder_arr);
77 remainder_mc.x = 30;
78 remainder_mc.y = 30;
79 addChild(remainder_mc);
80 }
81
82 private function onKeyDownHandler(e:KeyboardEvent):void
83 {
84 if (e.keyCode == 82/*R*/)
85 {
86 for (var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next)
87 {
88 if (bb.m_userData is MovieClip)
89 {
90 removeChild(bb.m_userData);
91 bb.m_userData = null;
92 }
93 if (!bb.IsStatic()) { world.DestroyBody(bb) };
94 }
95 getRemainder();
96 tip_txt.text = '提示框:';
97 }
98 }
99
100 private function onDownHandler(e:MouseEvent):void
101 {
102 if (remainder_arr.length <= 0)
103 {
104 tip_txt.text = '所有物体都摆放了,成功过关';
105 return;
106 }
107 now_type = remainder_arr[0].type;
108 tip_mc.graphics.clear();
109 tip_mc.graphics.beginFill(0x900000, 0.6);
110 switch(now_type)
111 {
112 //圆形
113 case 1:
114 tip_mc.graphics.drawCircle(0, 0, remainder_arr[0].width * 0.5);
115 break;
116 //三角形
117 case 2:
118 tip_mc.graphics.moveTo( 0, remainder_arr[0].height * 0.5);
119 tip_mc.graphics.lineTo(remainder_arr[0].width * 0.5, -remainder_arr[0].height * 0.5);
120 tip_mc.graphics.lineTo( -remainder_arr[0].width * 0.5, -remainder_arr[0].height * 0.5);
121 tip_mc.rotation = 180;
122 break;
123 //矩形
124 case 3:
125 tip_mc.graphics.drawRect( -now_width * 0.5, -now_height * 0.5, now_width, now_height);
126 break;
127 }
128 tip_mc.graphics.endFill();
129 tip_mc.x = mouseX;
130 tip_mc.y = mouseY;
131 stage.addEventListener(MouseEvent.MOUSE_MOVE, onMoveHandler);
132 stage.addEventListener(MouseEvent.MOUSE_UP, onUpHandler);
133 }
134
135 private function onUpHandler(e:MouseEvent):void
136 {
137 stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMoveHandler);
138 stage.removeEventListener(MouseEvent.MOUSE_UP, onUpHandler);
139 tip_mc.graphics.clear();
140 createBox(now_type);
141
142 remainder_arr.shift();
143 removeChild(remainder_mc);
144 remainder_mc = getScrollTip(remainder_arr);
145 remainder_mc.x = 30;
146 remainder_mc.y = 30;
147 addChild(remainder_mc);
148 }
149
150 private function onMoveHandler(e:MouseEvent):void
151 {
152 tip_mc.x = mouseX;
153 tip_mc.y = mouseY;
154 e.updateAfterEvent();
155 }
156
157 private function initB2D():void
158 {
159 addEventListener(Event.ENTER_FRAME, onLoopHandler, false, 0, true);
160 createWorld();
161 showDebug();
162 createGrounds();
163 }
164
165 private function createBox(value:int):void
166 {
167 var bodyDef:b2BodyDef;
168 var boxDef:b2PolygonDef;
169 var circleDef:b2CircleDef;
170 var body:b2Body;
171
172 bodyDef = new b2BodyDef();
173 bodyDef.position.Set(mouseX / RATIO, mouseY / RATIO);
174 switch(value)
175 {
176 case 1:
177 circleDef = new b2CircleDef();
178 circleDef.radius = now_width * 0.5 / RATIO;
179 circleDef.density = 1;
180 circleDef.friction = 0.1;
181 circleDef.restitution = 0.3;
182 bodyDef.userData = new CircleSkin();
183 bodyDef.userData.width = now_width;
184 bodyDef.userData.height = now_width;
185 addChild(bodyDef.userData);
186 body = world.CreateBody(bodyDef);
187 body.CreateShape(circleDef);
188 break;
189 case 2:
190 bodyDef.angle = 180 * (Math.PI / 180);
191 boxDef = new b2PolygonDef();
192 boxDef.vertexCount = 3;
193 boxDef.vertices[0].Set(now_width * 0.5 / RATIO, -now_height * 0.5 / RATIO);
194 boxDef.vertices[1].Set(0, now_height * 0.5 / RATIO);
195 boxDef.vertices[2].Set( -now_width * 0.5 / RATIO, -now_height * 0.5 / RATIO);
196 boxDef.density = 1;
197 boxDef.friction = 0.3;
198 boxDef.restitution = 0.2;
199 bodyDef.userData = new TriSkin();
200 bodyDef.userData.width = now_width;
201 bodyDef.userData.height = now_height;
202 addChild(bodyDef.userData);
203 body = world.CreateBody(bodyDef);
204 body.CreateShape(boxDef);
205 break;
206 case 3:
207 boxDef = new b2PolygonDef();
208 boxDef.SetAsBox(now_width / RATIO / 2, now_height / RATIO / 2);
209 boxDef.density = 1;
210 boxDef.friction = 0.4;
211 boxDef.restitution = 0.1;
212 bodyDef.userData = new RectSkin();
213 bodyDef.userData.width = now_width;
214 bodyDef.userData.height = now_height;
215 addChild(bodyDef.userData);
216 body = world.CreateBody(bodyDef);
217 body.CreateShape(boxDef);
218 break;
219 }
220 body.SetMassFromShapes();
221 }
222
223 private function createGrounds():void
224 {
225 var bodyDef:b2BodyDef;
226 var boxDef:b2PolygonDef;
227 var body:b2Body;
228
229 //刚体定义
230 bodyDef = new b2BodyDef();
231 //设置位置
232 bodyDef.position.Set(275.0 / RATIO, 250.0 / RATIO);
233 //形状定义
234 boxDef = new b2PolygonDef();
235 //设置高宽
236 boxDef.SetAsBox(250.0 / RATIO / 2, 20.0 / RATIO / 2);
237 //密度,0为静态物
238 boxDef.density = 0;
239 //摩擦力
240 boxDef.friction = 0.3;
241 //弹力
242 boxDef.restitution = 0.2;
243
244 body = world.CreateBody(bodyDef);
245 body.CreateShape(boxDef);
246 //计算质量,因此处为静态物,所以不需计算
247 //body.SetMassFromShapes();
248 }
249
250 private function showDebug():void
251 {
252 spriteToDrawOn = new Sprite();
253 addChild(spriteToDrawOn);
254 var dbgDraw:b2DebugDraw = new b2DebugDraw();
255 //绘制图形
256 dbgDraw.m_sprite = spriteToDrawOn;
257 //缩放比例
258 dbgDraw.m_drawScale = RATIO;
259 //填充线的颜色透明度
260 dbgDraw.m_alpha = 1.0;
261 //填充区域的颜色透明度
262 dbgDraw.m_fillAlpha = 0.5;
263 //线型粗细
264 dbgDraw.m_lineThickness = 1.0;
265 //绘制图形位
266 dbgDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;
267
268 world.SetDebugDraw(dbgDraw);
269 }
270
271 private function createWorld():void
272 {
273 //边界
274 var worldAABB:b2AABB = new b2AABB();
275 worldAABB.lowerBound.Set( -3000 / RATIO, -3000 / RATIO);
276 worldAABB.upperBound.Set(3000 / RATIO, 3000 / RATIO);
277 //重力
278 var gravity:b2Vec2 = new b2Vec2(0, 10);
279 //允许休眠
280 var doSleep:Boolean = true;
281
282 world = new b2World(worldAABB, gravity, doSleep);
283 }
284
285 private function onLoopHandler(e:Event):void
286 {
287 if (world == null) return;
288 //刷新物理空间
289 world.Step(TIME_STEP, ITERATIONS);
290
291 for (var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next)
292 {
293 if (bb.m_userData is MovieClip)
294 {
295 bb.m_userData.x = bb.GetPosition().x * RATIO;
296 bb.m_userData.y = bb.GetPosition().y * RATIO;
297 bb.m_userData.rotation = bb.GetAngle() * 180 / Math.PI;
298
299 if (bb.IsSleeping())
300 {
301 MovieClip(bb.m_userData).gotoAndStop(1);
302 }else
303 {
304 MovieClip(bb.m_userData).gotoAndStop(2);
305 }
306 }
307
308 if (bb.GetPosition().y * RATIO > stage.stageHeight)
309 {
310 tip_txt.text = '有物体掉落,游戏失败,请按R键重置游戏';
311 removeChild(bb.m_userData);
312 bb.m_userData = null;
313 world.DestroyBody(bb);
314 }
315 }
316 }
317
318 public function getScrollTip(arr:Array):Sprite
319 {
320 var sp:Sprite = new Sprite();
321 for (var i:int = 0; i < arr.length; i++)
322 {
323 var isp:Shape = new Shape();
324 isp.graphics.beginFill(0x3399CC, 0.4);
325 switch(arr[i].type)
326 {
327 case 1:
328 isp.graphics.drawCircle(0, 0, arr[i].width * 0.5);
329 break;
330 case 2:
331 isp.graphics.moveTo( 0, arr[i].height * 0.5);
332 isp.graphics.lineTo(arr[i].width * 0.5, -arr[i].height * 0.5);
333 isp.graphics.lineTo( -arr[i].width * 0.5, -arr[i].height * 0.5);
334 isp.rotation = 180;
335 break;
336 case 3:
337 isp.graphics.drawRect( -arr[i].width * 0.5, -arr[i].height * 0.5, arr[i].width, arr[i].height);
338 break;
339 }
340 isp.graphics.endFill();
341 isp.x = i * 50;
342 sp.addChild(isp);
343 }
344
345 return sp;
346 }
347
348 public function getData():Array
349 {
350 var arr:Array = [];
351 for (var i:int = 0; i < 10; i++)
352 {
353 var obj:Object = new Object();
354 if (i < 4)
355 {
356 obj.type = 3;
357 }else if (i > 6)
358 {
359 obj.type = 2;
360 }else
361 {
362 obj.type = 1;
363 }
364
365 obj.width = 40;
366 obj.height = 40;
367 arr.push(obj);
368 }
369 return arr;
370 }
371 }
372
373 }

代码运行结果截图(发现不是cnblogs的没注册用户看不到swf,下次不传swf了)

代码运行结果

贴图的刷新

bb.m_userData.x = bb.GetPosition().x * RATIO;

bb.m_userData.y = bb.GetPosition().y * RATIO;

bb.m_userData.rotation = bb.GetAngle() * 180 / Math.PI;

为了效果更好一点,可用Body.IsSleeping()来判断是否处于稳定状态来表现不同的效果

 

注:游戏结束在这里只是提示有物体脱离舞台并未锁定。

 

点击这里下载源码


/Files/_dark/Brickbox2d.rar

一个堆方块的小游戏,比比谁堆得高,主要用来练习box2d,效果如图

posted on 2012-03-14 12:29  pengyingh  阅读(1110)  评论(0编辑  收藏  举报

导航