Java pong game, with level collision not working

      I've been trying to make a pong game recently, and I can't get the rectangle collision right, between a paddle(one rectangle) and a few tiles (another few rectangles). I've tried many ways to do the collision, but I can't get it right.

Here are some Images you might need

Here is my current code

Element


import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;


public class Element {

    public int x;
    public int y;
    public int w;
    public int h;
    public int id;
    public Image image;
    public Rectangle cr; 

    public Element(int x2, int y2, int n, Image i) {
        this.x = x2;
        this.y = y2;
        this.id = n;
        this.image = i;
    }

    public int getID() {
        return id;
    }

    public Image getImage() {
        return image;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getWidth() {
        return w;
    }

    public int getHeight() {
        return h;
    }
    public Rectangle getCollisionRec(){
        return cr = new Rectangle(getX(), getY(),getWidth(),getHeight());
    }
    public void draw(Graphics2D g) {
        g.drawImage(getImage(), getX(), getY(), null);
        g.draw(getCollisionRec());
        g.setPaint(null);
        g.setColor(Color.GRAY);
    }
}

Templetes


import java.awt.Image;

public class Templetes extends Element {

    ///////////////For 4 sided shapes/////////////// 
    public Templetes(int x, int y,int n, Image i) {
        super(x, y, n,i);
    }
    ////////////////////////////////////////////////
}

Level


import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import javax.swing.ImageIcon;

public class Level {

    public Element[][] overallLevel;
    public Element[] elements;
    public Rectangle[][] collisionRecs;
    public Rectangle[] recColElements;
    public int Lines;
    public int levelLength;
    public int leftBorder;
    public int rightBorder;
    public int unit = 20;
    /////////Images/////////
    public Image background;
    public Image square13;
    /////////////////////////

    public Image getBackground() {
        return background;
    }

    public Level() {
        square13 = new ImageIcon(this.getClass().getResource("/ultrapong/levels/images/Square13.png")).getImage();

    }
    /*
    Legend
    : = empty slot
    S = square with all borders
    
     */

    public void defineLevel(String[] definitions) {
        overallLevel = new Element[Lines][definitions[0].length()];
        collisionRecs = new Rectangle[Lines][definitions[0].length()];

        levelLength = definitions[0].length() * unit;
        leftBorder = 0;
        rightBorder = 500;

        int elementsCounter = 0;
        for (int i = 0; i < definitions.length; i++) {
            char[] lineDefinition = definitions[i].toCharArray();
            for (int j = 0; j < lineDefinition.length; j++) {
                if (lineDefinition[j] == ':') {
                    overallLevel[i][j] = null;
                    collisionRecs[i][j] = null;
                } else if (lineDefinition[j] == 'S') {
                    Templetes t13 = new Templetes(j * unit, i * unit, 1, square13);
                    overallLevel[i][j] = t13;
                    collisionRecs[i][j] = t13.getCollisionRec();
                    elementsCounter++;
                }
            }
        }
        elements = new Element[elementsCounter];
        recColElements = new Rectangle[elementsCounter];

        int counter = 0;

        for (int i = 0; i < overallLevel.length; i++) {
            for (int j = 0; j < overallLevel[i].length; j++) {
                if (overallLevel[i][j] != null) {
                    elements[counter] = overallLevel[i][j];
                    recColElements[counter] = collisionRecs[i][j];
                    counter++;
                }
            }
        }

    }

    public void checkCollision(Rectangle p, Rectangle t) {
        if (recColElements != null) {
            if (t.intersects(p)) {
                if (p.x + p.width == t.x) {
                    p.x = t.x - p.width;
                } else if (p.x == t.x + t.width) {
                    p.x = t.x + t.width;
                }

            }
        }
    }

    public void drawLevel(Graphics2D g) {
        try {
            for (int i = 0; i < elements.length; i++) {
                elements[i].draw(g);
            }
        } catch (Exception e) {
            System.out.toString();
        }
    }

