1
\$\begingroup\$

Github is a link to all my code and the working JAR file.

I have been learning LibGDX and attempted to make a working Pong game, I am new to this and would like some pointers on where I could improve my code, if there is anything I have done incorrectly or can do easier, better.

This is my first week with LibGDX so I am not sure if I am doing things correctly.

Pong.java

package my.game.pong;

//MY PONG GAME :D

import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
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;

public class Pong extends Game {
    private final float WIDTH = 800f, HEIGHT = 480f;
    private final float FPS = 60f;
    private float deltaTime;

    protected OrthographicCamera camera;
    protected SpriteBatch batch;

    protected int enemyScore, playerScore;
    protected BitmapFont scoreText;

    protected Ball ball;
    private Texture ballTexture;

    protected Player player;
    protected Enemy enemy;
    private Texture batTexture;

@Override
public void create () {
    camera = new OrthographicCamera();
    camera.setToOrtho(false, WIDTH, HEIGHT);

    batch = new SpriteBatch();

    playerScore = enemyScore = 0;
    scoreText = new BitmapFont();

    ballTexture = new Texture(Gdx.files.internal("ball.png"));
    ball = new Ball(this, WIDTH/2 - 20/2, HEIGHT/2 - 20/2, ballTexture);

    batTexture = new Texture(Gdx.files.internal("bat.png"));
    player = new Player(this, WIDTH - 60, HEIGHT/2 - 75/2, batTexture);
    enemy = new Enemy(this, 40, HEIGHT/2 - 75/2, batTexture);

    this.setScreen(new MainMenu(this));
}
@Override
public void render () {
    super.render();
}   
@Override
public void dispose () {
    batch.dispose();
    ballTexture.dispose();
    batTexture.dispose();
    scoreText.dispose();
}
public float getWidth () {
    return WIDTH;
}
public float getHeight () {
    return HEIGHT;
}
public void setDeltaTime (float dt) {
    deltaTime = Math.min(dt, 1/FPS);
}
public float getDeltaTime () {
    return deltaTime;
}
}

MainMenu.java

package my.game.pong;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.BitmapFont;

public class MainMenu implements Screen {
private Pong pong;

private BitmapFont start;
private BitmapFont winner;

MainMenu(Pong pong) {
    this.pong = pong;
    start = new BitmapFont();
    winner = new BitmapFont();
}

@Override
public void show() {
    // TODO Auto-generated method stub

}
@Override
public void render(float delta) {
    Gdx.gl.glClearColor(0,0,0,0);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    pong.camera.update();
    pong.batch.setProjectionMatrix(pong.camera.combined);

    pong.batch.begin();
        if(pong.enemyScore > pong.playerScore) {
            winner.draw(pong.batch, "Enemy AI WINS!", pong.getWidth()/2 - 55, pong.getHeight() - 100);
        }
        if(pong.enemyScore < pong.playerScore) {
            winner.draw(pong.batch, "Player 1 WINS!", pong.getWidth()/2 - 55, pong.getHeight() - 100);
        }
        start.draw(pong.batch, "Touch to start", pong.getWidth()/2-50, pong.getHeight()/2);
    pong.batch.end();

    if(Gdx.input.isTouched()) {
        pong.setScreen(new GameScreen(pong));
        pong.enemyScore = pong.playerScore = 0;
    }
}

@Override
public void resize(int width, int height) {
    // TODO Auto-generated method stub

}
@Override
public void pause() {
    // TODO Auto-generated method stub

}
@Override
public void resume() {
    // TODO Auto-generated method stub

}
@Override
public void hide() {
    // TODO Auto-generated method stub

}
@Override
public void dispose() {
    start.dispose();
    winner.dispose();

}

}

GameScreen.java

package my.game.pong;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;

