News Mar 20: R.U.B.E v1.7 released. Instanciable objects!
Feb 21: Added RUBE sample loader for Cocos2d-X v3.4.
Feb 17: Funny Fists released in iOS App store and Google Play store. (YouTube video)
2014
May 25: R.U.B.E v1.6 released. Samplers!
May 12: "On-the-fly" resource updating for Cocos2d-x
Apr 22: New blog post: Inverted pendulum control
Apr 7: On-screen logging class for Cocos2d-x
Mar 22: Downhill Supreme 2 released for iOS and Android
Jan 2: YouTube video: Making soft-body wheels in RUBE
2013
Oct 6: Check out Supplyfront RTS, my 7dRTS entry continued.
Sep 26: R.U.B.E v1.5 released. Customizable item labels, snap-to-grid, export filtering, full-text help search.
Sep 18: Added RUBE sample loader for Cocos2d-X.
Sep 16: Updated RUBE sample loader for Cocos2d v2.
Aug 12: RUBE loader for Nape by Zeh Fernando
Aug 7: Added RUBE sample loader for SFML.
Jul 30: Try my MiniLD 7dRTS entry.
Jul 24: Added physics-driven particles tutorial.
Jul 20: New blog post: rendering solid ground (as in Downhill Supreme)
Jul 18: R.U.B.E v1.4 released. Command-line interface for batch jobs, hotkeys for scripts, better script management.
May 22: Downhill Supreme is out now! (iOS)
Apr 2: R.U.B.E v1.3 released. Collision bitplane editing, Cocos2d-iphone loader, usability improvements.
Mar 11: R.U.B.E v1.2 released. Now supports weld, friction, motor joints and automatic image reloading.
Mar 10: RUBE loader for libGDX by Tim Scott
Jan 28: New blog post: a functional combustion engine!
Jan 22: New blog post: wind tunnel
Jan 20: Added explosions tutorial.
Jan 16: Added buoyancy tutorial.
Jan 16: AndEngine sample project to load RUBE scene by Bart Hirst
Jan 14: R.U.B.E v1.1 released, now supports custom properties.
Jan 1: All basic tutorials are now available in Chinese at ohcoder.com Huge thankyou to @OhCoder!
2012
Dec 23: Discussion forums have been set up
Dec 4: R.U.B.E v1.0 is ready !
Dec 2: YouTube video: Box2D pendulum clock
Nov 26: New blog post: rocket platform thingy
Nov 25: Video of R.U.B.E scenes in Chipmunk.
Nov 23: A sample project to load R.U.B.E scenes into Chipmunk physics! Details here.
Nov 14: An XCode sample project to load R.U.B.E scenes on iOS is now available. Details here.
Nov 11: A Java version of b2dJson is now available, based on JBox2D.
Nov 6: A Javascript version of b2dJson based on box2dweb is now available. Demo here!
Nov 2: The full specification of the JSON format used by b2dJson can be found here: b2dJson file structure
Oct 28: YouTube video: 2-minute ragdoll in R.U.B.E Box2D editor
Sep 29: YouTube video: R.U.B.E Box2D editor usage example

Box2D C++ tutorials - Hovercar suspension

Last edited: July 14 2013


Halt! You should have a good understanding of
the basic tutorials before venturing further.


Suspension calculation


Wheeled vehicles typically have damped spring suspension which means that the distance between the chassis and the wheel will tend toward a specific rest position, but is limited in how fast it can move there. This kind of movement is simulated very well by Box2D's wheel joint. The distance joint also has similar behaviour to the linear part of the wheel joint. If you want to keep two bodies in alignment and with a suspension-like movement you could use a prismatic joint and a distance joint together.

But how about if you don't want any joints at all between the two bodies? A good example of this is a 'hovercar' type behavior where a body is suspended above the ground as if on a dampened spring when it is close to the ground, but there is nothing to stop it from moving further upwards. This means that the body could be above anything so we don't really want to make any joints between it and the ground.

In this topic we'll look at what forces should be applied to a body to get this kind of movement. We'll use a raycast to find out what is below the body and how far away it is. Although the example will look at a 'hovercar' scenario it's worth noting that you can do the same thing for player characters where the character is made from two bodies joined by a prismatic joint, instead of using a distance joint. The nice thing about that method is you can alter the target height to get the character to crouch, and you can customize the way the height is changed (eg. make it quicker to crouch than to stand up, etc).


The basics


