Task

Completing the GHW Games challenge - Use the Java Game Libraries - Create an Asteroids Game - Update a Classic Game

What it does

A user can play the classic Asteroids game on their pc.

How we built it

I've built it using Java and LibGDX library.

Firstly, generate default project using libGDX. Then locate the .java file for our game core\src\com\mygdx\game\AsteroidGame.java, and import required libraries, and defining variables.

package com.mygdx.game;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;

public class AsteroidGame extends ApplicationAdapter {
    private OrthographicCamera camera;
    private SpriteBatch batch;
    private TextureRegion playerShip;
    private TextureRegion asteroid;
    private TextureRegion bullet;
    private Vector2 playerPosition;
    private Vector2 playerVelocity;
    private Rectangle playerBounds;
    private Array<Vector2> asteroids;
    private Array<Rectangle> asteroidBounds;
    private Array<Vector2> bullets;
    private int score;
    private boolean gameOver;
    private BitmapFont font;
}

Set Up Camera and batch

@Override
public void create() {
    float w = Gdx.graphics.getWidth();
    float h = Gdx.graphics.getHeight();

    camera = new OrthographicCamera(w, h);
    camera.position.set(camera.viewportWidth / 2f, camera.viewportHeight / 2f, 0);
    camera.update();

    batch = new SpriteBatch();
}

Import the textures for player ship, asteroid, and bullet.

    @Override
    public void create() {
        // Rest of the code

        playerShip = new TextureRegion(new Texture("player.png"));
        asteroid = new TextureRegion(new Texture("asteroids.png"));
        bullet = new TextureRegion(new Texture("bullet.png"));
    }

Initialize player, asteroids, bullets, score, game over variable, and font.

    @Override
    public void create() {
        // Rest of the code

        playerPosition = new Vector2(w / 2, h / 2);
        playerVelocity = new Vector2(0, 0);
        playerBounds = new Rectangle(playerPosition.x, playerPosition.y, playerShip.getRegionWidth(), playerShip.getRegionHeight());

        asteroids = new Array<Vector2>();
        asteroidBounds = new Array<Rectangle>();

        bullets = new Array<Vector2>();

        score = 0;
        gameOver = false;

        font = new BitmapFont();
        font.getData().setScale(2);
    }

In render function, define player to move with mouse and shoot with left click, spawn asteroids, give movement to bullets and asteroids, destroy asteroids on collision with bullets, and game over when player collides with an asteroid.

    @Override
    public void render() {
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        if (!gameOver) {
            Gdx.gl.glClearColor(0, 0, 0, 1);
            Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

            Vector2 mousePosition = new Vector2(Gdx.input.getX(), Gdx.graphics.getHeight() - Gdx.input.getY());
            playerVelocity.set(mousePosition).sub(playerPosition).nor().scl(3);
            playerPosition.add(playerVelocity);

            wrapAroundScreen(playerPosition);
            playerBounds.setPosition(playerPosition);

            if (MathUtils.random() < 0.01f) {
                spawnAsteroid();
            }

            for (int i = 0; i < asteroids.size; i++) {
                Vector2 asteroidPosition = asteroids.get(i);
                asteroidPosition.add(-2, 0);

                if (asteroidPosition.x < -50) {
                    asteroids.removeIndex(i);
                    asteroidBounds.removeIndex(i);
                    i--;
                    continue;
                }

                Rectangle asteroidRect = asteroidBounds.get(i);
                asteroidRect.setPosition(asteroidPosition);

                if (asteroidRect.overlaps(playerBounds)) {
                    gameOver = true;
                    break;
                }

                for (int j = 0; j < bullets.size; j++) {
                    Vector2 bulletPosition = bullets.get(j);
                    if (asteroidRect.contains(bulletPosition)) {
                        score++;
                        asteroids.removeIndex(i);
                        asteroidBounds.removeIndex(i);
                        bullets.removeIndex(j);
                        i--;
                        break;
                    }
                }
            }

            if (Gdx.input.isButtonJustPressed(Input.Buttons.LEFT)) {
                bullets.add(new Vector2(playerPosition.x + playerShip.getRegionWidth() / 2, playerPosition.y + playerShip.getRegionHeight() / 2));
            }

            for (int i = 0; i < bullets.size; i++) {
                Vector2 bulletPosition = bullets.get(i);
                bulletPosition.add(10, 0); // Adjust bullet speed

                if (bulletPosition.x > Gdx.graphics.getWidth()) {
                    bullets.removeIndex(i);
                    i--;
                }
            }

            batch.setProjectionMatrix(camera.combined);
            batch.begin();
            for (Vector2 asteroidPos : asteroids) {
                batch.draw(asteroid, asteroidPos.x, asteroidPos.y);
            }
            for (Vector2 bulletPos : bullets) {
                batch.draw(bullet, bulletPos.x, bulletPos.y);
            }
            batch.draw(playerShip, playerPosition.x, playerPosition.y);
            batch.end();
        }
    }

    private void wrapAroundScreen(Vector2 position) {
        position.x = MathUtils.clamp(position.x, 0, Gdx.graphics.getWidth() - playerShip.getRegionWidth());
        position.y = MathUtils.clamp(position.y, 0, Gdx.graphics.getHeight() - playerShip.getRegionHeight());
    }

    private void spawnAsteroid() {
        float x = Gdx.graphics.getWidth();
        float y = MathUtils.random(Gdx.graphics.getHeight() - asteroid.getRegionHeight());
        asteroids.add(new Vector2(x, y));
        asteroidBounds.add(new Rectangle(x, y, asteroid.getRegionWidth(), asteroid.getRegionHeight()));
    }

