Box2D Flash game creation tutorial – part 2

After seeing the character creation in Box2D Flash game creation tutorial – part 1, it’s time to add some coins to collect.

This process will involve some interesting Box2D features, like sensors and custom collision management.

I would suggest to read the basics of sensors at Erase Box: the tutorial and custom collision management at Creation of a Flash Stabilize! clone using Box2D – part 4.

Although they are both referred to an older Box2D version, they’ll introduce you to sensor and collisions.

Now the concept is simple: we are placing some circular sensors around the stage (the coins), then we’ll create a custom contact listener class to check whether the player is over a coin or not. If it’s over, we’ll remove the coin.

So this is the main script:

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	public class ball02 extends Sprite {
		// world creation
		public var world:b2World=new b2World(new b2Vec2(0,10.0),true);
		public var world_scale:int=30;
		// the player
		public var player:b2Body;
		// force to apply to the player
		public var force:b2Vec2;
		// variables to store whether the keys are pressed or not
		// true = pressed;
		// false = unpressed
		public var left,right,up,down:Boolean=false;
		// declaring my custom contact listener class
		public var contact_listener=new custom_contact_listener();
		public function ball02():void {
			// assigning the contact listener to the world
			world.SetContactListener(contact_listener);
			// calling debug draw function
			debug_draw();
			// drawing the boundaries
			draw_box(250,400,500,10,false,"ground");
			draw_box(0,200,10,400,false,"left");
			draw_box(500,200,10,400,false,"right");
			draw_box(250,0,500,10,false,"roof");
			// adding the player at 250,200
			add_player(250,200);
			// adding some coins
			for (var i:int = 1; i<=5; i++) {
				draw_coin(Math.random()*400+50,Math.random()*300+50,Math.random()*3+2);
			}
			// listeners needed for the game to work
			addEventListener(Event.ENTER_FRAME, update);
			stage.addEventListener(KeyboardEvent.KEY_DOWN,on_key_down);
			stage.addEventListener(KeyboardEvent.KEY_UP,on_key_up);
		}
		// according to the key pressed, set the proper variable to "true"
		public function on_key_down(e:KeyboardEvent):void {
			switch (e.keyCode) {
				case 37 :
					left=true;
					break;
				case 38 :
					up=true;
					break;
				case 39 :
					right=true;
					break;
				case 40 :
					down=true;
					break;
			}
		}
		// according to the key released, set the proper variable to "false"
		public function on_key_up(e:KeyboardEvent):void {
			switch (e.keyCode) {
				case 37 :
					left=false;
					break;
				case 38 :
					up=false;
					break;
				case 39 :
					right=false;
					break;
				case 40 :
					down=false;
					break;
			}
		}
		// function to draw a coin
		public function draw_coin(px,py,r):void {
			var my_body:b2BodyDef= new b2BodyDef();
			my_body.position.Set(px/world_scale, py/world_scale);
			var my_circle:b2CircleShape=new b2CircleShape(r/world_scale);
			var my_fixture:b2FixtureDef = new b2FixtureDef();
			my_fixture.shape=my_circle;
			// look! it's a sensor!!
			my_fixture.isSensor=true;
			var world_body:b2Body=world.CreateBody(my_body);
			world_body.CreateFixture(my_fixture);
		}
		// simple function to draw a box
		public function draw_box(px,py,w,h,d,ud):void {
			var my_body:b2BodyDef= new b2BodyDef();
			my_body.position.Set(px/world_scale, py/world_scale);
			if (d) {
				my_body.type=b2Body.b2_dynamicBody;
			}
			var my_box:b2PolygonShape = new b2PolygonShape();
			my_box.SetAsBox(w/2/world_scale, h/2/world_scale);
			var my_fixture:b2FixtureDef = new b2FixtureDef();
			my_fixture.shape=my_box;
			var world_body:b2Body=world.CreateBody(my_body);
			world_body.SetUserData(ud);
			world_body.CreateFixture(my_fixture);
		}
		// function to add the player
		public function add_player(px,py):void {
			var my_body:b2BodyDef= new b2BodyDef();
			my_body.position.Set(px/world_scale, py/world_scale);
			my_body.type=b2Body.b2_dynamicBody;
			var my_circle:b2CircleShape=new b2CircleShape(10/world_scale);
			var my_fixture:b2FixtureDef = new b2FixtureDef();
			my_fixture.shape=my_circle;
			player=world.CreateBody(my_body);
			player.CreateFixture(my_fixture);
		}
		// debug draw
		public function debug_draw():void {
			var debug_draw:b2DebugDraw = new b2DebugDraw();
			var debug_sprite:Sprite = new Sprite();
			addChild(debug_sprite);
			debug_draw.SetSprite(debug_sprite);
			debug_draw.SetDrawScale(world_scale);
			debug_draw.SetFlags(b2DebugDraw.e_shapeBit);
			world.SetDebugDraw(debug_draw);
		}
		// function to be executed at every frame
		public function update(e:Event):void {
			// setting the force to null
			force=new b2Vec2(0,0);
			// according to the key(s) pressed, add the proper vector force
			if (left) {
				force.Add(new b2Vec2(-10,0));
			}
			if (right) {
				force.Add(new b2Vec2(10,0));
			}
			if (up) {
				force.Add(new b2Vec2(0,-20));
			}
			if (down) {
				force.Add(new b2Vec2(0,5));
			}
			// if there is any force, then apply it
			if (force.x||force.y) {
				player.ApplyForce(force,player.GetWorldCenter());
			}
			world.Step(1/30,10,10);
			world.ClearForces();
			// scanning through all bodies
			for (var worldbody:b2Body = world.GetBodyList(); worldbody; worldbody = worldbody.GetNext()) {
				// if a body is marked as "remove"...
				if (worldbody.GetUserData()=="remove") {
					// ... just remove it!!
					world.DestroyBody(worldbody);
				}
			}
			world.DrawDebugData();
		}
	}
}

