Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

Adding some fun : Light Obstacles

The next step in the development of this game is adding several types of obstacles to enhance the gameplay. I’ll start by adding light obstacles, that hero can push.

For that, let’s start by creating the ObstacleLight, that is obviously a subclass of Obstacle.

Here is the ObstacleLight.java :

public class ObstacleLight extends Obstacle{

    public ObstacleLight(World world, Camera camera, MapObject rectangleObject) {
        super(world, camera, rectangleObject);
        
        body.setUserData("ObstacleLight");
        body.getFixtureList().get(0).setUserData("ObstacleLight");
        
        //Weight
        if(rectangleObject.getProperties().get("Weight") != null){
            body.getFixtureList().get(0).setDensity(
                    body.getFixtureList().get(0).getDensity() * Float.parseFloat(rectangleObject.getProperties().get("Weight").toString())
            );
            body.resetMassData();
        }
    }
    
    @Override
    public BodyType getBodyType(){
        return BodyType.DynamicBody;
    }
}

Differences with the Obstacle class :

  • The UserData of the body and fixture ane "ObstacleLight" instead of "Obstacle"
  • The BodyType is a DynamicBody, which means that the body will move under the action of forces (collision, gravity...)
  • The ObstacleLight can have various weights, depending on the value we give to the property "Weight" in the Tiled map editor. If we don’t put a property "Weight", the ObstacleLight will have a default weight, which is the same weight as the hero.

Talking about the weight, and the density, it’s time to explain the way I deal with this :
In Box2D, to create a Body, you first need to create a Fixture. The Body will be then created from this fixture, and some other properties you put in the BodyDef. Each Fixture will have a weight. But you can’t set the weight in Box2D. Instead you set the density of the Fixture with
fixtureDef.density
and the weight is calculated like this : weight = density * area of the shape.

The shape is also included in the Fixture by

fixtureDef.shape = shape

For that you use a Shape you created earlier, that can be a PolygonShape or a CircleShape.

Thus, for example, for a rectangle, the weight will be : weight = density * width * height.

The bigger, the heavier. Which is logical.

But, for my game I want a way to control precisely the weight of every Obstacle in my game, without having to do some calculation. Therefore, in my game, EVERY bodies will have the exact same weight by default. For that, I create a constant in the GameConstants, for the density :

public static float DENSITY = 1.0f;

And the default density of the fixture of every object is defined like this (Example of a rectangle box):

fixtureDef.density = (float)(GameConstants.DENSITY/(width * height));

I divide GameConstants.Density by the area of the fixture : The bigger, the least dense, and the weight is stable.


Why the hell do I need to work like that ?

Because it’s very easy to manage. For example, when I put obstacles in my level editor, and I want my hero to interact with the object, I don’t need to calculate what density I need to give to each obstacle, according to the difficulty I want the hero having to push these objects. With my system, every object will have the same weight as the hero by default. Thus I know that the hero can push them with a moderate effort, meaning, a moderate fuel consumption. If I want the hero to have trouble pushing an object I only need to add a property "Weight", and attribute it a high value, say 10, and I’ll know that this object will weigh 10 times more than the hero. If I want a really light object, I’ll attribute a low value to "Weight", say 0.1f, and I’ll know that the hero can displace it with no effort. And I can do that independently of the size of the object, which gives a great liberty for designing levels.


Then we need to modify the TiledMapReader to recognize the ObstacleLight

Thus, the for loop of the TiledMapReader.java becomes :

for (RectangleMapObject rectangleObject : objects.getByType(RectangleMapObject.class)) {
            if(rectangleObject.getProperties().get("Type") != null){
                //End of the level
                if(rectangleObject.getProperties().get("Type").equals("Exit")){
                    Exit finish = new Exit(world, camera, rectangleObject);
                    obstacles.add(finish);
                }
                //Light obstacles
                else if(rectangleObject.getProperties().get("Type").equals("Light")){
                    ObstacleLight obstacle = new ObstacleLight(world, camera, rectangleObject);
                    obstacles.add(obstacle);
                }
            }
            else{
                Obstacle obstacle = new Obstacle(world, camera, rectangleObject);
                obstacles.add(obstacle);
            }
        }

All that remains is to add ObstacleLight to the level in Tiled

For that, you only need to create rectangles and give a property "Type" with the value "Light". The created ObstacleLight will have the same weight as the hero by default, but just add a property "Weight" with any value to modify their weight.

And here is the result !


Nice, this looks like a lot of fun!

Thanks !