2
\$\begingroup\$

I have been working on my Pong game and I added some nice new things to it. You can now select the difficulty on the start screen and I also added a pause screen. Also I gave the Pong ball a tail just like in Snake.

The game is running fine on Windows but on Android, each 1/2 sec, I'm getting small FPS drops. I was wondering if there are any tips on making the game performance better so it needs less RAM/CPU space to run smooth.

Also tips how to make my code easier to read and more clean would be nice.

This is how my game looks like at the moment:

Pong Game Example

Here are my working classes for this game right now:

MainScreen.java

package ***.***.***;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import ***.***.***.Paddle;
import ***.***.***.Ball;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import ***.***.***.Tail;

public class MainScreen implements Screen {
    private GameState gameState = GameState.MENU;
    private GameType gameType = GameType.EASY;

    int countdown = 90;
    int ballt=20;
    int i = 0;;
    float EnemySpeed;
    float BallSpeed;
    float DragX;
    float TouchX;
    float PlayerX;
    float distance;
    boolean tap = true;;
    boolean col; //Colission check
    boolean tapped = true;

    private int score = 0;
    private int lives = 5;

    private Paddle player;
    private Paddle enemy;
    private Ball ball;
    private BitmapFont font;
    private SpriteBatch batch;
    private ShapeRenderer shape;
    Tail[] tail = new Tail[10];
    Texture BackGround;
    Texture PauseIcon;

    public static float difficulty;

    float width = Gdx.graphics.getWidth();
    float height = Gdx.graphics.getHeight();
    float screenwidth = width / 270;
    float screenheight = height / 480;

    public MainScreen() {
        player = new Paddle();
        enemy  = new Paddle();
        ball = new Ball();

        for(int i = 0; i < tail.length; i++){
            tail[i] = new Tail(0,0);
        }

        batch = new SpriteBatch();

        shape = new ShapeRenderer();

        //Load in Fonts
        font = new BitmapFont(Gdx.files.internal("data/whitetext.fnt"),
        Gdx.files.internal("data/whitetext.png"), false);
        BackGround = new Texture("data/background.jpg");
        PauseIcon = new Texture("data/pause_icon.png");
    }

    public enum GameState {
        MENU, INIT, START, UPDATE, GAMEOVER, PAUSE, EXIT, WIN;
    }

    public enum GameType {
        EASY, NORMAL, HARD;
    }

    @Override
    public void render(float delta) {
        if (gameState != GameState.PAUSE){
        Update();
        }else{
            pause();
        }

        if(Gdx.input.isTouched()){tap = false;}else tap = true;

    }

    private boolean hasBallCollision(Paddle player2){
        if(ball.getX()+8*screenwidth>player2.getX()-40*screenwidth&&ball.getX()+8*screenwidth<player2.getX()+40*screenwidth){
            if(ball.getY()>player2.getY()&&ball.getY()<player2.getY()+16*screenheight){
                col = true;
                return true;
            }
            return false;
        }else{
            return false;
        }
    }

    public float getDif() {
        return difficulty;
    }

    public void setDif(float difficulty) {
        this.difficulty = difficulty;
    }