    public void drawBackground(Graphics2D g) {
        g.drawImage(getBackground(), 0, 0, null);
    }
}

BoxLevel

import javax.swing.ImageIcon;
import ultrapong.levels.Config.*;

public class BoxLevel extends Level {

public static final String row1 = "SS:::::::::::::::::::::SS";
public static final String row2 = "SS:::::::::::::::::::::SS";
public static final String row3 = ":::::::::::::::::::::::::";
public static final String row4 = ":::::::::::::::::::::::::";
public static final String row5 = ":::::::::::::::::::::::::";
public static final String row6 = ":::::::::::::::::::::::::";
public static final String row7 = ":::::::::::::::::::::::::";
public static final String row8 = ":::::::::::::::::::::::::";
public static final String row9 = ":::::::::::::::::::::::::";
public static final String row10 = ":::::::::::::::::::::::::";
public static final String row11 = ":::::::::::::::::::::::::";
public static final String row12 = ":::::::::::::::::::::::::";
public static final String row13 = ":::::::::::::::::::::::::";
public static final String row14 = ":::::::::::::::::::::::::";
public static final String row15 = ":::::::::::::::::::::::::";
public static final String row16 = ":::::::::::::::::::::::::";
public static final String row17 = ":::::::::::::::::::::::::";
public static final String row18 = ":::::::::::::::::::::::::";
public static final String row19 = ":::::::::::::::::::::::::";
public static final String row20 = ":::::::::::::::::::::::::";
public static final String row21 = ":::::::::::::::::::::::::";
public static final String row22 = "SS:::::::::::::::::::::SS";
public static final String row23 = "SS:::::::::::::::::::::SS";
public static final String row24 = ":::::::::::::::::::::::::";
public static final String row25 = ":::::::::::::::::::::::::";

public BoxLevel() {
    Lines = 25;
    background = new ImageIcon(this.getClass().getResource("/ultrapong/levels/images/backgrounds/whitebackground.PNG")).getImage();
    String[] definitions = {row1, row2, row3, row4, row5,
        row6, row7, row8, row9, row10, row11, row12, row13,
        row14, row15, row16, row17, row18, row19, row20,
        row21, row22, row23, row24, row25};
    super.defineLevel(definitions);

}

}



Paddle

import java.awt.;
import javax.swing.
;

public class Paddle {

public Image paddleImage;
public int width;
public int height;
public Rectangle paddleRec;
public float x, y;
public float originalX;
public int centerX = width / 2;

public Paddle(int i) {
    paddleImage = new ImageIcon(this.getClass().getResource("/ultrapong/Paddle" + i + ".png")).getImage();
    width = paddleImage.getWidth(null);
    height = paddleImage.getHeight(null);

}
public float recX;
public float recY;
public float moveX;
public boolean movable = false;

public Rectangle paddleRec() {
    return new Rectangle(Math.round(getX()), Math.round(getY()), getWidth(), getHeight());
}

public void update() {
    keepinbounds();
    move();
}

public void keepinbounds() {
    if (x < 0) {
        x = 0;
        moveX = 0;
    }
    if (y < 0) {
        y = 0;
    }
    if (x + width > 500) {
        x = 500 - width;

    }
    if (y + height > 500) {
        y = 500 - height;
    }
    if (x < 0) {
        x = 0;

    }
    if (y < 0) {
        y = 0;
    }

}

public float move() {
    return x = x + moveX;

}

public float getX() {
    return x;
}

public float getY() {
    return y;
}

public int getWidth() {
    return width;
}

public int getHeight() {
    return height;
}

public Image getImage() {
    return paddleImage;
}

}



Player

import java.awt.event.KeyEvent;

public class Player extends Paddle {

public Player() {
    super(1);
    y = 450;
    x = 220;
}

public void keyPressed(KeyEvent e) {
    int keyCode = e.getKeyCode();
    if (keyCode == KeyEvent.VK_D || keyCode == KeyEvent.VK_RIGHT) {
        moveX = 4;
    }
    if (keyCode == KeyEvent.VK_A || keyCode == KeyEvent.VK_LEFT) {
        moveX = -4;

    }

}

public void keyReleased(KeyEvent e) {
    int keyCode = e.getKeyCode();
    if (keyCode == KeyEvent.VK_D || keyCode == KeyEvent.VK_RIGHT) {
        moveX = 0;
    }
    if (keyCode == KeyEvent.VK_A || keyCode == KeyEvent.VK_LEFT) {
        moveX = 0;
    }
}

}



MainConfig

import java.awt.;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.
;
import ultrapong.levels.;
import ultrapong.levels.Config.
;

public class MainConfig extends JPanel implements Runnable {

public int setSizeX, setSizeY;
public Player player;
public Thread mainLoop;
public Ball ball = new Ball();
public Level currentLevel;
public int counter;

public MainConfig() {
    player = new Player();
    setDoubleBuffered(true);
    addKeyListener(new KL());
    setFocusable(true);
    setSizeX = 506;
    setSizeY = 498;
    currentLevel = new BoxLevel();
}

public void addNotify() {
    super.addNotify();
    mainLoop = new Thread(this);
    mainLoop.start();
}

public void paint(Graphics g) {
    super.paint(g);
    Graphics2D g2d = (Graphics2D) g;
    currentLevel.drawBackground(g2d);
    g2d.drawImage(player.getImage(), player.paddleRec().x, player.paddleRec().y, null);
    g2d.drawImage(ball.getImage(), ball.ball().x, ball.ball().y, null);
    currentLevel.drawLevel(g2d);
    Toolkit.getDefaultToolkit().sync();
    g.dispose();
}

public class KL extends KeyAdapter {

