Handling multiple gravity sources

Some games require handling gravity from multiple variable sources. In this recipe, we'll handle this and create a simple miniature solar system to demonstrate it using ThrusterControl from the Building a rocket engine recipe. To (greatly) simplify the relation between the planets, they won't affect each other with their gravity, but only the ship. It will also be made in a 2D-asteroids-like fashion, although the gravity would still apply for a 3D game.

We'll add some basic controls to rotate the ship to the left and right, and you can use the thruster to make the ship move forward.

How to do it...

Apart from ThrusterControl, we'll create two more small classes and an application class that joins everything together. Let's start with a class that represents the player's ship. This will consist of the following six steps:

  1. Create a new class called SpaceShip, which has a Node field called shipNode in it.
  2. In the constructor, we set up the physics for it by creating a new RigidBodyControl instance with BoxCollisionShape. To create it in a way that it is affected by gravity, we also give it a mass of 1 that will be supplied in the constructor as follows:
    RigidBodyControl control = new RigidBodyControl(new BoxCollisionShape(new Vector3f(1, 1, 1)), 1);
    shipNode.addControl(control);
  3. Now, we create a Node instance called thruster. We also set the name of Node to Thruster for the control to find it automatically, as shown in the following line of code:
    Node thruster = new Node("Thruster");
  4. We set localTranslation to be at one of the sides of the spaceship and attach it to shipNode, as follows:
    thruster.setLocalTranslation(-1, 0, 0);
    shipNode.attachChild(thruster);
  5. Then, we rotate the ship's spatial so that it's facing sideways:
    shipNode.rotate(0, FastMath.PI, 0);
  6. Finally, we add a new ThrusterControl instance to the spaceship's spatial.

That's it for the SpaceShip class. Now, we create a class for our planets, as follows:

  1. We start off by defining a class called StellarBody, which extends AbstractControl. The StellarBody class has four float fields: size, speed, orbit, and cycle.
  2. The constructor takes three of these (size, speed, and orbit) as the input, as shown in the following code:
    public StellarBody(float orbit, float speed, float size)
  3. We override the setSpatial method and add RigidBodyControl to the supplied spatial with SphereCollisionShape, using size as the radius and 0 for mass:
    RigidBodyControl rigidBody = new RigidBodyControl(new SphereCollisionShape(size), 0f);
    rigidBody.setGravity(Vector3f.ZERO);
    spatial.addControl(rigidBody);
  4. In the controlUpdate method, we make it move along its orbit by increasing the speed of the cycle by multiplying it by tpf, as follows:
    cycle += (speed * tpf)  % FastMath.TWO_PI;
  5. Then, we set the actual position of the planet along the orbit using the sin and cos methods of the FastMath class:
    float x = FastMath.sin(cycle);
    float z = FastMath.cos(cycle);
  6. We multiply the result by the orbit and set localTranslation of the spatial to the new location as follows:
    spatial.setLocalTranslation(x * orbit, 0, z * orbit);
  7. Then, we also need to set physicsLocation of RigidBodyControl to the same location.
  8. We need a new method, getGravity, that will take the position of the ship as an input Vector3f.
  9. The method begins by subtracting the input position by worldTranslation, to get the position of the ship relative to the StellarBody class, as follows:
    Vector3f relativePosition = spatial.getWorldTranslation().subtract(position);
  10. The result is normalized and then modified by a formula to get a suitable gravity. This value is returned to the calling method, as follows:
    relativePosition.normalizeLocal();
    return relativePosition.multLocal(size * 1000 / relativePosition.lengthSquared());

To test all of this, we need to add a few things to SimpleApplication. To do this, perform the following set of steps:

  1. First of all, we implement AnalogListener.
  2. We add an ArrayList<StellarBody> list called gravitationalBodies.
  3. In the simpleInitApp method, we should begin by initializing bulletAppState and set up some controls for the spaceship. We add actions to rotate the spaceship to the left and right as well as fire the ship's thruster, as follows:
    String[] mappings = new String[]{"rotateLeft", "rotateRight", "boost"};
    inputManager.addListener(this, mappings);
    inputManager.addMapping("boost", new KeyTrigger(KeyInput.KEY_SPACE));
    inputManager.addMapping("rotateLeft", new KeyTrigger(KeyInput.KEY_LEFT));
    inputManager.addMapping("rotateRight", new KeyTrigger(KeyInput.KEY_RIGHT));
  4. Since it's a 2D representation, we move the camera some distance up and make it look as if it is at the center of the world. This can be implemented as follows:
    cam.setLocation(new Vector3f(0, 300f, 0));
    cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
  5. We create an instance called ship of SpaceShip and attach its geometry to rootNode and physicsSpace of bulletAppState.
  6. Now we can create a number of StellarBody instances using the following steps:
    1. For each instance, we should create a Geometry class with a Sphere shape that will have the same radius as the size we will supply to the StellarBody control.
    2. The Geometry class should both be attached to rootNode and physicsSpace of bulletAppState.
    3. We add StellarBody as a control to the Geometry class and the gravitationalBodies list.
  7. Inside the update method, we have to take into account the gravity of the StellarBody instances.
  8. First, we define a new Vector3f instance called combinedGravity.
  9. Then, we loop through our gravitationalBodies list and apply the following line of code to apply the gravity to combinedGravity:
    combinedGravity.addLocal(body.getGravity(ship.getSpatial().getWorldTranslation()));
  10. Finally, we call the ship.setGravity(combinedGravity); statement.

How it works...

Due to the extreme difficulty in creating a stable solar system with more than three bodies, StellarBody controls the need to have a static orbit around the center of the system. Using 0 as mass ensures that they aren't affected by gravity. The orbit field represents the orbit's distance from the center of the system, and it will rotate around the center using speed as a factor. The cycle field stores information on how far along its orbit it has come, and will reset once it reaches two PI (a full circle).

The getGravity method returns the gravity relative to the position that is supplied, which in this case is the location of the ship. It first determines the direction and then applies the gravity based on the distance between the two.

By using the gravitationalBodies list, we have a dynamic way to simply add up all the gravitational forces in the system to a single Vector3f object, which we then apply to the spaceship in the update method of the application.

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

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