Finally, we have reached the third part of the project page, I do not call this the tutorial page anymore because I am not a good teacher and thus I do not wish to teach you step by step on how to create this entire LibGdx demo project. Do read through the code and take your time to understand them. All code provided is working well, so don’t worry about it.

In this part, we are going to create another two files besides the one you have seen in the previous chapter.

The first file is the player class where we are going to create the body of this object which is linked with the box2d world. We also will handle various touch events passed to this class by the touch adapter class and then assign force and velocity to this player object. If you want to learn more about the LibGdx’s Box2d extension then do read this page online. Below is the entire source code for the player class.

package com.gamingdirectional.lonemoon;

import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Array;

import java.util.ArrayList;

public class Player {

    private final Vector2 position;
    private World world;
    final static int BOX2D_TOGAME_SCALE = 1;
    Body body;
    // Create an array to be filled with the bodies
    // (better don't create a new one every time though)
    Array<Body> bodies = new Array<Body>();
    TextureRegion planeRegion;

    public Player(Vector2 position, World world, TextureRegion planeRegion) {
        this.position = position;
        this.world = world;
        this.planeRegion = planeRegion;
        createBodyFixture();
    }

    public void createBodyFixture() {

        // First we create a body definition
        BodyDef bodyDef = new BodyDef();
        // We set our body to dynamic, for something like ground which doesn't move we would set it to StaticBody
        bodyDef.type = BodyDef.BodyType.DynamicBody;
        // Set our body's starting position in the world
        bodyDef.position.set(position.x+planeRegion.getRegionWidth()/2, position.y+planeRegion.getRegionHeight()/2);

        // Create our body in the world using our body definition
        body = world.createBody(bodyDef);

        // Create a box shape and set its radius to 6
        PolygonShape box = new PolygonShape();
        box.setAsBox(planeRegion.getRegionWidth()/2*BOX2D_TOGAME_SCALE, planeRegion.getRegionHeight()/2*BOX2D_TOGAME_SCALE);

        // Create a fixture definition to apply our shape to
        FixtureDef fixtureDef = new FixtureDef();
        fixtureDef.shape = box;
        fixtureDef.density = 0.5f;
        fixtureDef.friction = 0.3f;
        fixtureDef.restitution = 0.6f; // Make it bounce a little bit

        // Create our fixture and attach it to the body
        Fixture fixture = body.createFixture(fixtureDef);

        // Remember to dispose of any shapes after you're done with them!
        // BodyDef and FixtureDef don't need disposing, but shapes do.
        box.dispose();

        // link body to player
        body.setUserData(planeRegion);

    }

    public void applyForceOnBody(ArrayList<Boolean> direction) {
        /* Apply a force of 1 meter per second on the X-axis and Y-axis at center of the body slowly moving it according to the direction specified */
        if(direction.get(0) && direction.get(1)) {
            body.applyForceToCenter(1.0f, 1.0f,  false);
            body.setLinearVelocity(50, 50);
        }

        else if(direction.get(0) && !direction.get(1)) {
            body.applyForceToCenter(1.0f, -1.0f,  false);
            body.setLinearVelocity(50, -50);
        }

        else if(!direction.get(0) && direction.get(1)) {
            body.applyForceToCenter(-1.0f, 1.0f,  false);
            body.setLinearVelocity(-50, 50);
        }

        else if(!direction.get(0) && !direction.get(1)) {
            body.applyForceToCenter(-1.0f, -1.0f, false);
            body.setLinearVelocity(-50, -50);
        }
    }

    public Body getPlayerBody() {
        return body;
    }

}

Next, we will create the DetectTouch class which extends the InputAdapter abstract class. Each time the user touches the phone screen, this class will call the player class to adjust the plane position on the air.

package com.gamingdirectional.lonemoon;

import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.math.Vector2;
import java.util.ArrayList;

public class DetectTouch extends InputAdapter{

    Vector2 touchPosition;
    float viewportHeight;
    Player player;
    ArrayList<Boolean> direction;
    final static int BOX2D_TOGAME_SCALE = 1;

    public DetectTouch(Player player, float viewportHeight) {

        touchPosition = new Vector2();
        this.viewportHeight = viewportHeight;
        direction = new ArrayList<Boolean>();
        direction.add(0, true);
        direction.add(1, true);
        this.player  = player;
    }


    private void applyForce() {
        player.applyForceOnBody(direction);
    }

    public boolean touchUp (int x, int y, int pointer, int button) {
        touchPosition.set((float)x/BOX2D_TOGAME_SCALE, (viewportHeight-(float)y)/BOX2D_TOGAME_SCALE);
        calculateposition();
        applyForce();

        return true; // return true to indicate the event was handled
    }

    public boolean touchDown (int x, int y, int pointer, int button) {

        return true;
    }

    public void calculateposition() {
        if(touchPosition.x > player.getPlayerBody().getPosition().x && touchPosition.y > player.getPlayerBody().getPosition().y) {
            direction.set(0, true);
            direction.set(1, true);
        } else if(touchPosition.x < player.getPlayerBody().getPosition().x && touchPosition.y > player.getPlayerBody().getPosition().y) {
            direction.set(0, false);
            direction.set(1, true);
        } else if(touchPosition.x < player.getPlayerBody().getPosition().x && touchPosition.y < player.getPlayerBody().getPosition().y) {
            direction.set(0, false);
            direction.set(1, false);
        } else if(touchPosition.x > player.getPlayerBody().getPosition().x && touchPosition.y < player.getPlayerBody().getPosition().y) {
            direction.set(0, true);
            direction.set(1, false);
        }
    }

}

