The magic of compound objects with Box2D

You can call it complex shapes, compound shapes, complex objects, compound objects or even poisoned underpants, but you all want to create complex “things” with Box2D.

Despite the “complex” word, it’s very easy to create one.

You just have to add multiple shapes to a body.

If you followed my latest Box2D tutorials, you will find the whole code I am going to show you very familiar, with some exceptions I will explain.

But first let’s take a look at what I am going to create: a perfect tile based maze, made with small squares representing the tiles and managed as a single object.

Here it is: if you drag the maze, you will see it’s a single object made (compound) with small tiles – the real objects, used as primitives.

Here it is the code:

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
package {
	import flash.display.Sprite;
	import flash.events.Event;
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	import Box2D.Dynamics.Joints.*;
	import flash.events.MouseEvent;
	public class HelloWorld extends Sprite {
		var body:b2Body;
		var mouseJoint:b2MouseJoint;
		var m_world:b2World;
		var m_iterations:int=10;
		var m_timeStep:Number=1.0/30.0;
		var maze:Array = [[1,1,1,1,1,1,1,1,1],[1,0,0,0,0,0,0,0,1],[1,1,1,1,1,1,1,0,1],[1,0,0,0,1,0,0,0,1],[1,0,1,0,1,0,1,1,1],[1,0,1,0,1,0,0,0,1],[1,0,1,1,1,1,1,0,1],[1,0,0,0,0,0,0,0,1],[1,1,1,1,1,1,1,1,1]];
		public function HelloWorld() {
			addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, createMouse);
			stage.addEventListener(MouseEvent.MOUSE_UP, destroyMouse);
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.lowerBound.Set(-100.0, -100.0);
			worldAABB.upperBound.Set(100.0, 100.0);
			var gravity:b2Vec2=new b2Vec2(0.0,10.0);
			var doSleep:Boolean=true;
			m_world=new b2World(worldAABB,gravity,doSleep);
			// debug draw start
			var m_sprite:Sprite;
			m_sprite = new Sprite();
			addChild(m_sprite);
			var dbgDraw:b2DebugDraw = new b2DebugDraw();
			var dbgSprite:Sprite = new Sprite();
			m_sprite.addChild(dbgSprite);
			dbgDraw.m_sprite=m_sprite;
			dbgDraw.m_drawScale=30;
			dbgDraw.m_alpha = 1;
			dbgDraw.m_fillAlpha=0.7;
			dbgDraw.m_lineThickness=1;
			m_world.SetDebugDraw(dbgDraw);
			// debug draw end
			var bodyDef:b2BodyDef;
			var boxDef:b2PolygonDef;
			// lower static object
			bodyDef = new b2BodyDef();
			bodyDef.position.Set(8.5, 13);
			boxDef = new b2PolygonDef();
			boxDef.SetAsBox(8.5, 0.5);
			boxDef.friction=0.3;
			boxDef.density=0;
			body=m_world.CreateBody(bodyDef);
			body.CreateShape(boxDef);
			body.SetMassFromShapes();
			// objects
			bodyDef = new b2BodyDef();
			bodyDef.position.Set(1, 3);
			body = m_world.CreateBody(bodyDef);
			for (var j=0; j<9; j++) {
				for (var i=0; i<9; i++) {
					if (maze[j][i]==1) {
						boxDef = new b2PolygonDef();
						boxDef.SetAsOrientedBox(0.5,0.5,new b2Vec2(i, j), 0);
						boxDef.density=1.0;
						boxDef.friction=0.5;
						boxDef.restitution=0.2;
						body.CreateShape(boxDef);
					}
				}
			}
			body.SetMassFromShapes();
		}
		public function createMouse(evt:MouseEvent):void {
			var body:b2Body=GetBodyAtMouse();
			if (body) {
				var mouseJointDef:b2MouseJointDef=new b2MouseJointDef;
				mouseJointDef.body1=m_world.GetGroundBody();
				mouseJointDef.body2=body;
				mouseJointDef.target.Set(mouseX/30, mouseY/30);
				mouseJointDef.maxForce=30000;
				mouseJointDef.timeStep=m_timeStep;
				mouseJoint=m_world.CreateJoint(mouseJointDef) as b2MouseJoint;
			}
		}
		public function destroyMouse(evt:MouseEvent):void {
			if (mouseJoint) {
				m_world.DestroyJoint(mouseJoint);
				mouseJoint=null;
			}
		}
		private var mousePVec:b2Vec2 = new b2Vec2();
		public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body {
			var mouseXWorldPhys = (mouseX)/30;
			var mouseYWorldPhys = (mouseY)/30;
			mousePVec.Set(mouseXWorldPhys, mouseYWorldPhys);
			var aabb:b2AABB = new b2AABB();
			aabb.lowerBound.Set(mouseXWorldPhys - 0.001, mouseYWorldPhys - 0.001);
			aabb.upperBound.Set(mouseXWorldPhys + 0.001, mouseYWorldPhys + 0.001);
			var k_maxCount:int=10;
			var shapes:Array = new Array();
			var count:int=m_world.Query(aabb,shapes,k_maxCount);
			var body:b2Body=null;
			for (var i:int = 0; i < count; ++i) {
				if (shapes[i].GetBody().IsStatic()==false||includeStatic) {
					var tShape:b2Shape=shapes[i] as b2Shape;
					var inside:Boolean=tShape.TestPoint(tShape.GetBody().GetXForm(),mousePVec);
					if (inside) {
						body=tShape.GetBody();
						break;
					}
				}
			}
			return body;
		}
		public function Update(e:Event):void {
			m_world.Step(m_timeStep, m_iterations);
			if (mouseJoint) {
				var mouseXWorldPhys=mouseX/30;
				var mouseYWorldPhys=mouseY/30;
				var p2:b2Vec2=new b2Vec2(mouseXWorldPhys,mouseYWorldPhys);
				mouseJoint.SetTarget(p2);
			}
		}
	}
}