Display score on top-left, and controls on top-right.

    @Override
    public void render() {
        // Rest of the code

        if (!gameOver) {
            // Rest of the code

            batch.begin();
            font.getData().setScale(1);
            font.draw(batch, "Score: " + score, 20, Gdx.graphics.getHeight() - 20);
            font.draw(batch, "Move mouse to move the ship", Gdx.graphics.getWidth() - 200, Gdx.graphics.getHeight() - 20);
            font.draw(batch, "Left click to shoot", Gdx.graphics.getWidth() - 200, Gdx.graphics.getHeight() - 40);
            batch.end();
        }
    }

When game is over, display a game over message and score.

    @Override
    public void render() {
        // Rest of the code

        if (!gameOver) {
            // Rest of the code
        }
        else {
            batch.begin();
            font.getData().setScale(2);
            font.draw(batch, "Game Over!", Gdx.graphics.getWidth() / 2 - 100, Gdx.graphics.getHeight() / 2);
            font.draw(batch, "Score: " + score, Gdx.graphics.getWidth() / 2 - 75, Gdx.graphics.getHeight() / 2 - 50);
            batch.end();
        }
    }

Give controls to Quit as Q and Restart as R.

    @Override
    public void render() {
        // Rest of the code

        if (!gameOver) {
            // Rest of the code

            font.draw(batch, "Press Q to quit", Gdx.graphics.getWidth() - 200, Gdx.graphics.getHeight() - 60);
            batch.end();

            if (Gdx.input.isKeyJustPressed(Input.Keys.Q)) {
                exitGame();
            }
        }
        else {
            // Rest of the code

            font.draw(batch, "Press R to restart or Q to exit.", Gdx.graphics.getWidth() / 2 - 175, Gdx.graphics.getHeight() / 2 - 100);
            batch.end();

            if (Gdx.input.isKeyJustPressed(Input.Keys.R)) {
                restartGame();
            } else if (Gdx.input.isKeyJustPressed(Input.Keys.Q)) {
                exitGame();
            }
        }
    }

    private void restartGame() {
        playerPosition.set(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
        playerVelocity.set(0, 0);
        playerBounds.setPosition(playerPosition);
        asteroids.clear();
        asteroidBounds.clear();
        bullets.clear();
        score = 0;
        gameOver = false;
    }

    private void exitGame() {
        Gdx.app.exit();
    }

Create a dispose function.

@Override
    public void dispose() {
        batch.dispose();
        playerShip.getTexture().dispose();
        asteroid.getTexture().dispose();
        bullet.getTexture().dispose();
        font.dispose();
    }

Full Code:

package com.mygdx.game;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;

public class AsteroidGame extends ApplicationAdapter {
    private OrthographicCamera camera;
    private SpriteBatch batch;
    private TextureRegion playerShip;
    private TextureRegion asteroid;
    private TextureRegion bullet;
    private Vector2 playerPosition;
    private Vector2 playerVelocity;
    private Rectangle playerBounds;
    private Array<Vector2> asteroids;
    private Array<Rectangle> asteroidBounds;
    private Array<Vector2> bullets;
    private int score;
    private boolean gameOver;
    private BitmapFont font;

    @Override
    public void create() {
        float w = Gdx.graphics.getWidth();
        float h = Gdx.graphics.getHeight();

        camera = new OrthographicCamera(w, h);
        camera.position.set(camera.viewportWidth / 2f, camera.viewportHeight / 2f, 0);
        camera.update();

        batch = new SpriteBatch();

        playerShip = new TextureRegion(new Texture("player.png"));
        asteroid = new TextureRegion(new Texture("asteroids.png"));
        bullet = new TextureRegion(new Texture("bullet.png"));

        playerPosition = new Vector2(w / 2, h / 2);
        playerVelocity = new Vector2(0, 0);
        playerBounds = new Rectangle(playerPosition.x, playerPosition.y, playerShip.getRegionWidth(), playerShip.getRegionHeight());

        asteroids = new Array<Vector2>();
        asteroidBounds = new Array<Rectangle>();

        bullets = new Array<Vector2>();

        score = 0;
        gameOver = false;

        font = new BitmapFont();
        font.getData().setScale(2);
    }

    @Override
    public void render() {
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        if (!gameOver) {
            Gdx.gl.glClearColor(0, 0, 0, 1);
            Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

            Vector2 mousePosition = new Vector2(Gdx.input.getX(), Gdx.graphics.getHeight() - Gdx.input.getY());
            playerVelocity.set(mousePosition).sub(playerPosition).nor().scl(3);
            playerPosition.add(playerVelocity);

            wrapAroundScreen(playerPosition);

            playerBounds.setPosition(playerPosition);

            if (MathUtils.random() < 0.01f) {
                spawnAsteroid();
            }

            for (int i = 0; i < asteroids.size; i++) {
                Vector2 asteroidPosition = asteroids.get(i);
                asteroidPosition.add(-2, 0);

                if (asteroidPosition.x < -50) {
                    asteroids.removeIndex(i);
                    asteroidBounds.removeIndex(i);
                    i--;
                    continue;
                }

                Rectangle asteroidRect = asteroidBounds.get(i);
                asteroidRect.setPosition(asteroidPosition);

                if (asteroidRect.overlaps(playerBounds)) {
                    gameOver = true;
                    break;
                }

                for (int j = 0; j < bullets.size; j++) {
                    Vector2 bulletPosition = bullets.get(j);
                    if (asteroidRect.contains(bulletPosition)) {
                        score++;
                        asteroids.removeIndex(i);
                        asteroidBounds.removeIndex(i);
                        bullets.removeIndex(j);
                        i--;
                        break;
                    }
                }
            }

            if (Gdx.input.isButtonJustPressed(Input.Buttons.LEFT)) {
                bullets.add(new Vector2(playerPosition.x + playerShip.getRegionWidth() / 2, playerPosition.y + playerShip.getRegionHeight() / 2));
            }

            for (int i = 0; i < bullets.size; i++) {
                Vector2 bulletPosition = bullets.get(i);
                bulletPosition.add(10, 0);

                if (bulletPosition.x > Gdx.graphics.getWidth()) {
                    bullets.removeIndex(i);
                    i--;
                }
            }

            batch.setProjectionMatrix(camera.combined);
            batch.begin();
            for (Vector2 asteroidPos : asteroids) {
                batch.draw(asteroid, asteroidPos.x, asteroidPos.y);
            }
            for (Vector2 bulletPos : bullets) {
                batch.draw(bullet, bulletPos.x, bulletPos.y);
            }
            batch.draw(playerShip, playerPosition.x, playerPosition.y);
            batch.end();

            batch.begin();
            font.getData().setScale(1);
            font.draw(batch, "Score: " + score, 20, Gdx.graphics.getHeight() - 20);

            font.draw(batch, "Move mouse to move the ship", Gdx.graphics.getWidth() - 200, Gdx.graphics.getHeight() - 20);
            font.draw(batch, "Left click to shoot", Gdx.graphics.getWidth() - 200, Gdx.graphics.getHeight() - 40);
            font.draw(batch, "Press Q to quit", Gdx.graphics.getWidth() - 200, Gdx.graphics.getHeight() - 60);
            batch.end();

            if (Gdx.input.isKeyJustPressed(Input.Keys.Q)) {
                exitGame();
            }
        }
        else {
            batch.begin();
            font.getData().setScale(2);
            font.draw(batch, "Game Over!", Gdx.graphics.getWidth() / 2 - 100, Gdx.graphics.getHeight() / 2);
            font.draw(batch, "Score: " + score, Gdx.graphics.getWidth() / 2 - 75, Gdx.graphics.getHeight() / 2 - 50);
            font.draw(batch, "Press R to restart or Q to exit.", Gdx.graphics.getWidth() / 2 - 175, Gdx.graphics.getHeight() / 2 - 100);
            batch.end();

            if (Gdx.input.isKeyJustPressed(Input.Keys.R)) {
                restartGame();
            } else if (Gdx.input.isKeyJustPressed(Input.Keys.Q)) {
                exitGame();
            }
        }      
    }

    @Override
    public void dispose() {
        batch.dispose();
        playerShip.getTexture().dispose();
        asteroid.getTexture().dispose();
        bullet.getTexture().dispose();
        font.dispose();
    }

    private void wrapAroundScreen(Vector2 position) {
        position.x = MathUtils.clamp(position.x, 0, Gdx.graphics.getWidth() - playerShip.getRegionWidth());
        position.y = MathUtils.clamp(position.y, 0, Gdx.graphics.getHeight() - playerShip.getRegionHeight());
    }

    private void spawnAsteroid() {
        float x = Gdx.graphics.getWidth();
        float y = MathUtils.random(Gdx.graphics.getHeight() - asteroid.getRegionHeight());
        asteroids.add(new Vector2(x, y));
        asteroidBounds.add(new Rectangle(x, y, asteroid.getRegionWidth(), asteroid.getRegionHeight()));
    }

    private void restartGame() {
        playerPosition.set(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
        playerVelocity.set(0, 0);
        playerBounds.setPosition(playerPosition);
        asteroids.clear();
        asteroidBounds.clear();
        bullets.clear();
        score = 0;
        gameOver = false;
    }

    private void exitGame() {
        Gdx.app.exit();
    }
}

Challenges we ran into

Making the bullets shoot and move properly :)

Accomplishments that we're proud of

Recreating a simple version of the classic Asteroids game.

Built With

Share this project:

Updates