Assembling bodies from fixtures

So far, we have only used the AndEngine class, PhysicsFactory, to create rectangle (box) bodies. It is the simplest and most straightforward way to create bodies. The whole creation of a body remains hidden.

A body consists of one or more fixtures. Let's create a slightly more complicated body by manually creating three fixtures and putting them together to form a body. We will first create fixtures for the head, torso, and body, just to illustrate how to create multiple fixtures, but we are going to see how filtering works on the example of these fixtures as well.

Tip

There are more ways of creating fixtures. One of the more automated ways is to use an external editor such as R.U.B.E. and a loader that will load the data to AndEngine. However, such a loader exists only as an unofficial extension.

Here's a screenshot showing how the final body should look in the game:

Assembling bodies from fixtures

Creating an empty body

First, we need to create an empty body. The body is in fact just a virtual holder for fixtures. When we create an empty body and add it to the game, nothing will show and no collisions will happen. We are going to change the createPlayer() method of the PlayerFactory class. Here's the new code:

  public Player createPlayer(float x, float y) {
    Player player = new Player(x, y, ResourceManager.getInstance().playerTextureRegion, vbom);
    player.setZIndex(2);
    
    BodyDef bodyDef = new BodyDef();
    bodyDef.type = BodyType.DynamicBody;
    bodyDef.position.x = x / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT;
    bodyDef.position.y = y / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT;
    
    Body playerBody = physicsWorld.createBody(bodyDef);
    
    playerBody.setLinearDamping(1f);
    playerBody.setFixedRotation(true);
    playerBody.setUserData(player);
    physicsWorld.registerPhysicsConnector(new PhysicsConnector(player, playerBody));
    
    player.setBody(playerBody);    
    return player;
  }

We are defining a BodyDef object that has a position. In the physics world, the units are meters, not pixels. Therefore, we use the pixel-to-meter conversion. We specified that our BodyDef object defines a dynamic body. Other than this, we keep the code unchanged. Next, we are going to add the fixtures. Each fixture can have different properties. Let's make use of that.

The head fixture

What we want to achieve is almost perfect collisions with the enemy flies. Moreover, right now, the whole body collides with a platform, making it jump even when the head touches it, and this is something we don't really want. Only the legs should propel the player upwards. What we can do is to turn the head into a circular shape sensor.

Sensors do not generate the pre-solve and post-solve events in the contact listener. This makes sense because a sensor can't influence its environment; it's immaterial. However, it will still generate the begin and end contact events that we use to detect the contact with enemies.

Here's a method that will create a circular fixture where the player's head should be. It belongs to the PlayerFactory class:

  private void createHead(Body body) {
    FixtureDef headFixtureDef = PhysicsFactory.createFixtureDef(1f, 0f, 1f, true);
    CircleShape circle = new CircleShape();
    circle.setRadius(32 / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT);
    circle.setPosition(new Vector2(0, 12 / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT));
    headFixtureDef.shape = circle;
    body.createFixture(headFixtureDef);
  }

First, we create a fixture definition in the same way we created it before. Then, we create a circle shape, set its radius in meters, and set its position to the place where the character's head is, again in meters. Then, we assign this shape to the fixture definition and call the correct method in the Body class to create the fixture itself.

Before we add it to the final physics body, let's create the other fixtures as well.

Creating the torso

Circle shapes are easy; they only have a radius and position. To create every other shape, a polygon shape must be created. Polygons are tricky. They must be defined by vertices. Only convex polygon shapes are allowed; therefore, other shapes must be assembled from multiple fixtures. The order of vertices is defined by a simple rule. The "outside" of the shape is always on the right of each side. This means we have to define our vertices in a counterclockwise order, as shown in the following diagram:

Creating the torso

Note

Creating the vertices in the wrong order or creating a non-convex (concave) shape will most likely make the game crash with an error.

Creating the vertices manually is a bit tedious. Let's see how it looks for our trapezoid-shaped torso:

  private void createTorso(Body body) {
    FixtureDef torsoFixtureDef = PhysicsFactory.createFixtureDef(1f, 0f, 0.5f, true);
    
    PolygonShape middleBox = new PolygonShape();

    final float halfWidth = 30 / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT;
    final float halfHeight = 8 / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT;
    final float yShift =  - 30 / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT;
    Vector2[] vertices = new Vector2[4];
    vertices[0] = new Vector2(halfWidth, halfHeight + yShift);
    vertices[1] = new Vector2(-halfWidth, halfHeight + yShift);
    vertices[2] = new Vector2(-halfWidth * 0.75f, -halfHeight + yShift);
    vertices[3] = new Vector2(halfWidth * 0.75f, -halfHeight + yShift);
    middleBox.set(vertices);
    
    torsoFixtureDef.shape = middleBox;
    
    body.createFixture(torsoFixtureDef);
  }

The code is not complicated, just long. First, we create the fixture definition and again we want the torso to actually be a sensor. We create a PolygonShape object and set four vertices to it. The positions are defined using variables called halfWidth and halfHeight, so we can easily change all vertices at once. The following figure explains what they mean:

Creating the torso

The following diagram shows the final points that are plotted:

Creating the torso

Creating the legs

Finally, we create the legs. We will use the same method using the polygonal shape as we used when creating the torso. However, this time we will create a rectangle, and we can use a convenience method, setAsBox(), to define the vertices for us.

Also, we don't want the legs to be a sensor, but rather a regular material part of the body. We are also going to increase the density of the legs to make the final body about the same weight as the original big box. This can be done as follows:

  private void createLegs(Body body) {
    FixtureDef legsFixtureDef = PhysicsFactory.createFixtureDef(4f, 0.2f, 1f, false);  
    
    PolygonShape legsBox = new PolygonShape();
    legsBox.setAsBox(20 / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT,
        4 / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT,
        new Vector2(0, -44 / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT),
            0);
    legsFixtureDef.shape = legsBox;
    
    body.createFixture(legsFixtureDef);
  }    

Tip

There is a special polygonal shape—the edge. It is simply one edge of a polygon defined by two vertices. There is a method, setAsEdge(), to create this polygonal shape.

Assembling the body

Finally, we assemble the body parts together. This is done in the createPlayer() method in the PlayerFactory class, the same place where we created the empty body. We simply call the three methods that we just defined, as follows:

  public Player createPlayer(float x, float y) {
    Player player = new Player(x, y, ResourceManager.getInstance().playerTextureRegion, vbom);
    player.setZIndex(2);
    
    BodyDef bodyDef = new BodyDef();
    bodyDef.type = BodyType.DynamicBody;
    bodyDef.position.x = x / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT;
    bodyDef.position.y = y / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT;
    
    Body playerBody = physicsWorld.createBody(bodyDef);
    
    createTorso(playerBody);
    createHead(playerBody);
    createLegs(playerBody);

    playerBody.setLinearDamping(1f);
    playerBody.setFixedRotation(true);
    playerBody.setUserData(player);
    physicsWorld.registerPhysicsConnector(new PhysicsConnector(player, playerBody));
    
    player.setBody(playerBody);    
    return player;
  }

We can run the game now. It will work almost the same as before, just with more precise collisions and the jumping will only happen when the player actually touches the platform top with legs.

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

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