You can find the maze at line 16 (yes, I did not create it dynamically, feel free to do it if you want), and now let’s look at the difference between a normal and a compound object.

A normal object, like the green static floor, after the CrateShape method at line 51, has a setMassFromShapes that finalizes the object itself.

If you look at the maze, inside the two cycles at lines 57-58, I only have CreateShape calls while a single setMassFromShapes is called after I completed the cycles.

This way, I am getting all objects merged in one big, compound, object.

And that’s all…

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (14 votes, average: 4.43 out of 5)
Loading ... Loading ...
If you found this post useful, please consider a small donation.
» 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.

12 Responses to “The magic of compound objects with Box2D”

  1. JeanPier on December 12th, 2008 7:43 pm

    Very good post… as usual.
    Thanks Emanuele for this.

  2. Josh of Cubicle Ninjas on December 12th, 2008 8:13 pm

    If we end up with a Box 2D platformer (with level editor) I think you should sell the source. People can make it off of your tutorials if interested, but I think there would be a big market to sell this as well. :)

  3. Orion on December 12th, 2008 9:41 pm

    ;o, I can’t see anything on my own flash oO with the same code.

  4. sathoro on December 12th, 2008 11:48 pm

    This could be a cool game idea if there is a ball at one end of the maze and you have to pick up and rotate the maze to get the ball to the end.

  5. sathoro on December 12th, 2008 11:49 pm

    Sorry I meant start the ball at the beginning and then have it go to the end.

  6. anlik on December 13th, 2008 3:56 am

    up up up, I hope that i can see Box2D tutorial everydayO(∩_∩)O,Thanks

  7. Nathan on December 13th, 2008 6:57 pm

    I was thinking the same thing as sathoro. I’ve been trying to do one of those for a long time. Now I think I know how!

  8. ruslan on December 14th, 2008 2:18 am

    How do you visualize the shapes(boxes)??

  9. Pippo on December 14th, 2008 8:25 pm

    using debugsrite

  10. samuvagyok on December 15th, 2008 3:41 pm

    Good article!
    Have you ever tried to use “custom” polygons?
    When I created one in box2d it seems it doesn’t use the physics – no gravity, no collision etc. and I still can’t find any helpful post in the box2d forum. Maybe I missed something that is in the setAsBox method…
    Would you write an article about custom shapes?
    Thanks, and best regards!
    S

  11. Justin Edmond on June 6th, 2009 8:02 pm

    I’m getting the same issue as some other people where nothing is showing up…

    I’m digging around the code but I can’t find a reason why it should be doing this.

Leave a Reply




Trackbacks

  1. Understanding custom polygons in Box2D : Emanuele Feronato on December 19th, 2008 7:41 pm

    [...] week I explained The magic of compound objects with Box2D, now it’s time to understanding custom [...]

flash games company