As you can see the calculateposition method above will prepare all the details that the player class needs to reposition the plane.

Finally, we have made a small amendment to the main activity class, I have used one of those methods uses by the developer on stackoverflow to calculate the delta time uses in the world object.

package com.gamingdirectional.lonemoon;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.FitViewport;

public class MainActivity extends ApplicationAdapter  {

	SpriteBatch batch;
	OrthographicCamera camera;
	float viewportWidth;
	float viewportHeight;
	TextureRegion bgRegion;
	TextureRegion planeRegion;
	Vector2 planePosition;
	BitmapFont font;
	StringBuilder score = new StringBuilder();
	Integer point;
	TextureAtlas atlas;
	Vector2 touchPosition;
	Vector3 cameraposition = new Vector3();
	DetectTouch touchadaptor;
	Player player;
	World world;
	static final float STEP_TIME = 1f/60f;
	float accumulator = 0;
	Box2DDebugRenderer debugRenderer;
	final static int BOX2D_TOGAME_SCALE = 1;
	private FitViewport viewport;
	private Stage bj;

	public MainActivity(int width, int height) {
		viewportWidth = width;
		viewportHeight = height;
	}
	
	@Override
	public void create () {
		resetGame();
	}

	@Override
	public void render () {
		Gdx.gl.glClearColor(1, 0, 0, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		updateGame();
		drawGame();
	}

	private void drawGame() {

		updateCamera();
		batch.setProjectionMatrix(camera.combined);
		batch.begin();
		batch.draw(bgRegion, 0, 0);
		batch.draw((TextureRegion) player.getPlayerBody().getUserData(), player.getPlayerBody().getPosition().x-planeRegion.getRegionWidth()/2, player.getPlayerBody().getPosition().y-planeRegion.getRegionHeight()/2);
		font.draw(batch, score, viewportWidth/6-font.getScaleX()/2, viewportHeight - font.getScaleY()/4);
		batch.end();
		debugRenderer.render(world, camera.combined);
	}

	private void updateCamera() {
		cameraposition.set(player.getPlayerBody().getPosition().x, player.getPlayerBody().getPosition().y,0);
		//camera.unproject(cameraposition);
		camera.update();
	}

	private void updateGame() {
		updatePosition();
		updateScore();
	}

	// step time control
	private void updatePosition() {

		float delta = Gdx.graphics.getDeltaTime();

		accumulator += Math.min(delta, 0.25f);

		if (accumulator >= STEP_TIME) {
			accumulator -= STEP_TIME;
			world.step(STEP_TIME, 6, 2);
		}
	}

	private void updateScore() {
		Float f = player.getPlayerBody().getPosition().x;
		score.replace(0, 1, f.toString());
	}


	/* resetting game objects, background, font, camera, and create sprite batch object */

	private void resetGame() {
		batch = new SpriteBatch();
		atlas = new TextureAtlas(Gdx.files.internal("lonemoon.txt"));
		debugRenderer = new Box2DDebugRenderer();
		resetScore();
		resetFont();
		resetPysWorld();
		resetPlane();
		resetCamera();
		resetBackground();
		Gdx.input.setInputProcessor(touchadaptor);
	}

	private void resetPysWorld() {
		world = new World(new Vector2(0, 0), false);
	}

	private void resetScore() {
		point = 0;
	}

	private void resetFont() {

		font = new BitmapFont(Gdx.files.internal("freefont.fnt"),
				Gdx.files.internal("freefont.png"), false);
	}

	private void resetCamera() {
		camera = new OrthographicCamera();
		camera.setToOrtho(false, viewportWidth, viewportHeight);
		viewport = new FitViewport(viewportWidth, viewportHeight, camera);
	}

	private void resetPlane() {
		planeRegion = atlas.findRegion("plane");//new TextureRegion(plane, plane.getWidth(), plane.getHeight());
		planePosition= new Vector2();
		touchPosition=new Vector2();
		planePosition.set((viewportWidth/2-planeRegion.getRegionWidth()/2)/BOX2D_TOGAME_SCALE, (viewportHeight/2 - planeRegion.getRegionHeight()/2)/BOX2D_TOGAME_SCALE);
		player = new Player(planePosition, world, planeRegion);
		touchadaptor = new DetectTouch(player, viewportHeight);
	}

	private void resetBackground() {
		bgRegion = atlas.findRegion("background0");//new TextureRegion(background, background.getWidth(), background.getHeight());
	}


	@Override
	public void dispose () {
		batch.dispose();
		font.dispose();
		atlas.dispose();
		world.dispose();
	}

}

And there you have it, enjoy the code and happy coding!

Box2D with LibGdx outcome in Android Phone

Box2D is not that simple as you might think, there is a time I even think of giving up using LibGdx to develop this game and switch to Godot engine because of Box2D, but I manage to get through half of the hurdle, I hope there are not too many Box2D problems lie ahead when we continue… 🙂

Please follow and like us:

Leave a Reply

Your email address will not be published. Required fields are marked *