public class GameScreen implements Screen {
private final Pong pong;

GameScreen(Pong pong) {
    this.pong = pong;
}

@Override
public void show() {
    // TODO Auto-generated method stub

}
@Override
public void render(float delta) {
    pong.setDeltaTime(delta);
    Gdx.gl.glClearColor(0,0,0,0);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    pong.camera.update();
    pong.batch.setProjectionMatrix(pong.camera.combined);

    pong.ball.update(pong.getDeltaTime());
    pong.player.update(pong.getDeltaTime());
    pong.enemy.update(pong.getDeltaTime());

    pong.batch.begin();
        pong.scoreText.draw(pong.batch, pong.enemyScore + " | " + pong.playerScore, pong.getWidth()/2 - 20, pong.getHeight() - 20);
        pong.ball.draw(pong.batch);
        pong.player.draw(pong.batch);
        pong.enemy.draw(pong.batch);
    pong.batch.end();       

    if(pong.enemyScore == 3 || pong.playerScore == 3) {
        pong.setScreen(new MainMenu(pong));;
    }
}
@Override
public void resize(int width, int height) {
    // TODO Auto-generated method stub

}
@Override
public void pause() {
    // TODO Auto-generated method stub

}
@Override
public void resume() {
    // TODO Auto-generated method stub

}
@Override
public void hide() {
    // TODO Auto-generated method stub

}
@Override
public void dispose() {
    // TODO Auto-generated method stub

}
}

Ball.java

package my.game.pong;

import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.Rectangle;

public class Ball extends Sprite implements Collidable {
private final Pong pong;
private final float VELOCITY = 400;
private float sX, sY; // Store Start Positions
private float vX, vY;
private boolean up, down, left, right;
Ball(Pong pong, float xLocation, float yLocation, Texture texture) {
    super(texture);
    this.pong = pong;
    this.translate(xLocation,  yLocation);
    sX = xLocation;
    sY = yLocation;
    vX = vY = VELOCITY;
    up = left = true;
    down = right = false;
}

public void update(float deltaTime) {
    screenBounds();
    checkCollision(pong.player.getBoundingRectangle());
    checkCollision(pong.enemy.getBoundingRectangle());
    checkDirection();
    moveBall(deltaTime);
}
public void screenBounds () {
    if(this.getX() < 0) {
        resetBall();
        pong.playerScore += 1;
        right = true; left = false;
    }
    if(this.getX() + this.getWidth() > pong.getWidth()) {
        resetBall();
        pong.enemyScore += 1;
        right = false; left = true;
    }
    if(this.getY() < 0) {
        this.setY(0);
        up = true; down = false;
    }
    if(this.getY() + this.getHeight() > pong.getHeight()) {
        this.setY(pong.getHeight() - this.getHeight());
        up = false; down = true;
    }
}
public void resetBall() {
    this.setX(sX);
    this.setY(sY);
}
public void checkDirection () {
    if(up) vY = VELOCITY;
    if(down) vY = -VELOCITY;
    if(left) vX = -VELOCITY;
    if(right) vX = VELOCITY;
}
public void checkCollision(Rectangle sprite) {
    final Rectangle ballBounds = this.getBoundingRectangle();
    final float offset = 10;

    /*
     * Crazy collision code, I wanted the ball to accurately bounce of all sides of the bats
     * this is the only way I found I could do this, by checking for an overlap and then
     * pushing the ball back out from the overlap an into the correct position at the edge of the bat
     * depending on where it hits, if it overlaps the right "player" bat on the right side then it is pushed
     * out to the left so it rests on the right side, so there is no overlap, then the I changed the direction
     * and made the ball bounce back, I did this for the top, bottom and left sides too.
     * 
     * Its still not accurate but it works.
     */
    if(ballBounds.overlaps(sprite)) {
        // LEFT of the ball > LEFT of the bat
        if(ballBounds.getX() < sprite.getX()) {
            //TOP of ball > BOTTOM of bat && BOTTOM of the ball < TOP of the bat
            if((ballBounds.getY() + offset) > sprite.getY() &&
                    (ballBounds.getY() + offset) < (sprite.getY() + sprite.getHeight())) { 
                //PUSH OUT
                ballBounds.setX(sprite.getX() - ballBounds.getWidth());
                left = true; right = false;
            }               
        }
        // RIGHT of the ball > RIGHT of the bat
        if(ballBounds.getX() + ballBounds.getWidth() > sprite.getX() + sprite.getWidth()) { 
            //TOP of ball > BOTTOM of bat && BOTTOM of the ball < TOP of the bat
            if(ballBounds.getY() + offset > sprite.getY() &&
                    ballBounds.getY() + offset < sprite.getY() + sprite.getHeight()) {                  
                //PUSH OUT
                ballBounds.setX(sprite.getX() + sprite.getWidth());
                left = false; right = true;
            }               
        }
        // BOTTOM of the ball < BOTTOM of the bat
        if(ballBounds.getY() < sprite.getY()) { 
            // RIGHT of the ball > LEFT of the bat
            if(ballBounds.getX() + ballBounds.getWidth() > sprite.getX()) { 
                ballBounds.setY(sprite.getY() - ballBounds.getHeight());
                down = true; up = false;
            }
        }
        // TOP of the ball > TOP of the bat
        if(ballBounds.getY() + ballBounds.getHeight() > sprite.getY() + sprite.getHeight()) { 
            // RIGHT of the ball > LEFT of the bat
            if(ballBounds.getX() + ballBounds.getWidth() > sprite.getX()) {
                ballBounds.setY(sprite.getY() + sprite.getHeight() + ballBounds.getHeight());
                down = false; up = true;
            }
        }
    }
}
public void moveBall (float deltaTime) {
    this.translate(vX * deltaTime, vY * deltaTime);
}

public boolean isRight() {
    return right;
}
}