After finding out how far the body is from the ground, all we really need to do is apply a force of the right magnitude. When the body is close to its target height we don't want it to move much so the force will be very small - think of this as a spring at its rest length - and when the body is close to the ground the force should be large. Hovercar suspension A common way for calculating the force of a spring is to multiply the distance from the rest position by a 'spring constant' which specifies how strong the spring is. So to achieve this it's just a matter of calling ApplyForce with a force calculated from the distance below the target height. At this point let's do a little coding to get us that far for now.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  {//ground
      b2EdgeShape edgeShape;
      edgeShape.Set( b2Vec2(-20,0), b2Vec2(20,0) );
  
      b2BodyDef bodyDef;
      m_world->CreateBody(&bodyDef)->CreateFixture(&edgeShape, 0);
  }
  
  {//hovercar
      b2BodyDef bodyDef;
      bodyDef.type = b2_dynamicBody;
      bodyDef.fixedRotation = true;
      bodyDef.position.Set(0,10);
      m_hovercarBody = m_world->CreateBody(&bodyDef);
  
      b2PolygonShape polygonShape;
      polygonShape.SetAsBox(2,0.5f);//4x1 box
      b2FixtureDef fixtureDef;
      fixtureDef.shape = &polygonShape;
      fixtureDef.density = 1;
  
      m_hovercarBody->CreateFixture(&fixtureDef);
  }
Now in the Step function we can cast a ray downwards to find the ground and apply a force depending on how far away it is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  float targetHeight = 3;
  float springConstant = 100;
  
  //make the ray at least as long as the target distance
  b2Vec2 startOfRay = m_hovercarBody->GetPosition();
  b2Vec2 endOfRay = m_hovercarBody->GetWorldPoint( b2Vec2(0,-5) );
  
  HovercarRayCastClosestCallback callback;
  m_world->RayCast(&callback, startOfRay, endOfRay);
  
  if ( callback.m_hit ) {
      float distanceAboveGround = (startOfRay - callback.m_point).Length();
      
      //dont do anything if too far above ground
      if ( distanceAboveGround < targetHeight ) {
          float distanceAwayFromTargetHeight = targetHeight - distanceAboveGround;
          m_hovercarBody->ApplyForce( b2Vec2(0,springConstant*distanceAwayFromTargetHeight),
                                          m_hovercarBody->GetWorldCenter() );
      }
  }
Hovercar suspension

The extra touch


Well that's a good start, but you'll notice that with only a spring to affect the body, it keeps bouncing forever. This is because there is no damping on the body which means that even though we are reducing the force applied as it gets closer to the target height, all the accumulated force we've applied in the previous time steps has not yet been countered by gravity.

One way to fix this is to use SetLinearDamping on the body, but this would cause the motion to be affected all the time, even when the body is way above the ground. We could also manually apply a vertical damping to the velocity of the body when it is within range of the ground, but really what we're trying to do is scale down the force, not damp velocity.

A better way is the 'look-ahead' technique used in the rotate to angle topic, to take into account the current velocity of the body. This will cause the calculated force to scale down earlier as the rest position is approached. Let's try that.
1
2
3
4
  //after checking if distanceAboveGround < targetHeight
  //replace distanceAboveGround with the 'look ahead' distance
  //this will look ahead 0.25 seconds - longer gives more 'damping'
  distanceAboveGround += 0.25f * m_hovercarBody->GetLinearVelocity().y;
This leaves the velocity of the body otherwise untampered with, so that it will interact normally with other influences in the world. Hovercar suspension

The finale


If you are very observant you might have noticed that the body does not come to rest at the height we wanted. This is because as we push up, gravity pushes down in a continual struggle. Rather than coming to rest at the position where the hover force we're applying would be zero, the body will come to rest at the position where our hover force and gravity are equal. So for the example above, rather than settling at a height of 3 units, the body will settle at a height of 2.6 units.

This could be countered by altering the spring constant, but to really get at the root of the problem it would be cleaner to cancel gravity for the body:
1
2
3
4
  //after checking if distanceAboveGround < targetHeight
  //negate gravity
  m_hovercarBody->ApplyForce( m_hovercarBody->GetMass() * -m_world->GetGravity(),
                                  m_hovercarBody->GetWorldCenter() );


Other considerations


With the simple example we have here, you'll find that the body is very sensitive to the surface contours, because there is only one ray. For example, in the situation below the ray can fit between the two boxes and is not seeing a good representation of the ground below. You would probably want to have more than one ray along the bottom of the body and take the average/minimum of them or something like that to avoid this problem. Hovercar suspension

Source code


Here is the source code for those who would like to try it out for themselves. This is a 'test' for the testbed, based on Box2D v2.3.0.
Box2D sticky projectiles
Testbed test: iforce2d_HovercarSuspension.h

Linux binary
Windows binary

YouTube video


Not seeing the any comments below? Try changing the 'https' in the page URL to just 'http'
Click here: http://www.iforce2d.net/b2dtut/suspension