    public void Update(){

        //DRAW BACKGROUND
        batch.begin();
        batch.draw(BackGround,0,0,270*screenwidth,480*screenheight);
        batch.end();

        //Player Drag

        if(Gdx.input.isTouched() && Gdx.input.getY()>240*screenheight){
            DragX = Gdx.input.getX();
            if(tapped)distance = DragX - player.getX();
            tapped = false;

            PlayerX = DragX+-distance;
        }else{
            tapped = true;
        }

        //MENU
        if(gameState == GameState.MENU){
            if (Gdx.input.isTouched() && tap && Gdx.input.getY()>230*screenheight && Gdx.input.getY()<260*screenheight && Gdx.input.getX()>95*screenwidth && Gdx.input.getX()<175*screenwidth){gameType = GameType.EASY;gameState = GameState.INIT;}
            if (Gdx.input.isTouched() && tap && Gdx.input.getY()>280*screenheight && Gdx.input.getY()<310*screenheight && Gdx.input.getX()>75*screenwidth && Gdx.input.getX()<205*screenwidth){gameType = GameType.NORMAL;gameState = GameState.INIT;}
            if (Gdx.input.isTouched() && tap && Gdx.input.getY()>330*screenheight && Gdx.input.getY()<360*screenheight && Gdx.input.getX()>95*screenwidth && Gdx.input.getX()<175*screenwidth){gameType = GameType.HARD;gameState = GameState.INIT;}

            //Gdx.app.log("X"+Gdx.input.getX(), "Y"+Gdx.input.getY());

            batch.begin();
            font.setColor(1.0f, 1.0f, 1.0f, 1.0f);
            font.setScale(1.0f*screenwidth,1.0f*screenheight);
            font.draw(batch, "PONG", 60*screenwidth, 400*screenheight);
            font.setScale(0.5f*screenwidth,0.5f*screenheight);
            font.draw(batch, "EASY", 95*screenwidth, 250*screenheight);
            font.draw(batch, "NORMAL", 75*screenwidth, 200*screenheight);
            font.draw(batch, "HARD", 95*screenwidth, 150*screenheight);
            batch.end();
        }

        //INIT
        if(gameState == GameState.INIT){
            player.init(40);
            enemy.init(430);
            ball.init();
            gameState = GameState.START;
            difficulty = 1.0f;
            countdown = 90;
        }

        //START
        if(gameState == GameState.START){
            if(countdown == 0) gameState = GameState.UPDATE;
            countdown--;
            batch.begin();
            font.setColor(1.0f, 1.0f, 1.0f, 1.0f);
            font.setScale(1.0f*screenwidth,1.0f*screenheight);
            if(countdown >= 60 && countdown <= 90)font.draw(batch, "3", 120 * screenwidth, 300 * screenheight);
            if(countdown >= 30 && countdown <= 60)font.draw(batch, "2", 120 * screenwidth, 300 * screenheight);
            if(countdown >= 0 && countdown <= 30)font.draw(batch, "1", 120 * screenwidth, 300 * screenheight);
            batch.end();
        }

        //UPDATE
        if(gameState == GameState.UPDATE){

        if (Gdx.input.isTouched() && tap && Gdx.input.getY()>0 && Gdx.input.getY()<32*screenheight && Gdx.input.getX()>0 && Gdx.input.getX()<32*screenwidth) gameState= GameState.PAUSE;

        player.setX(PlayerX);
        player.update();
        enemy.update();
        for (int i = tail.length - 1; i > 0; i--) { 
            tail[i] = tail[i - 1];
            tail[i].Update();
        }
        tail[0] = new Tail(ball.getX(),ball.getY());
        ball.update();

        //DRAW SCORE/LIVES/PAUSEICON
        batch.begin();
        font.setColor(1.0f, 1.0f, 1.0f, 1.0f);
        font.setScale(0.2f*screenwidth,0.2f*screenheight);
        font.draw(batch, "Score: "+ score +"/5", 190 * screenwidth, 475 * screenheight);
        font.draw(batch, "Lives: "+ lives, 45 * screenwidth, 475 * screenheight);
        batch.draw(PauseIcon,0,448*screenheight,32.0f*screenwidth,32.0f*screenheight);
        batch.end();
        }

        if(lives == 0)gameState = GameState.GAMEOVER;
        if(score == 5)gameState = GameState.WIN;

        if(ball.getY() < 0 * screenheight){
            lives--;
            gameState = GameState.INIT;
        }
        if(ball.getY() > 480 * screenwidth - 16 * screenwidth){
            score++;
            gameState = GameState.INIT;
        }

        //LOSE SCREEN
        if(gameState == GameState.GAMEOVER){
            if(tap && Gdx.input.isTouched())gameState=GameState.MENU;
            batch.begin();
            font.setColor(1.0f, 1.0f, 1.0f, 1.0f);
            font.setScale(0.5f * screenwidth,0.5f * screenheight);
            font.draw(batch, "Game Over", 30 * screenwidth, 300 * screenheight);
            font.setScale(0.3f * screenwidth,0.3f * screenheight);
            font.draw(batch, "Main Menu", 60 * screenwidth, 260 * screenheight);
            batch.end();
            score = 0;
            lives = 5;
        }

        //WIN SCREEN
        if(gameState == GameState.WIN){
            if(tap && Gdx.input.isTouched())gameState=GameState.MENU;
            Gdx.app.log("TAP"+tap, "Touched"+Gdx.input.isTouched());
            batch.begin();
            font.setColor(1.0f, 1.0f, 1.0f, 1.0f);
            font.setScale(0.5f * screenwidth,0.5f * screenheight);
            font.draw(batch, "You Won!", 60 * screenwidth, 300 * screenheight);
            font.setScale(0.4f*screenwidth,0.4f * screenheight);
            font.draw(batch, "Main Menu", 20 * screenwidth, 260 * screenheight);
            batch.end();
            score = 0;
            lives = 5;
        }

        //Difficulty

        if(gameType == GameType.EASY){EnemySpeed=2.2f;BallSpeed=0.1f;}
        if(gameType == GameType.NORMAL){EnemySpeed=2.5f;BallSpeed=0.2f;}
        if(gameType == GameType.HARD){EnemySpeed=3.0f;BallSpeed=0.5f;}

        //Enemy Ball Colission
        if(ball.getX() + 8 * screenwidth > enemy.getX() - 40 * screenwidth && ball.getX() + 8 * screenwidth<enemy.getX() + 40 * screenwidth){
            if(ball.getY() + 16 * screenheight > enemy.getY() && ball.getY() + 16 * screenheight<enemy.getY() + 16 * screenheight){
                float zy;
                zy = -3 * screenheight;
                ball.setZy(zy);
                if(col)difficulty += BallSpeed;
                col = false;
            }
        }

        //Enemy AI
        if(enemy.getX()<ball.getX()){
            float ex;
            ex = EnemySpeed * screenwidth * difficulty;
            enemy.setDeltaX(ex);
        }
        if(enemy.getX() > ball.getX()){
            float ex;
            ex = - EnemySpeed * screenwidth * difficulty;
            enemy.setDeltaX(ex);
        }

        //Gdx.app.log("TAP", ""+tap);

        if (hasBallCollision(player)){
            ball.setZy(3 * screenheight);
        }
    }