    @Override
    public void keyPressed(KeyEvent e) {
        player.keyPressed(e);
    }

    @Override
    public void keyReleased(KeyEvent e) {
        player.keyReleased(e);
    }
}

public void update() {
    ball.updateBounce(player.paddleRec(), ball.ball());
    ball.update();
    player.update();
    for (int i = 0; i < currentLevel.elements.length; i++) {
        currentLevel.checkCollision(player.paddleRec(), currentLevel.recColElements[i]);
    }
}

public void run() {

    float startingTime = System.currentTimeMillis();
    float cumTime = System.currentTimeMillis() - startingTime;
    while (true) {
        float timePassed = System.currentTimeMillis() - cumTime;
        cumTime += timePassed;
        update();
        repaint();
        try {
            Thread.sleep(20);
        } catch (Exception e) {
            System.err.toString();
        }
    }
}

}



Ball

import java.awt.Rectangle;
import java.util.Random;
import java.awt.Image;
import javax.swing.ImageIcon;
import static java.lang.Math.*;

public class Ball {

public int x;
public int y;
public int dx = 0;
public int dy = 0;
public int diameter;
public Image ballImg;
public Player player = new Player();

public Ball() {
    x = 250;
    y = 250;
    dx = 3;
    dy = 3;
    diameter = 5;
    ballImg = new ImageIcon(this.getClass().getResource("/ultrapong/Ball.png")).getImage();
}
public Random random = new Random();

public void updateBounce(Rectangle r, Rectangle b) {
    if (r.intersects(b)) {
        bounceY();
    }
    if (getY() >= 500 - 10 && getX() >= player.getX() && getX() <= player.x + player.paddleRec().width) {
        bounceY();
    }
}

public void update() {
    if (y < 0) {
        bounceY();
    }
    if (y + diameter > 460) {
        for (int i = 250; i < 350 && i > 100; i++) {
            y = random.nextInt(i);
        }
        for (int i = 250; i < 350 && i > 100; i++) {
            x = random.nextInt(i);
        }
        for (int i = 0; i < 3 && i > 0; i++) {
            dx = random.nextInt(i);
        }
        for (int i = 0; i < 3 && i > 0; i++) {
            dy = random.nextInt(i);
        }
    }
    if (x < 0) {
        bounceX();
    }
    if (x + diameter + (diameter / 2) > 500) {
        bounceX();
    }
    x = x + dx;
    y = y + dy;

}

public Rectangle ball() {
    return new Rectangle(getX(), getY(), diameter(), diameter());
}

public Image getImage() {
    return ballImg;
}

public int getX() {
    return x;
}

public int getY() {
    return y;
}

public int diameter() {
    return diameter;
}

public void velocityX(int vx) {
    this.dx = vx;
}

public void velocityY(int vy) {
    this.dy = vy;
}

public void bounceX(Paddle p) {
    double angle = atan(dx / dy);
    if (dx < 0) {
        angle += PI;
    }
    double mag = sqrt(pow(dy, 2) + pow(dx, 2));
    dx *= -1;
}

public void bounceX() {
    dx *= -1;
}

public void bounceY(Paddle p) {
    double angle = atan(dx / dy);
    if (dy < 0) {
        angle += PI;
    }
    double mag = sqrt(pow(dy, 2) + pow(dx, 2));
    dy += -1;
}

public void bounceY() {
    dy *= -1;
}

}



UltraPongCopy

import javax.swing.JFrame;

public class UltraPongCopy {

