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.
Log in or sign up for Devpost to join the conversation.