Let’s see the interesting lines:

Line 22: declaring my contact_listener variable, as custom_contact_listener type

Line 25: assigning my custom contact listener class to Box2D world

Lines 36-38: calling five times the draw_coin function passing three parameters: x position, y position and radius.

Lines 79-89: the draw_coin function… just a basic function that draw a circle… just notice at line 86 how I am declaring the circle as a sensor. This way the circle exists in the world but won’t physically collide with anything. Also, a sensor should be a static body, or it will fall down outside the stage as it won’t collide with anything.

Lines 150-156: scanning through all bodies to find, and eventually remove, bodies marked with remove. Such marker is handled by the custom contact listener class, located in the custom_contact_listener.as file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package {
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Dynamics.Joints.*;
	import Box2D.Dynamics.Contacts.*;
	import Box2D.Common.*;
	import Box2D.Common.Math.*;
	class custom_contact_listener extends b2ContactListener {
		override public function BeginContact(contact:b2Contact):void {
			// getting the fixtures that collided
			var fixtureA:b2Fixture=contact.GetFixtureA();
			var fixtureB:b2Fixture=contact.GetFixtureB();
			// if the fixture is a sensor, mark the parent body to be removed
			if (fixtureB.IsSensor()) {
				fixtureB.GetBody().SetUserData("remove");
			}
			if (fixtureA.IsSensor()) {
				fixtureA.GetBody().SetUserData("remove");
			}
		}
	}
}

BeginContact function will give us the fixtures involved in the collision. Then at lines 15-17 and 18-20 I am checking if the fixture is a sensor, then eventually mark its parent body to be removed.

Important: don’t try to remove the body inside this function, because bodies have a locked status while they are in the middle of a timestep, so you should remove a body only after you performed the Step (line 147 of the main file). This gave me a little headache, so you’ve been warned!

This is the result:

Pick up all little circles moving the player tapping on arrow keys.

Download the source code.

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (19 votes, average: 4.21 out of 5)
Loading ... Loading ...
Flash Templates provided by Template Monster are pre-made web design products developed using Flash technology.
They can be easily customized to meet the unique requirements of your project.
Be my fan on Facebook and follow me on Twitter! Exclusive content for my Facebook fans and Twitter followers

This post has 6 comments

  1. manish

    on February 25, 2010 at 1:13 pm

    i m in a in animation school quite wonderful
    using box2d as environment makes our job easier even i do not know much programming to make something nice animation thanks
    post but a question why u always wear a cap I saw your many pics on facebook

  2. Dave Fulton

    on February 26, 2010 at 2:09 am

    Awesome stuff! Box2D is my next area of exploration with actionscript so it’s great that you are sharing so much valuable information.

    I can’t seem to get Box2D to work with CS3 though… is there a version conflict?

  3. mike

    on March 9, 2010 at 11:54 pm

    Hi,

    Your website is such a great ressource, Thank you!

    Could you provide us with CS3 versions of your fla?

    Thank you SO much!

  4. A Freelancer’s Flash Bash [6] | Freelance Flash Games News

    on March 18, 2010 at 9:34 pm

    [...] Feronato has a nice little series on Box2D game creation, and compiled a list of isometric engines for flash game creation. Be sure to check it out for some [...]

  5. Ollie

    on April 15, 2011 at 10:15 am

    Thanks for the tutorial.

    Is it possible to read the position of the sensor? It reports a position of 0,0 when I call .GetPosition

  6. Getting a contact point between two bodies in box2d as3

    on June 21, 2011 at 7:18 pm

    [...] First of all you need your own custom class that extends b2ContactListener and add it to box2d’s world as contact listener, this is a quick tip so I’m not gonna go in detail about this, here’s a good introduction. [...]