One of the demos in the TestBad application that comes with the Box2d Firemonkey physics engine project is a demo called Theo Jansen Walker. This demo should work in Delphi XE5, Delphi XE6, and AppMethod. It should also work cross platform on Android, IOS, Windows, and OSX. The walker is a pretty complex physics object built up of a chassis and legs. It basically looks like a spider and is actually a representation of a Strandbeest. From what I can gather about it on the Wikipedia entry a Strandbeest is self propelled based on it’s interactions with it’s environment. That being said it could easily be modified for use in a game. The walker has six legs that move it around. When you load up the physics simulation the walker will automatically start walking towards the right side of the screen. However, you can also use the keyboard to make the walker walk left and right. Here is the sample code of the code needed to create the chassis:

// Chassis begin shape := Tb2PolygonShape.Create; shape.SetAsBox(2.5, 1.0); sd := Tb2FixtureDef.Create; sd.density := 1.0; sd.shape := shape; sd.filter.groupIndex := -1; bd := Tb2BodyDef.Create; bd.bodyType := b2_dynamicBody; {$IFDEF OP_OVERLOAD} bd.position := pivot + m_offset; {$ELSE} bd.position := Add(pivot, m_offset); {$ENDIF} m_chassis := m_world.CreateBody(bd); m_chassis.CreateFixture(sd); end; begin cshape := Tb2CircleShape.Create; cshape.m_radius := 1.6; sd := Tb2FixtureDef.Create; sd.density := 1.0; sd.shape := cshape; sd.filter.groupIndex := -1; bd := Tb2BodyDef.Create; bd.bodyType := b2_dynamicBody; {$IFDEF OP_OVERLOAD} bd.position := pivot + m_offset; {$ELSE} bd.position := Add(pivot, m_offset); {$ENDIF} m_wheel := m_world.CreateBody(bd); m_wheel.CreateFixture(sd); end; begin jd := Tb2RevoluteJointDef.Create; {$IFDEF OP_OVERLOAD} jd.Initialize(m_wheel, m_chassis, pivot + m_offset); {$ELSE} jd.Initialize(m_wheel, m_chassis, Add(pivot, m_offset)); {$ENDIF} jd.collideConnected := False; jd.motorSpeed := m_motorSpeed; jd.maxMotorTorque := 400.0; jd.enableMotor := m_motorOn; m_motorJoint := Tb2RevoluteJoint(m_world.CreateJoint(jd)); end; {$IFDEF OP_OVERLOAD} wheelAnchor := pivot + MakeVector(0.0, -0.8); {$ELSE} wheelAnchor := Add(pivot, MakeVector(0.0, -0.8)); {$ENDIF} CreateLeg(-1.0, wheelAnchor); CreateLeg(1.0, wheelAnchor); m_wheel.SetTransform(m_wheel.GetPosition, 120.0 * Pi / 180.0); CreateLeg(-1.0, wheelAnchor); CreateLeg(1.0, wheelAnchor); m_wheel.SetTransform(m_wheel.GetPosition, -120.0 * Pi / 180.0); CreateLeg(-1.0, wheelAnchor); CreateLeg(1.0, wheelAnchor); 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 // Chassis begin shape : = Tb2PolygonShape . Create ; shape . SetAsBox ( 2.5 , 1.0 ) ; sd : = Tb2FixtureDef . Create ; sd . density : = 1.0 ; sd . shape : = shape ; sd . filter . groupIndex : = - 1 ; bd : = Tb2BodyDef . Create ; bd . bodyType : = b2_dynamicBody ; { $ IFDEF OP_OVERLOAD } bd . position : = pivot + m_offset ; { $ ELSE } bd . position : = Add ( pivot , m_offset ) ; { $ ENDIF } m_chassis : = m_world . CreateBody ( bd ) ; m_chassis . CreateFixture ( sd ) ; end ; begin cshape : = Tb2CircleShape . Create ; cshape . m_radius : = 1.6 ; sd : = Tb2FixtureDef . Create ; sd . density : = 1.0 ; sd . shape : = cshape ; sd . filter . groupIndex : = - 1 ; bd : = Tb2BodyDef . Create ; bd . bodyType : = b2_dynamicBody ; { $ IFDEF OP_OVERLOAD } bd . position : = pivot + m_offset ; { $ ELSE } bd . position : = Add ( pivot , m_offset ) ; { $ ENDIF } m_wheel : = m_world . CreateBody ( bd ) ; m_wheel . CreateFixture ( sd ) ; end ; begin jd : = Tb2RevoluteJointDef . Create ; { $ IFDEF OP_OVERLOAD } jd . Initialize ( m_wheel , m_chassis , pivot + m_offset ) ; { $ ELSE } jd . Initialize ( m_wheel , m_chassis , Add ( pivot , m_offset ) ) ; { $ ENDIF } jd . collideConnected : = False ; jd . motorSpeed : = m_motorSpeed ; jd . maxMotorTorque : = 400.0 ; jd . enableMotor : = m_motorOn ; m_motorJoint : = Tb2RevoluteJoint ( m_world . CreateJoint ( jd ) ) ; end ; { $ IFDEF OP_OVERLOAD } wheelAnchor : = pivot + MakeVector ( 0.0 , - 0.8 ) ; { $ ELSE } wheelAnchor : = Add ( pivot , MakeVector ( 0.0 , - 0.8 ) ) ; { $ ENDIF } CreateLeg ( - 1.0 , wheelAnchor ) ; CreateLeg ( 1.0 , wheelAnchor ) ; m_wheel . SetTransform ( m_wheel . GetPosition , 120.0 * Pi / 180.0 ) ; CreateLeg ( - 1.0 , wheelAnchor ) ; CreateLeg ( 1.0 , wheelAnchor ) ; m_wheel . SetTransform ( m_wheel . GetPosition , - 120.0 * Pi / 180.0 ) ; CreateLeg ( - 1.0 , wheelAnchor ) ; CreateLeg ( 1.0 , wheelAnchor ) ;