    @Override
    public void resize(int width, int height) {

    }

    @Override
    public void show() {
        Gdx.app.log("Show", "Called");
    }

    @Override
    public void hide() {
        Gdx.app.log("Hide", "Called");
    }

    @Override
    public void pause() {    
        if(tap && Gdx.input.isTouched() && Gdx.input.getY()<240*screenheight)gameState=GameState.UPDATE;
        if(tap && Gdx.input.isTouched() && Gdx.input.getY()>240*screenheight){gameState=GameState.MENU;score=0; lives=5;}
        batch.begin();
        batch.draw(BackGround,0,0,270*screenwidth,480*screenheight);
        font.setColor(1.0f, 1.0f, 1.0f, 1.0f);
        font.setScale(0.4f*screenwidth,0.4f * screenheight);
        font.draw(batch, "Tap to Resume", 25 * screenwidth, 360 * screenheight);
        font.draw(batch, "Main Menu", 60 * screenwidth, 160 * screenheight);
        batch.end();

    }

    @Override
    public void resume() {
        Gdx.app.log("Resume", "Called");
    }

    @Override
    public void dispose() {

    }

}

Paddle.java //enemy and player bar

package ***.***.***;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class Paddle {

    private float width = Gdx.graphics.getWidth();
    private float height = Gdx.graphics.getHeight();

    private float screenwidth = width/270;
    private float screenheight = height/480;

    private SpriteBatch batch;
    private Texture sprite;

    private float x,y;
    private float deltaX;

    public void init(int parameter) {
        x=135*screenwidth;
        y=parameter*screenheight;

        batch = new SpriteBatch();
        sprite = new Texture("data/bar.png");
    }

    public void setX(float ex) { 
        x = ex;
    }

    public void setY(float ex) { 
        y = ex;
    }


    public void update(){
        x += deltaX;
        if(x>229*screenwidth)x=229*screenwidth;
        if(x<41*screenwidth)x=41*screenwidth;
        batch.begin();
        batch.draw(sprite,x-40*screenwidth,y,80*screenwidth,16*screenheight);
        batch.end();
    }

    public float getX() {
        return x;
    }

    public float getY(){
        return y;
    }

    public void setDeltaX(float deltaX) {
        this.deltaX = deltaX;
    }
}

