Box2D with the WorldConstructionKit

Before the weekend, I suggest to dig into the Box2D API for Flash with the great WorldConstructionKit.
Box2D is a physical engine wrote in C++ and translated into many languages such as Java, Objective-C, AS3, JavaScript…

For ActionScript3, there are 2 ports : Boris the brave’s port, and the WCK. You can find a performance comparison here made by Allan Bishop.
The WCK is the best thanks to the Alchemy port : it translates the C++ into something which can be understable by Flash. More information here.

The WCK provides a “a toolset / framework for rapidly developing physics based games / websites within the Flash IDE. WCK allows you to layout your 2d worlds / game levels entirely within Flash and have them hook into the physics simulation without writing any code.”

Here are some demos I made with it :
A simple circle made without any code, only with MovieClip on the Stage and propreties added by the WCK components (dynamic object -> the circle vs static objects -> the wall). If you wish the camera followed the circle, just add the name of your MovieClip in the focusOn property of your World; don’t forget to check the scrolling’s property too.

A simple Mario game. In this case, for sure, there is some programming. (Do not forget to click into the windows before pressing any keys)

The code of the AI :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package {
 
	import Box2DAS.Common.V2;
	import Box2DAS.Dynamics.StepEvent;
 
	import shapes.Box;
 
	import flash.events.Event;
 
	/**
	 * @author Aymeric
	 */
 
	public class Momie extends Box {
 
		private var _arriere:Boolean;
 
		public function Momie() {
 
			this.addEventListener(Event.ADDED_TO_STAGE, _init);
		}
 
		private function _init(evt:Event):void {
 
			this.removeEventListener(Event.ADDED_TO_STAGE, _init);
		}
 
		public override function create():void {
			reportBeginContact = true;
			reportEndContact = true;
			super.create();
 
			listenWhileVisible(world, StepEvent.STEP, _parseInput);
		}
 
		private function _parseInput(evt:Event):void {
 
			if (_arriere == false) {
				if (this.x < 800) {
					b2body.ApplyImpulse(new V2(0.2, 0), b2body.GetWorldCenter());
				} else {
					_arriere = true;
					this.gotoAndPlay("marche_gauche");
				}
			} else {
 
				if (this.x > 30) {
					b2body.ApplyImpulse(new V2(-0.2, 0), b2body.GetWorldCenter());
 
				} else {
					_arriere = false;
					this.gotoAndPlay("marche_droite");
				}
			}
 
		}
	}
}

Nothing too complex, but note that the character moved thanks to some force vector working with impulsion. Not as simple than an old x++ 😉

And now the Hero :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package {
 
	import Box2DAS.Collision.*;
	import Box2DAS.Common.*;
	import Box2DAS.Dynamics.*;
 
	import misc.*;
 
	import shapes.*;
 
	import wck.*;
 
	import flash.events.*;
	import flash.utils.Timer;
 
	/**
	 * @author Aymeric
	 */
 
	public class Heros extends Box {
 
		public var contacts:ContactList;
 
		private var _momie:Momie;
		private var _memoireSens:String;
		private var _timer:Timer;
 
		public function Heros() {
 
			this.addEventListener(Event.ADDED_TO_STAGE, _init);
		}
 
		private function _init(evt:Event):void {
 
			this.removeEventListener(Event.ADDED_TO_STAGE, _init);
		}
 
		public override function create():void {
 
			reportBeginContact = true;
			reportEndContact = true;
			// fixedRotation = true;
			super.create();
			listenWhileVisible(world, StepEvent.STEP, parseInput, false, 10);
			listenWhileVisible(this, ContactEvent.BEGIN_CONTACT, handleContact);
			contacts = new ContactList();
			contacts.listenTo(this);
		}
 
		public function handleContact(ctcEvt:ContactEvent):void {
 
			_momie = ctcEvt.other.m_userData as Momie;
			var piece:Circle = ctcEvt.other.m_userData as Circle;
			if (_momie) {
				// Util.addChildAtPosOf(world, new Boum(), p);
 
				_momie.gotoAndStop("mort");
				_momie.removeAllListeners();
 
				_timer = new Timer(500, 1);
				_timer.addEventListener(TimerEvent.TIMER_COMPLETE, _supprimerMomie);
				_timer.start();
			}
 
			if (piece) {
				piece.remove();
				piece = null;
			}
		}
 
		private function _supprimerMomie(tEvt:TimerEvent):void {
 
			_momie.remove();
			_momie = null;
 
			_timer.removeEventListener(TimerEvent.TIMER_COMPLETE, _supprimerMomie);
			_timer = null;
		}
 
		public function parseInput(e:Event):void {
			var manifold:b2WorldManifold = null;
			var dot:Number = -1;
			if (!contacts.isEmpty()) {
				contacts.forEach(function(keys:Array, c:ContactEvent) {
					var wm:b2WorldManifold = c.getWorldManifold();
					if (wm.normal) {
						var d:Number = wm.normal.dot(gravity);
						if (!manifold || d > dot) {
							manifold = wm;
							dot = d;
						}
					}
				});
				contacts.clean();
			}
			var left:Boolean = Input.kd('A', 'LEFT');
			var right:Boolean = Input.kd('D', 'RIGHT');
			var jump:Boolean = Input.kp(' ', 'UP');
			var v:V2;
 
			if (jump && manifold) {
 
				v = manifold.normal.clone().multiplyN(-7);
				b2body.ApplyImpulse(v, b2body.GetWorldCenter());
 
			} else if (left) {
 
				if (this.currentLabel != "marche_gauche") {
					this.gotoAndPlay("marche_gauche");
				}
				b2body.ApplyImpulse(new V2(-0.2, 0), b2body.GetWorldCenter());
				_memoireSens = "gauche";
 
			} else if (right) {
 
				if (this.currentLabel != "marche_droite") {
					this.gotoAndPlay("marche_droite");
				}
				b2body.ApplyImpulse(new V2(0.2, 0), b2body.GetWorldCenter());
				_memoireSens = "droite";
 
			} else {
				if (_memoireSens == "gauche") {
					this.gotoAndPlay("init_gauche");
				} else {
					this.gotoAndPlay("init_droite");
				}
 
			}
		}
	}
}

There are some collisions problem to handle, I haven’t found the best resolution yet…

To conclude I found that the WCK is really a great tool to create a dynamic world ! I’m sure that a breakout game is really easy to make with the WCK, but if you want to make something very complex, you must learn Box2D and have some knowledges of C++.

Recently, I tried the Citrus Engine and find it awesome. It is a game engine based on Box2D (but not the WCK), but I’m sad, it is no more supported… However maybe, I will make a school project with it.
Stay tuned !

The zip files of the little game.

Leave a Reply

Your email address will not be published. Required fields are marked *