Self-balancing using RotationalLimitMotors

Many games today use a blend of animations and physics to create realistic movement. For animated characters, this revolves around balance. It could take the shape of a runner who leans inwards through a curve to counter the centrifugal force. Creating a system like this is not easy and requires a lot of tweaking. In this recipe, we'll look into some of the fundamentals of this, and we'll create a new Control class that will try to balance itself using the rotational motors of SixDofJoint.

Note

Six Degrees of Freedom (SixDof) relates to the six ways the joint can rotate: +x, -x, +y, -y, +z, and -z. One way it differs from a point2point joint is that in addition, it also has motors for each axis, which makes it possible for it to also apply force.

How to do it...

To simulate balancing, we will begin by creating the upper body of a stickman-shape figure with a torso and two rigid arms. To do this, perform the following set of steps:

  1. First of all, we should set up an application with BulletAppState.
  2. In the simpleInitApp method, we create a small square Box Geometry to be the waist of the character. It can be 0.25f in all the axes.
  3. We add RigidBodyControl to it with 0 in mass since it shouldn't move.
  4. Then, we create an oblong box to be the torso and place it above the waist. It should have RigidBodyControl with 1 in mass and BoxCollisionShape should be of the same size as the geometry:
    torso = new Geometry("Torso", new Box(0.25f, 2f, 0.25f);
    RigidBodyControl torsoRigidBody = new RigidBodyControl(new BoxCollisionShape(...), 1f);
    ...
    torsoRigidBody.setPhysicsLocation(new Vector3f(0, 4.25f, 0));
  5. Next, we create SixDofJoint between the waist and torso and afterwards add it to physicsSpace as follows:
    SixDofJoint waistJoint =  new SixDofJoint(waistRigidBody, torsoRigidBody, new Vector3f(0, 0.25f, 0), new Vector3f(0, -2.25f, 0f), true);
  6. We should limit the joint so that it can't rotate on any axes other than the x axis, and it shouldn't be able to rotate too much. We can use the following setAngularLowerLimit and setAngularUpperLimit methods for this:
    waistJoint.setAngularLowerLimit(new Vector3f(-FastMath.QUARTER_PI * 0.3f, 0, 0));
    waistJoint.setAngularUpperLimit(new Vector3f(FastMath.QUARTER_PI * 0.3f, 0, 0));
  7. Next, we create one of the arms.
  8. We create one of the arms by placing it at the same location as that of the torso and giving it a size of Vector3f(0.25f, 0.25f, 2f), making it stretch out sideways, as shown in the following code snippet:
    leftArm = new Geometry("Left Arm", new Box(0.25f, 0.25f, 2f);
    RigidBodyControl leftArmRigidBody = new RigidBodyControl(new BoxCollisionShape(...), 1f);
    ...
    leftArmRigidBody.setPhysicsLocation(new Vector3f(0, 4.25f, 0));
  9. We create another SixDofJoint for it using the pivot points of Vector3f(0, 2.5f, 0.25f) and Vector3f(0, 0, -2.5f), offsetting it some distance to the side of the torso's spatial.
  10. Then, we set the angular limits of the joint to Vector3f(0, 0, 0) and Vector3f(FastMath.QUARTER_PI, 0, 0).
  11. We repeat the previous three steps to create the opposite arm, but we'll reverse the offset values to make the arm protrude in the opposite direction of the torso.

We now have the basics done for our recipe. Running it should show the character slumping to one side with the arms stretched out to the sides. Now, we can begin with balancing by performing the following steps:

  1. We create a new class called BalanceControl, extending AbstractControl.
  2. It should have a SixDofJoint field called joint and a RotationalLimitMotor field called motorX.
  3. Create a setJoint method.
  4. Inside this method, after setting the joint, we also populate motorX with one of the RotationalLimitMotor instances, as follows:
    motorX = joint.getRotationalLimitMotor(0);
  5. Inside the controlUpdate method, we get bodyA from the joint and store it in PhysicsRigidBody. This is the torso:
    PhysicsRigidBody bodyA = joint.getBodyA();
  6. We get the current rotation of bodyA to see how much it pivots. We then convert the rotation to angles and store them as follows:
    float[] anglesA = new float[3];
    bodyA.getPhysicsRotation().toAngles(anglesA);
  7. We then store angles[0] in a float variable called x.
  8. If x is more than 0.01f or less than -0.01, we should start motorX and rotate it to compensate for the pivot, as follows:
    motorX.setEnableMotor(true);
    motorX.setTargetVelocity(x*1.1f);
    motorX.setMaxMotorForce(13.5f);
  9. Otherwise, we turn off the motor as follows:
    motorX.setTargetVelocity(0);
    motorX.setMaxMotorForce(0);

How it works...

Running the result, we should see the stickman desperately trying to stay upright while flailing his arms up and down. The reason is that getting the forces right when balancing can be very difficult. With values that are too high, the stickman will constantly overshoot the target and instead rotate in the other direction. With values that are too low, it won't have the strength to get upright. With some further tweaking to targetVelocity and maxMotorForce, we might be able make him stable.

We started by creating the basic shape of a figure that would try to keep the balance. The waist was made to not be affected by the physics, so it could be a solid point. We then added a torso and two arms, resulting in a center of mass somewhere in the upper part of the torso. By placing each of the body parts at some distance from each other with the joints, we give them more freedom of movement.

The BalanceControl class we created has one simple strategy. It looks for the torso (bodyA)'s rotation along the x axis, and tries to keep it as close to 0 as possible. If it notices that it's anything but near 0, it will try to move the arms, shifting the center of the mass to the opposite direction.

Despite the low number of components, getting it all to balance out is really difficult! Having more components, such as a whole human skeleton, requires a much more advanced strategy, with body parts moving in a synchronized fashion, rather than they trying to do so individually.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.251.128