Ball.java

package ***.***.***;

import java.util.Random;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import ***.***.***.MainScreen;

public class Ball {

    SpriteBatch batch;
    Texture ballsprite;

    Random random = new Random();

    float width = Gdx.graphics.getWidth();
    float height = Gdx.graphics.getHeight();
    float screenwidth = width/270;
    float screenheight = height/480;

    public float x,y,zx,zy,randomx,randomw;

    float difficulty;

    private MainScreen main;

    public Ball(){

    }

    public void init(){
        batch = new SpriteBatch();
        ballsprite = new Texture("data/ball.png");

        x = 127 * screenwidth;
        y = 232 * screenheight;
        randomw = random.nextInt(2);
        if(randomw == 1)randomw =- 1;else randomw = 1;
        randomx = random.nextInt(20) + 20;
        zx = randomx / 10 * screenwidth * randomw;
        zy = 3 * screenwidth;

        main = new MainScreen();
    }

    public void update(){
        difficulty = main.getDif();
        //Colission check
        if(x<0*screenheight)zx = 3 * screenwidth;
        if(x>270*screenwidth - 16 * screenwidth)zx =- 3 * screenwidth;

        x += zx * difficulty;
        y += zy * difficulty;

        //Draw ball
        batch.begin();
        batch.draw(ballsprite,x,y,16 * screenheight,16 * screenwidth);
        batch.end();
    }

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }

    public void setZy(float zy) {
        this.zy = zy;
    }

    public void setZx(float zx) {
        this.zx = zx;
    }

}

Tail.java // The new class for the tail following the ball.

package ***.***.***;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;

public class Tail {

    float width = Gdx.graphics.getWidth();
    float height = Gdx.graphics.getHeight();
    float screenwidth = width/270;
    float screenheight = height/480;


    public float x;
    public float y;

    ShapeRenderer shapeRenderer;
    SpriteBatch batch;

    public boolean active = false;

    public Tail(float x, float y){
        shapeRenderer = new ShapeRenderer();
        batch = new SpriteBatch();

        this.x = x;
        this.y = y;





    }

    public void Update(){
          batch.begin();
          shapeRenderer.begin(ShapeType.Filled);
          shapeRenderer.setColor(new Color(1, 1, 1, 0.2f));
          shapeRenderer.circle(x+8*screenwidth, y+8*screenheight, 6*screenheight);
          shapeRenderer.end();
          batch.end();  

    }

}
\$\endgroup\$
  • \$\begingroup\$ Do you have a git repository for this by any chance? It would be a lot easier to check into the performance if there is. \$\endgroup\$ – Simon Forsberg Jan 29 '15 at 14:02
6
\$\begingroup\$

You're missing the best parts of LibGDX!

LibGDX has a lot of convenient, easy-to-use classes and widgets to help you create your game faster and more efficient. To name few: Stage, Actors, Button, Game, Viewport, and much more. You're only using Screen, SpriteBatch and Texture. You should always read LibGDX Wiki if you want to use them.

There are Six sample games using LibGDX in that Wiki that gives you overview and best practices using LibGDX. I suggest first that you download and run them, read the code, and learn their patterns.

General tip: before you start using any frameworks, make sure that you understand all the features it has to offer, so you can use them well. Remember, frameworks are built to make programmers' lives easier. It wants you to use its features rather than doing everything yourself.

Okay, so now lets refactor your code. There's so much thing to do.


1. Use Stage and Actors

This is my favorite feature. An Actor represents any object in your screen. A Stage can contain many Actors. You can initialize Actors, position them, then add them to a Stage, and draw them simultaneously using a single Stage.draw() method. You can find out more by looking at the Wiki, so I won't explain them much here. Look at Scene2d Wiki

Lets see your code: (Honestly, this block of code is hard to read)

if (Gdx.input.isTouched() && tap && Gdx.input.getY()>230*screenheight && Gdx.input.getY()<260*screenheight && Gdx.input.getX()>95*screenwidth && Gdx.input.getX()<175*screenwidth){gameType = GameType.EASY;gameState = GameState.INIT;}