As you can see at the bottom it uses a function called CreateLeg to create each of the six legs. Here is the source code for the CreateLeg procedure:

procedure TJansenWalker.CreateLeg(s: PhysicsFloat; const wheelAnchor: TVector2); var p1, p2, p3, p4, p5, p6: TVector2; vertices: array[0..2] of TVector2; poly1, poly2: Tb2PolygonShape; fd1, fd2: Tb2FixtureDef; bd1, bd2: Tb2BodyDef; body1, body2: Tb2Body; djd: Tb2DistanceJointDef; rjd: Tb2RevoluteJointDef; begin p1 := MakeVector(5.4 * s, -6.1); p2 := MakeVector(7.2 * s, -1.2); p3 := MakeVector(4.3 * s, -1.9); p4 := MakeVector(3.1 * s, 0.8); p5 := MakeVector(6.0 * s, 1.5); p6 := MakeVector(2.5 * s, 3.7); fd1 := Tb2FixtureDef.Create; fd2 := Tb2FixtureDef.Create; fd1.filter.groupIndex := -1; fd2.filter.groupIndex := -1; fd1.density := 1.0; fd2.density := 1.0; poly1 := Tb2PolygonShape.Create; poly2 := Tb2PolygonShape.Create; if s > 0.0 then begin vertices[0] := p1; vertices[1] := p2; vertices[2] := p3; poly1.SetVertices(@vertices[0], 3); vertices[0] := b2Vec2_zero; {$IFDEF OP_OVERLOAD} vertices[1] := p5 - p4; vertices[2] := p6 - p4; {$ELSE} vertices[1] := Subtract(p5, p4); vertices[2] := Subtract(p6, p4); {$ENDIF} poly2.SetVertices(@vertices[0], 3); end else begin vertices[0] := p1; vertices[1] := p3; vertices[2] := p2; poly1.SetVertices(@vertices[0], 3); vertices[0] := b2Vec2_zero; {$IFDEF OP_OVERLOAD} vertices[1] := p6 - p4; vertices[2] := p5 - p4; {$ELSE} vertices[1] := Subtract(p6, p4); vertices[2] := Subtract(p5, p4); {$ENDIF} poly2.SetVertices(@vertices[0], 3); end; fd1.shape := poly1; fd2.shape := poly2; bd1 := Tb2BodyDef.Create; bd1.bodyType := b2_dynamicBody; bd1.position := m_offset; bd2 := Tb2BodyDef.Create; bd2.bodyType := b2_dynamicBody; {$IFDEF OP_OVERLOAD} bd2.position := p4 + m_offset; {$ELSE} bd2.position := Add(p4, m_offset); {$ENDIF} bd1.angularDamping := 10.0; bd2.angularDamping := 10.0; body1 := m_world.CreateBody(bd1); body2 := m_world.CreateBody(bd2); body1.CreateFixture(fd1); body2.CreateFixture(fd2); djd := Tb2DistanceJointDef.Create; // Using a soft distance constraint can reduce some jitter. // It also makes the structure seem a bit more fluid by // acting like a suspension system. djd.dampingRatio := 0.5; djd.frequencyHz := 10.0; {$IFDEF OP_OVERLOAD} djd.Initialize(body1, body2, p2 + m_offset, p5 + m_offset); {$ELSE} djd.Initialize(body1, body2, Add(p2, m_offset), Add(p5, m_offset)); {$ENDIF} m_world.CreateJoint(djd, False); {$IFDEF OP_OVERLOAD} djd.Initialize(body1, body2, p3 + m_offset, p4 + m_offset); {$ELSE} djd.Initialize(body1, body2, Add(p3, m_offset), Add(p4, m_offset)); {$ENDIF} m_world.CreateJoint(djd, False); {$IFDEF OP_OVERLOAD} djd.Initialize(body1, m_wheel, p3 + m_offset, wheelAnchor + m_offset); {$ELSE} djd.Initialize(body1, m_wheel, Add(p3, m_offset), Add(wheelAnchor, m_offset)); {$ENDIF} m_world.CreateJoint(djd, False); {$IFDEF OP_OVERLOAD} djd.Initialize(body2, m_wheel, p6 + m_offset, wheelAnchor + m_offset); {$ELSE} djd.Initialize(body2, m_wheel, Add(p6, m_offset), Add(wheelAnchor, m_offset)); {$ENDIF} m_world.CreateJoint(djd); rjd := Tb2RevoluteJointDef.Create; {$IFDEF OP_OVERLOAD} rjd.Initialize(body2, m_chassis, p4 + m_offset); {$ELSE} rjd.Initialize(body2, m_chassis, Add(p4, m_offset)); {$ENDIF} m_world.CreateJoint(rjd); end; 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 procedure TJansenWalker . CreateLeg ( s : PhysicsFloat ; const wheelAnchor : TVector2 ) ; var p1 , p2 , p3 , p4 , p5 , p6 : TVector2 ; vertices : array [ 0..2 ] of TVector2 ; poly1 , poly2 : Tb2PolygonShape ; fd1 , fd2 : Tb2FixtureDef ; bd1 , bd2 : Tb2BodyDef ; body1 , body2 : Tb2Body ; djd : Tb2DistanceJointDef ; rjd : Tb2RevoluteJointDef ; begin p1 : = MakeVector ( 5.4 * s , - 6.1 ) ; p2 : = MakeVector ( 7.2 * s , - 1.2 ) ; p3 : = MakeVector ( 4.3 * s , - 1.9 ) ; p4 : = MakeVector ( 3.1 * s , 0.8 ) ; p5 : = MakeVector ( 6.0 * s , 1.5 ) ; p6 : = MakeVector ( 2.5 * s , 3.7 ) ; fd1 : = Tb2FixtureDef . Create ; fd2 : = Tb2FixtureDef . Create ; fd1 . filter . groupIndex : = - 1 ; fd2 . filter . groupIndex : = - 1 ; fd1 . density : = 1.0 ; fd2 . density : = 1.0 ; poly1 : = Tb2PolygonShape . Create ; poly2 : = Tb2PolygonShape . Create ; if s > 0.0 then begin vertices [ 0 ] : = p1 ; vertices [ 1 ] : = p2 ; vertices [ 2 ] : = p3 ; poly1 . SetVertices ( @vertices [ 0 ] , 3 ) ; vertices [ 0 ] : = b2Vec2_zero ; { $ IFDEF OP_OVERLOAD } vertices [ 1 ] : = p5 - p4 ; vertices [ 2 ] : = p6 - p4 ; { $ ELSE } vertices [ 1 ] : = Subtract ( p5 , p4 ) ; vertices [ 2 ] : = Subtract ( p6 , p4 ) ; { $ ENDIF } poly2 . SetVertices ( @vertices [ 0 ] , 3 ) ; end else begin vertices [ 0 ] : = p1 ; vertices [ 1 ] : = p3 ; vertices [ 2 ] : = p2 ; poly1 . SetVertices ( @vertices [ 0 ] , 3 ) ; vertices [ 0 ] : = b2Vec2_zero ; { $ IFDEF OP_OVERLOAD } vertices [ 1 ] : = p6 - p4 ; vertices [ 2 ] : = p5 - p4 ; { $ ELSE } vertices [ 1 ] : = Subtract ( p6 , p4 ) ; vertices [ 2 ] : = Subtract ( p5 , p4 ) ; { $ ENDIF } poly2 . SetVertices ( @vertices [ 0 ] , 3 ) ; end ; fd1 . shape : = poly1 ; fd2 . shape : = poly2 ; bd1 : = Tb2BodyDef . Create ; bd1 . bodyType : = b2_dynamicBody ; bd1 . position : = m_offset ; bd2 : = Tb2BodyDef . Create ; bd2 . bodyType : = b2_dynamicBody ; { $ IFDEF OP_OVERLOAD } bd2 . position : = p4 + m_offset ; { $ ELSE } bd2 . position : = Add ( p4 , m_offset ) ; { $ ENDIF } bd1 . angularDamping : = 10.0 ; bd2 . angularDamping : = 10.0 ; body1 : = m_world . CreateBody ( bd1 ) ; body2 : = m_world . CreateBody ( bd2 ) ; body1 . CreateFixture ( fd1 ) ; body2 . CreateFixture ( fd2 ) ; djd : = Tb2DistanceJointDef . Create ; // Using a soft distance constraint can reduce some jitter. // It also makes the structure seem a bit more fluid by // acting like a suspension system. djd . dampingRatio : = 0.5 ; djd . frequencyHz : = 10.0 ; { $ IFDEF OP_OVERLOAD } djd . Initialize ( body1 , body2 , p2 + m_offset , p5 + m_offset ) ; { $ ELSE } djd . Initialize ( body1 , body2 , Add ( p2 , m_offset ) , Add ( p5 , m_offset ) ) ; { $ ENDIF } m_world . CreateJoint ( djd , False ) ; { $ IFDEF OP_OVERLOAD } djd . Initialize ( body1 , body2 , p3 + m_offset , p4 + m_offset ) ; { $ ELSE } djd . Initialize ( body1 , body2 , Add ( p3 , m_offset ) , Add ( p4 , m_offset ) ) ; { $ ENDIF } m_world . CreateJoint ( djd , False ) ; { $ IFDEF OP_OVERLOAD } djd . Initialize ( body1 , m_wheel , p3 + m_offset , wheelAnchor + m_offset ) ; { $ ELSE } djd . Initialize ( body1 , m_wheel , Add ( p3 , m_offset ) , Add ( wheelAnchor , m_offset ) ) ; { $ ENDIF } m_world . CreateJoint ( djd , False ) ; { $ IFDEF OP_OVERLOAD } djd . Initialize ( body2 , m_wheel , p6 + m_offset , wheelAnchor + m_offset ) ; { $ ELSE } djd . Initialize ( body2 , m_wheel , Add ( p6 , m_offset ) , Add ( wheelAnchor , m_offset ) ) ; { $ ENDIF } m_world . CreateJoint ( djd ) ; rjd : = Tb2RevoluteJointDef . Create ; { $ IFDEF OP_OVERLOAD } rjd . Initialize ( body2 , m_chassis , p4 + m_offset ) ; { $ ELSE } rjd . Initialize ( body2 , m_chassis , Add ( p4 , m_offset ) ) ; { $ ENDIF } m_world . CreateJoint ( rjd ) ; end ;

Download the Box2d Firemonkey physics engine and demos to check out the Theo Jansen Walker.

Or check out the full source code for the Theo Jansen Walker test web demo on Github.