   public UltraPongCopy() {
    JFrame f = new JFrame();
    MainConfig mc = new MainConfig();
    f.add(mc);
    f.setTitle("UltraPong");
    f.setSize(mc.setSizeX , mc.setSizeY);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setVisible(true);
    f.setResizable(false);
}

public static void main(String[] args) {
    UltraPongCopy ultraPongCopy = new UltraPongCopy();
}

}



Without look at much of your code, what exactly is going wrong?

Every time my paddle intersects the tile, it goes through.

You need to check your variables of all your rects/x/y etc…

debug or do a system.out or something so you can manually see ALL the variables involved in the collision
x/y/width/height of ball rect and of your paddle rect

Are you sure you have a proper width/height set in your paddle rect?


....
  public Paddle(int i) {
        paddleImage = new ImageIcon(this.getClass().getResource("/ultrapong/Paddle" + i + ".png")).getImage();
        width = paddleImage.getWidth(null);
        height = paddleImage.getHeight(null);
...

Do all the other aspects of your code work? i.e. does the ball bounce off the sides and top? does it bounce off the blocks?
does anything thats not supposed to go outside the screen, can go outside the screen?

Thanks for reminding me to check again, I forgot to set the size of t13’s rectangle width and height. But even though that, I made a System.out.println in Level’s checkCollision(), and even though the system says “Has Intersected”, it still couldn’t return paddleRec()'s x and width.

Method checkCollision() in Level.


    public void checkCollision(Rectangle p, Rectangle t) {
        if (recColElements != null) {
            for (int i = 0; i < recColElements.length; i++) {
                if (t.intersects(p)) {
                    System.out.println("Has intersected");
                    if (p.x + p.width >= t.x) {
                        p.x = t.x - p.width;
                    }
                    if (p.x <= t.x + t.width) {
                        p.x = t.x + t.width;
                    }

                }
            }
        }
    }

The next problem I see is that when you have them intersect, you have it multiply dy *= -1

The problem with this, is that sometimes the rectangle in 1 move goes far enough inside the other rectangle that it tries to go “up” but is still inside the other, so thereby intersects a 2nd time and flips it back to down. and this repeats and it behaves oddly. Sometimes itll fly “right through it” sometimes itll jitter around really strangely

You should have a “hitPaddle()” function that forces it to be upward moving, and not have it do a *=-1 but a straight =-1 or whatever value. So that even if its slightly “overlapping” itll still continue on an upward path. Then just use a different function for your other direction

Thanks, I’ll try it out.

I’m not sure I did it right,but is it ok if I do it this way.


public void bounceY() {
        dy = dy + -(dy * 2);
    }

unless I am mistaken you can make that a lot smaller, and just be

dy-=2dy

Sorry,the java compiler doesn’t like it.

If your curious, this is what it said

Exception in thread "Thread-2" java.lang.RuntimeException: Uncompilable source code - not a statement
	at ultrapong.Ball.bounceY(Ball.java:127)
	at ultrapong.Ball.updateBounce(Ball.java:40)
	at ultrapong.MainConfig.updatePosistion(MainConfig.java:61)
	at ultrapong.MainConfig.run(MainConfig.java:73)
	at java.lang.Thread.run(Thread.java:722)

The way you have your intersect code written, I was suggesting a bounceYUp() and bounceYDown()

Although I haven’t personally tested the code, Ive only looked at it, the problem arises when the rectangle intersects the same rectangle several frames in a row, and the bounceY is called 4 or 5 times during its interaction. That is why I was suggesting to not use a function/method that flip/flips back and forth. In some cases that flip/flop function works fine, but the way its set up currently, It could cause potential problems.

An easy way to test this, is to add a "system.out.println(“bounce”) inside your bounceY code and see if it is triggered multiple times when it hits the paddle.

Thanks for your reply :smiley:

I did some variations and made the code work.

New Variable


public int hit = 0;

Edited bounceY()


public void bounceY() {
        if (hit == 0) {
            dy *= -1;
            hit++;
        }else{
            dy *= 1;
        }
    }

If you don’t mind, is their a way, so that when paddleRec() intersect recColElements[], it dosen’t go through recColElements[].

what do you mean
so that when paddleRec() intersect recColElements[], it dosen’t go through recColElements[].

You can have it just exit the for loop once it intersects one.

Like,when paddleRec() intersects a rectangle from recColElements[], the paddle goes right through the actual rectangle.

Sorry If I didn’t make sense, its kinda hard to explain.

This is what it looks like.

Looking at your Element class, I don’t see the height and width getting set. The int will default to zero, which would explain why you are not getting a collision with them. Get the height and width from the image passed in the constructor. That should help.

Actually, I already fixed that on my third post in this thread, but thanks.

I’ve fixed some of collision between the paddle and recColElements[]. But the new problem is, that every time my paddle intersects the right of a rectangle from recColElements[], it teleports to the left of it.

Heres the code


public void checkCollision(Player p, Level r) {
        for (int i = 0; i < r.recColElements.length; i++) {
            int recLeftBorder = r.recColElements[i].x;
            int recRightBorder = r.recColElements[i].x + r.recColElements[i].width;
            System.out.println("Has intersected");
            if (p.paddleRec().intersects(r.recColElements[i]) && p.x + p.width >= recLeftBorder) {
                p.x = recLeftBorder - p.width;
                System.out.println("right");
            } else if (p.paddleRec().intersects(r.recColElements[i]) && p.x <= recRightBorder) {
                System.out.println("left");
                p.x = recRightBorder;

            }

        }
    }

Picture for reference

I’ve finally found a solution to my bug ;D

Here is the checkCollisionCode


    public void checkCollision(Player p, Level r) {
        for (int i = 0; i < r.recColElements.length; i++) {
            int recLeftBorder = r.recColElements[i].x;
            int recRightBorder = r.recColElements[i].x + r.recColElements[i].width;
            int middleBorder = r.recColElements[i].x + (r.recColElements[i].width/2);
            System.out.println("Has intersected");
            if (p.paddleRec().intersects(r.recColElements[i]) && p.x + p.width > recLeftBorder && p.x + p.width < middleBorder) {
                p.x = recLeftBorder - p.width;
            }
            if (p.paddleRec().intersects(r.recColElements[i]) && p.x < recRightBorder && p.x > middleBorder) {
                p.x = recRightBorder;
            }

        }
    }