if (Gdx.input.isTouched() && tap && Gdx.input.getY()>280*screenheight && Gdx.input.getY()<310*screenheight && Gdx.input.getX()>75*screenwidth && Gdx.input.getX()<205*screenwidth){gameType = GameType.NORMAL;gameState = GameState.INIT;}

if (Gdx.input.isTouched() && tap && Gdx.input.getY()>330*screenheight && Gdx.input.getY()<360*screenheight && Gdx.input.getX()>95*screenwidth && Gdx.input.getX()<175*screenwidth){gameType = GameType.HARD;gameState = GameState.INIT;}

I suppose this one is for buttons. There are 3 buttons labeled "Easy", "Normal", "Hard". When you click one of them, it begins the game. Right?

Well, you can use Buttons! What if you can write it like this:

buttonEasy.addListener(new ClickListener() {
    @Override
    public function click(Actor actor, float x, float y)
    {
         // Your button is clicked!
         // Do something
    }
});

Convenient, right ?

Well, a Button is an Actor. You can create them, position them, and add them to the Stage. Here's one example:

// Create new button
TextButton buttonEasy = new TextButton("Easy", uiSkin);
// Make the button 100x50 pixels
buttonEasy.setWidth(100);
buttonEasy.setHeight(50);
// Position it at x:75, y:75
buttonEasy.setX(75);
buttonEasy.setY(75);

// Add Click listener
// (See above code)

// Add to Stage
stage.addActor(buttonEasy);

Don't worry if you don't know the uiSkin and stage yet, I'll give you example of my code later.


2. Use Multiple Screens and a Game class

I think you already know about Screen, you used it at MainScreen.java. A Game class is basically the entry point of your game. I usually use it to initialize my SpriteBatch, loading all assets, and display the first Screen.

I found an interesting tutorial here: Using Screen and Game Classes

In your code, there's these conditionals:

if(gameState == GameState.MENU){
     // do something
}
if(gameState == GameState.GAMEOVER){
     // do something
}
if(gameState == GameState.WIN){
     // do something
}

Just use multiple Screens!
It is better to have one class for each screen, such as MainMenuScreen.java, GameScreen.java, OptionsScreen.java, etc.
I personally use one Stage for each Screen.


3. Make Use of the Viewport

Please read: LibGDX Viewport Wiki

I see this code in all of your classes:

float width = Gdx.graphics.getWidth();
float height = Gdx.graphics.getHeight();
float screenwidth = width / 270;
float screenheight = height / 480;

From the link I provided above, you can always do this:

// place this on your create() method
stage = new Stage(new StretchViewport(270, 480));

// place this on your resize() method
// this one fires when your screen is resized
stage.getViewport().update(width, height, false);

4. Putting them all together

So my codes usually looks much like this:

MyGame.java

public class MyGame extends Game {

    // place the screen dimension here
    public final int WIDTH = 270;
    public final int HEIGHT = 480;

    @Override
    public void create() {
        setScreen(new MainMenuScreen(this));              
    }
}

MainMenuScreen.java

public class MainMenuScreen extends Screen {

    private MyGame game; // Note it's "MyGame" not "Game"
    private Stage stage;
    private TextButton playButton;
    private TextButton optionsButton;

    // constructor to keep a reference to the main Game class
    public MainMenuScreen(MyGame game){
            this.game = game;
    }

    @Override
    public void create() {
        // Init Stage with Viewport
        // Use WIDTH and HEIGHT from game
        stage = new Stage(new FitViewport(game.WIDTH, game.HEIGHT));

        // Important! Tell stage to handle our inputs (clicks, touches, etc.)
        // If you forget to call this, your buttons, listeners, won't work
        Gdx.input.setInputProcessor(stage);

        // Init Skin -- read this more in the wiki
        // I'm using the LibGDX's default UI Skin
        Skin uiSkin = new Skin(Gdx.files.internal("uiskin.json"));

        // Init Buttons -- our buttons and actors need skins
        playButton = new TextButton("Play", uiSkin);
        optionsButton = new TextButton("Options", uiSkin);

        // Set Button dimensions and positions
        playButton.setWidth(100);
        playButton.setHeight(100);
        playButton.setX(100);
        playButton.setY(100); 

        optionsButton.setWidth(100);
        optionsButton.setHeight(100);
        optionsButton.setX(300);
        optionsButton.setY(300);

        // Add listeners to Buttons
        playButton.addListener(new ClickListener() {
            @Override
            public function click(Actor actor, float x, float y)
            {
                 // Go to Play Screen
                 game.setScreen(new GameplayScreen(game));
            }
        });
        optionsButton.addListener(new ClickListener() {
            @Override
            public function click(Actor actor, float x, float y)
            {
                 // Go to Options Screen
                 game.setScreen(new OptionsScreen(game));
            }
        });

        // Add them to our Stage -- remember, Button is an Actor
        stage.addActor(playButton);
        stage.addActor(optionsButton);
    }