Player.java

package my.game.pong;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.Vector3;

public class Player extends Sprite {
private final Pong pong;
private final float VELOCITY = 300;
private float vY;
private boolean up, down;
Player(Pong pong, float xLocation, float yLocation, Texture texture) {
    super(texture);
    this.pong = pong;
    this.translate(xLocation,  yLocation);
    vY = VELOCITY;
}

public void update (float deltaTime) {
    handleInput(deltaTime);
    checkBounds();
    movePlayer(deltaTime);
}

public void handleInput(float deltaTime) {
    if(Gdx.input.isKeyPressed(Keys.UP)) {
        up = true; down = false;
    }
    if(Gdx.input.isKeyPressed(Keys.DOWN)) {
        up = false; down = true;
    }
    if(!Gdx.input.isKeyPressed(Keys.UP) && !Gdx.input.isKeyPressed(Keys.DOWN)) {
        up = false; down = false;
    }
    if(Gdx.input.isTouched()) {
        // the following fixes the X,Y co-ordinates retrieved from getY(), getX() using unproject
        Vector3 temp = new Vector3(0, Gdx.input.getY(), 0);
        pong.camera.unproject(temp);
        this.setY(temp.y) ;
    }       
    if(up) vY = VELOCITY;
    if(down) vY = -VELOCITY;
}
public void checkBounds() {
    if(this.getY() < 0) {
        this.setY(0);
    }
    if(this.getY() + this.getHeight() > pong.getHeight()) {
        this.setY(pong.getHeight() - this.getHeight());
    }
}
public void movePlayer(float deltaTime) {
    if(up || down) this.translateY(vY * deltaTime);
}
}

Enemy.java

package my.game.pong;

import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;

public class Enemy extends Sprite {
private final Pong pong;
private final float SPEED = 300;
private float vY;

Enemy(Pong pong, float xLocation, float yLocation, Texture texture) {
    super(texture);
    this.pong = pong;
    this.translate(xLocation, yLocation);
    vY = SPEED;
}

public void update(float deltaTime) {

    processAI();
    checkBounds();
    moveEnemy(deltaTime);       
}
public void processAI() {
    if(pong.ball.getX() < pong.getWidth()/2 && !pong.ball.isRight()) {
        if(this.getY() + this.getHeight()/2 < pong.ball.getY()) {
            vY = SPEED;
        }
        if(this.getY() + this.getHeight()/2 > pong.ball.getY()) {
            vY = -SPEED;
        }
    }
    else {
        if(this.getY() + this.getHeight()/2 < pong.getHeight()/2 - 20) {
            vY = SPEED;
        }
        else if (this.getY() + this.getHeight()/2 > pong.getHeight()/2 + 20) {
            vY = -SPEED;
        } else vY = 0;
    }
}
public void checkBounds() {
    if(this.getY() < 0) {
        this.setY(0);
    }
    if(this.getY() + this.getHeight() > pong.getHeight()) {
        this.setY(pong.getHeight() - this.getHeight());
    }
}
public void moveEnemy(float deltaTime) {
    translateY(vY * deltaTime);
}   
}

I am just looking to write cleaner, better code and do things the correct way, I feel like my code works but some things are done wrong.

\$\endgroup\$
  • \$\begingroup\$ stupid question : Is the the pong.jar also from you? \$\endgroup\$ – chillworld May 19 '15 at 8:10
  • \$\begingroup\$ I'm asking this because pong.batch.begin() isn't the correct way. This means that the variable are at least package private in stead of private. And because the batch comes from Game.java out of pong.jar. \$\endgroup\$ – chillworld May 19 '15 at 8:15
  • \$\begingroup\$ pong.jar is from me yes, the batch comes from Pong.java which is protected not private. \$\endgroup\$ – ke2uke May 19 '15 at 8:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.