Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

Animated Obstacles : Piston (1/2)

Next type of Obstacle : Piston

With this Obstacle, we increase the complexity in our code : The piston needs 2 fixtures (Head and Axis), and it needs to move. This will give us the opportunity to create traps for the hero, and also the opportunity to have another losing condition : Hero gets crushed.

Here is a short video showing the process of creating the Pistons with Tiled and the result after running the code :

Creating the Piston in Tiled

In Tiled, for each Piston, we need to draw 2 Fixtures, one for the Head and one for the Axis of the Piston. The TiledMapReader will need to know that the 2 Rectangles we drew are connected to form a Piston, otherwise, the TiledMapReader will convert these 2 Rectangles in 2 simple Obstacles, it is to say, 2 walls.

To show that these 2 Rectangles form a Piston, will simply add a property called "Group" to these 2 Rectangles, and we’ll attribute the same value to, say 1, to the property "Group" of the 2 Rectangles. Of course, if we create several Pistons, for example 4 Pistons, we will have Group 1, Group 2, Group 3, and Group 4.

Now let’s see the code

Here is the code for ObstaclePiston.java :

public class ObstaclePiston extends Obstacle{

    private PolygonShape shape2;
    private float width2, height2, posX2, posY2;
    private float speed = 10;
    private float delay = 0;
    private Vector2 initialPosition, finalPosition, direction;
    private Vector2[] travel;
    private int step = 1;
    
    public ObstaclePiston(World world, OrthographicCamera camera, MapObject rectangleObject1, MapObject rectangleObject2) {
        super(world, camera, rectangleObject1);
        
        //Delay before activation
        if(rectangleObject1.getProperties().get("Delay") != null){
            delay = Float.parseFloat((String) rectangleObject1.getProperties().get("Delay"));
        }
        else if(rectangleObject2.getProperties().get("Delay") != null){
            delay = Float.parseFloat((String) rectangleObject2.getProperties().get("Delay"));
        }
        
        //Motion speed
        if(rectangleObject1.getProperties().get("Speed") != null){
            speed = Float.parseFloat((String) rectangleObject1.getProperties().get("Speed"));
        }
        else if(rectangleObject2.getProperties().get("Speed") != null){
            speed = Float.parseFloat((String) rectangleObject2.getProperties().get("Speed"));
        }
        
        //Creation of the second Fixture
        Rectangle rectangle2 = ((RectangleMapObject) rectangleObject2).getRectangle();
        
        width2 = (rectangle2.width/2) * GameConstants.MPP;
        height2 = (rectangle2.height/2) * GameConstants.MPP;
        posX2 = (rectangle2.x + rectangle2.width/2) * GameConstants.MPP;
        posY2 = (rectangle2.y + rectangle2.height/2) * GameConstants.MPP;
        
        shape2 = new PolygonShape();
        shape2.setAsBox(width2, height2, new Vector2(posX2 - posX, posY2 - posY), 0);
        
        bodyDef.position.set(new Vector2((rectangle2.x + rectangle2.width/2) * GameConstants.MPP, (rectangle2.y + rectangle2.height/2) * GameConstants.MPP));
        
        fixtureDef = new FixtureDef();
        fixtureDef.shape = shape2;
        fixtureDef.density = 0;  
        fixtureDef.friction = 0.5f;  
        fixtureDef.restitution = 0.5f;
        
        body.createFixture(fixtureDef);  
        body.setUserData("ObstaclePiston");        
        shape2.dispose();

        if(rectangleObject1.getProperties().get("Part").equals("Head")){
            body.getFixtureList().get(0).setUserData("ObstaclePiston");
            body.getFixtureList().get(1).setUserData("Obstacle");
            
            //initialPosition = body.getPosition();
            initialPosition = new Vector2(posX, posY);
            if(posX == posX2)
                finalPosition = new Vector2(initialPosition.x, initialPosition.y + rectangle2.height * Math.signum(posY2 - posY) * GameConstants.MPP);
            else
                finalPosition = new Vector2(initialPosition.x + rectangle2.width * Math.signum(posX2 - posX) * GameConstants.MPP, initialPosition.y);
        }
        else {
            body.getFixtureList().get(0).setUserData("Obstacle");
            body.getFixtureList().get(1).setUserData("ObstaclePiston");

            //initialPosition = body.getPosition();
            initialPosition = new Vector2(posX, posY);
            if(posX == posX2)
                finalPosition = new Vector2(initialPosition.x, initialPosition.y + rectangle.height * Math.signum(posY - posY2) * GameConstants.MPP);
            else
                finalPosition = new Vector2(initialPosition.x + rectangle.width * Math.signum(posX - posX2) * GameConstants.MPP, initialPosition.y);
        }

        travel = new Vector2[2];
        travel[0] = initialPosition;
        travel[1] = finalPosition;

        direction = new Vector2();
        direction = new Vector2(travel[step].x - body.getPosition().x, travel[step].y - body.getPosition().y);
    }
    
    @Override
    public BodyType getBodyType(){
        return BodyType.KinematicBody;
    }

    @Override
    public void active(){
        if(delay > 0){
            delay -= Gdx.graphics.getDeltaTime();
        }
        else{
            if(!new Vector2(travel[step].x - body.getPosition().x, travel[step].y - body.getPosition().y).hasSameDirection(direction)){            
                if(step > 0)
                    step = 0;
                else step = 1;
                
                direction = new Vector2(travel[step].x - body.getPosition().x, travel[step].y - body.getPosition().y);
            }
            body.setLinearVelocity(direction.clamp(speed, speed)); 
        }
    }
}

About this code :

  • ObstaclePiston is a subclass of Obstacle
  • After super(world, camera, rectangleObject1); , you can see a bunch of code lines to read the different properties of the piston to determine if there is a delay before the ObstaclePiston starts moving and the speed of the motion.
  • Then we create the second Fixture. Notice that the second Fixture is included in the Body (body.createFixture(fixtureDef);), thus there is only one Body.
  • Then there is another bunch of code lines that I am too lazy to detail. It allows to determine which Fixture is the Head and which Fixture is the Axis, and deduce from their positions what will be the direction of the ObstaclePiston motion.
  • Finally, the active() method applies the eventual delay before starting the motion and update the motion direction each time the ObstaclePiston reaches the end of its stroke.