    @Override
    public void render() { 
        super.render();
        stage.draw(); // One method to draw all Actors to screen
    }

    public void resize(int width, int height) {
        // use true here to center the camera
        // that's what you probably want in case of a UI
        stage.getViewport().update(width, height, false);
    }

}

5. Use Custom Actors

Now probably you are wondering, what should I do with my Paddle, Ball, and Tail classes?

The answer is, make them as an Actor.

I see that you are using ShapeRenderer in one of the classes, I personally dislike the use of ShapeRenderer. You can do a lot of things by extending the Actor class.

Taken from Scene2d Wiki:

public class MyActor extends Actor {
    TextureRegion region;

    public MyActor () {
        region = new TextureRegion(...);
    }

    @Override
    public void draw (Batch batch, float parentAlpha) {
        // Put your drawing code here
        // Draw your object the way you wanted to
        Color color = getColor();
        batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
        batch.draw(region, getX(), getY(), getOriginX(), getOriginY(),
            getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation());
    }
}

Other Techniques

  1. Using AssetManager to load all your assets (Textures, audio, images, etc.) in the beginning of your game, and give you a working Loading Screen.

  2. Using TexturePacker to pack all your images and textures into a single file. Greatly improves performance. https://github.com/libgdx/libgdx/wiki/Texture-packer

That's all I currently have in mind, feel free to ask questions, I will update my answer if there's something missing.

\$\endgroup\$
  • \$\begingroup\$ Fantastic answer! \$\endgroup\$ – Matsemann Feb 15 '15 at 12:11
2
\$\begingroup\$

You should choose the most restricted access level for your variables which fits your needs. If you declare a variable without any modifier like private or public it still can be accessed by each class inside the same package.

See also: https://stackoverflow.com/a/215505/2655508


boolean col; //Colission check  

If you need a comment to describe what the purpose of a variable is, then the variable is poorly named.


public static float difficulty;  

Why static ?


You still have a lot of magic numbers in your code. Extract them to meaningful constants.


if(Gdx.input.isTouched()){tap = false;}else tap = true;  

this can be simplified by negating the methods result like

tap = !Gdx.input.isTouched();  

The variables in the hasBallCollision() method are still screaming for space to breathe.

if(ball.getX()+8*screenwidth>player2.getX()-40*screenwidth&&ball.getX()+8*screenwidth<player2.getX()+40*screenwidth){
            if(ball.getY()>player2.getY()&&ball.getY()<player2.getY()+16*screenheight){  

Also you can remove the last else and instead just call return false; or better put the second if condition into the first and reduce this x * screenwidth like

if(ball.getX() > player2.getX() - 48 * screenwidth  
    && ball.getX() < player2.getX() + 32 * screenwidth  
    && ball.getY() > player2.getY()  
    && ball.getY()< player2.getY() + 16 * screenheight){    

but you also could just remove the if at all and have the method like

private boolean hasBallCollision(Paddle player2){

    col =   ball.getX() > player2.getX() - 48 * screenwidth  
            && ball.getX() < player2.getX() + 32 * screenwidth  
            && ball.getY() > player2.getY()  
            && ball.getY()< player2.getY() + 16 * screenheight;  

    return col;
}

getDif() and setDif() are already mentioned in the last review.


The Update() method should be named using camelCase casing. That is the standard for java.

if(ball.getY() < 0 * screenheight){

can be simplified to

if(ball.getY() < 0){

You should extract the different update states to separate methods like updateMenu(), updateInit() etc.


Empty parameterless constructors can be safely removed.


Remove any unneeded vertical space to improve readability.


Tail doesn't need its x and y to be public.

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.