Simulating a hook with Box2D

In a comment to Pumpkin Story prototype, a reader asked for a function to simulate a hook.

This can be quite easily done in some steps.

This first step will set a rule: you can only hook to a static body. Obviously it’s not a mandatory, rule, but just a gameplay one.

In this example, we’ll use the same script we saw at Box2D joints: Distance Joint with some modifications and the concept saw at Creating a sling with Box2D using joints when we must remove the hook.

This first part is simple, and will introduce us into the world of hooks.

Clicking and holding the mouse on a static object (the green one), we’ll create a joint between the centre of the ball and the mouse pointer.

Releasing the mouse will destroy the joint.

This is the 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
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 mouseJoint:b2MouseJoint;
		var mousePVec:b2Vec2 = new b2Vec2();
		var bd:b2BodyDef;
		var the_circle:b2CircleDef = new b2CircleDef();
		var circle:b2Body;
		var the_box:b2PolygonDef = new b2PolygonDef();
		var the_joint:b2DistanceJointDef = new b2DistanceJointDef();
		var joint:b2DistanceJoint;
		//
		public var m_world:b2World;
		public var m_iterations:int=10;
		public var m_timeStep:Number=1.0/30.0;
		public function HelloWorld() {
			// world setup
			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
			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.5;
			dbgDraw.m_lineThickness=1;
			dbgDraw.m_drawFlags=b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit;
			m_world.SetDebugDraw(dbgDraw);
			// ground
			the_box.SetAsBox(9,0.5);
			the_box.density=0;
			the_box.friction=0.4;
			the_box.restitution=0.1;
			bd = new b2BodyDef();
			bd.position.Set(8.5, 13);
			var ground:b2Body=m_world.CreateBody(bd);
			ground.CreateShape(the_box);
			ground.SetMassFromShapes();
			// roof
			the_box.SetAsBox(9,0.5);
			the_box.density=0;
			the_box.friction=0.4;
			the_box.restitution=0.1;
			bd = new b2BodyDef();
			bd.position.Set(8.5, 0);
			var roof:b2Body=m_world.CreateBody(bd);
			roof.CreateShape(the_box);
			roof.SetMassFromShapes();
			// circle
			the_circle.radius=2;
			the_circle.density=1.0;
			the_circle.friction=0.4;
			the_circle.restitution=0.3;
			bd = new b2BodyDef();
			bd.position.Set(6, 2);
			circle=m_world.CreateBody(bd);
			circle.CreateShape(the_circle);
			circle.SetMassFromShapes();
			// square
			the_box.SetAsBox(1.5,1.5);
			the_box.density=1.0;
			the_box.friction=0.4;
			the_box.restitution=0.1;
			bd = new b2BodyDef();
			bd.position.Set(2, 2);
			var box:b2Body=m_world.CreateBody(bd);
			box.CreateShape(the_box);
			box.SetMassFromShapes();
			// listeners
			stage.addEventListener(MouseEvent.MOUSE_DOWN, createMouse);
			stage.addEventListener(MouseEvent.MOUSE_UP, destroyMouse);
			addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
		}
		public function createMouse(evt:MouseEvent):void {
			var body:b2Body=GetBodyAtMouse(true);
			if (body) {
				the_joint.Initialize(circle, body, circle.GetWorldCenter(),new b2Vec2(mouseX/30,mouseY/30));
				the_joint.collideConnected=true;
				joint=m_world.CreateJoint(the_joint) as b2DistanceJoint;
			}
		}
		public function destroyMouse(evt:MouseEvent):void {
			if (joint) {
				m_world.DestroyJoint(joint);
				joint=null;
			}
		}
		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()) {
					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);
		}
	}
}

Let’s see what I changed from the original script

First, I changed GetBodyAtMouse function – lines 104-126 – so it only returns static objects at line 116.

Then, I modified createMouse function – lines 90-97 – in order to create a joint between the center of the ball and the mouse position in the selected static object, and obviously the destroyMouse one to remove the joint when I release the mouse.

There isn’t much more to see that you can’t see in the posts I mentioned before.

Here it is the result:

Click on a static object (green) to create a joint on the fly

No need to include the source code, just replace the script at Box2D joints: Distance Joint with this one.

Next time, we’ll see how to shoot the hook.

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (16 votes, average: 4.06 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 4 comments

  1. Guest

    on September 27, 2009 at 4:00 pm

    Thanks, you’re the best!!!

  2. Rendering joints with Box2D : Emanuele Feronato

    on September 29, 2009 at 4:00 pm

    [...] following script is the same you can find at Simulating a hook with Box2D but I will render the joints with lines. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [...]

  3. James

    on October 4, 2009 at 10:23 pm

    Where is the other half of this tutorial?

  4. New

    on October 31, 2009 at 11:32 am

    We already got drawing boxes on the fly, drawing circles on the fly, how about joints? This is kind of that, but only on mousejoint, and thus only pinning on the background, how about joints between bodies, would love that.