Java rectangle collision detection confusion

Here is an example of a Game Loop / Game Logic and collision detection via Rectangle2D#intersects(..) method .

It uses JPanel to draw everything on and Rectangle2D is used for Entity class (which is any object needed to be drawn to GamePanel which is our JPanel where everything is drawn).

The updateGame() method is where you will find the collision checking:

    private void updateGame() {

        if (entities.get(0).intersects(entities.get(1))) {
            System.out.println("Intersecting");
        }
         ....
    }

You are Player 1 and move with W,A,S,D. When you intersect Player 2, a println() will confirm the intersection.

enter image description here

GameLogic.java:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

/**
 *
 * @author David Kroukamp
 */
public class GameLogic {

    public GameLogic() {
        initComponents();
    }
    final GamePanel gp = new GamePanel(500, 500);

    private void initComponents() {

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Entity entity1 = new Entity(100, 100, 100, 100, createWhiteImage());
        Entity entity2 = new Entity(300, 200, 100, 100, createBlackImage());

        gp.addEntity(entity1);
        gp.addEntity(entity2);//just a standing still JPanel

        setGamePanelKeyBindings(gp, entity1);

        frame.add(gp);

        frame.pack();
        frame.setVisible(true);

        //start the game loop which will repaint the screen
        runGameLoop();
    }
    //Starts a new thread and runs the game loop in it.

    public void runGameLoop() {
        Thread loop = new Thread(new Runnable() {
            @Override
            public void run() {
                gp.running.set(true);
                gp.gameLoop();
            }
        });
        loop.start();
    }

    private void setGamePanelKeyBindings(GamePanel gp, final Entity entity) {
        gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("D"), "D pressed");
        gp.getActionMap().put("D pressed", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                entity.RIGHT = true;
            }
        });

        gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("A"), "A pressed");
        gp.getActionMap().put("A pressed", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                entity.LEFT = true;
            }
        });

        gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("W"), "W pressed");
        gp.getActionMap().put("W pressed", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                entity.UP = true;
            }
        });

        gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("S"), "S pressed");
        gp.getActionMap().put("S pressed", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                entity.DOWN = true;
            }
        });
        gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released D"), "D released");
        gp.getActionMap().put("D released", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                entity.RIGHT = false;
            }
        });

        gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released A"), "A released");
        gp.getActionMap().put("A released", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                entity.LEFT = false;
            }
        });

        gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released W"), "W released");
        gp.getActionMap().put("W released", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                entity.UP = false;
            }
        });

        gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released S"), "S released");
        gp.getActionMap().put("S released", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                entity.DOWN = false;
            }
        });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new GameLogic();
            }
        });
    }

    private BufferedImage createWhiteImage() {
        BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = img.createGraphics();
        g2.setColor(Color.WHITE);
        g2.fillRect(0, 0, img.getWidth(), img.getHeight());
        return img;
    }

    private BufferedImage createBlackImage() {
        BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = img.createGraphics();
        g2.setColor(Color.BLACK);
        g2.fillRect(0, 0, img.getWidth(), img.getHeight());
        return img;
    }
}

class Entity extends Rectangle2D.Double {

    private int speed = 5;
    public boolean UP = false,
            DOWN = false,
            LEFT = false,
            RIGHT = false;
    private final BufferedImage image;

    public Entity(int x, int y, int width, int height, BufferedImage image) {
        super(x, y, width, height);
        this.width = width;
        this.height = height;
        this.image = image;
    }

    public BufferedImage getImage() {
        return image;
    }

    public void move() {
        if (UP) {
            y -= speed;
        }
        if (DOWN) {
            y += speed;
        }
        if (LEFT) {
            x -= speed;
        }
        if (RIGHT) {
            x += speed;
        }

    }
}

class GamePanel extends JPanel {

    private int width, height;
    private int frameCount = 0;
    private int fps = 0;
    public static AtomicBoolean running = new AtomicBoolean(false), paused = new AtomicBoolean(false);
    final ArrayList<Entity> entities = new ArrayList<>();

    GamePanel(int w, int h) {
        super(true);
        setIgnoreRepaint(true);//mustnt repaint itself the gameloop will do that
        setLayout(null);
        width = w;
        height = h;
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(width, height);
    }

    public void addEntity(Entity e) {
        entities.add(e);
    }

    //Only run this in another Thread!
    public void gameLoop() {
        //This value would probably be stored elsewhere.
        final double GAME_HERTZ = 30.0;
        //Calculate how many ns each frame should take for our target game hertz.
        final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
        //At the very most we will update the game this many times before a new render.
        //If you're worried about visual hitches more than perfect timing, set this to 1.
        final int MAX_UPDATES_BEFORE_RENDER = 5;
        //We will need the last update time.
        double lastUpdateTime = System.nanoTime();
        //Store the last time we rendered.
        double lastRenderTime = System.nanoTime();

        //If we are able to get as high as this FPS, don't render again.
        final double TARGET_FPS = 60;
        final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;

        //Simple way of finding FPS.
        int lastSecondTime = (int) (lastUpdateTime / 1000000000);

        while (running.get()) {
            double now = System.nanoTime();
            int updateCount = 0;

            if (!paused.get()) {
                //Do as many game updates as we need to, potentially playing catchup.
                while (now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER) {
                    updateGame();
                    lastUpdateTime += TIME_BETWEEN_UPDATES;
                    updateCount++;
                }

                //If for some reason an update takes forever, we don't want to do an insane number of catchups.
                //If you were doing some sort of game that needed to keep EXACT time, you would get rid of this.
                if (now - lastUpdateTime > TIME_BETWEEN_UPDATES) {
                    lastUpdateTime = now - TIME_BETWEEN_UPDATES;
                }

                drawGame();
                lastRenderTime = now;

                //Update the frames we got.
                int thisSecond = (int) (lastUpdateTime / 1000000000);

                if (thisSecond > lastSecondTime) {
                    System.out.println("NEW SECOND " + thisSecond + " " + frameCount);
                    fps = frameCount;
                    frameCount = 0;
                    lastSecondTime = thisSecond;
                }

                //Yield until it has been at least the target time between renders. This saves the CPU from hogging.
                while (now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES) {
                    //allow the threading system to play threads that are waiting to run.
                    Thread.yield();

                    //This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
                    //You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
                    //FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this.
                    //On my OS it does not unpuase the game if i take this away
                    try {
                        Thread.sleep(1);
                    } catch (Exception e) {
                    }

                    now = System.nanoTime();
                }
            }
        }
    }

    private void drawGame() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                repaint();
            }
        });
    }

    private void updateGame() {

        if (entities.get(0).intersects(entities.get(1))) {
            System.out.println("Intersecting");
        }

        for (Entity e : entities) {
            e.move();
        }
    }

    @Override
    protected void paintComponent(Graphics grphcs) {
        super.paintComponent(grphcs);
        Graphics2D g2d = (Graphics2D) grphcs;

        applyRenderHints(g2d);

        g2d.setColor(Color.GREEN);
        g2d.fillRect(0, 0, getWidth(), getHeight());

        for (Entity e : entities) {
            g2d.drawImage(e.getImage(), (int) e.getX(), (int) e.getY(), null);
        }

        g2d.setColor(Color.BLACK);
        g2d.drawString("FPS: " + fps, 5, 10);

        frameCount++;
    }
    private final static RenderingHints textRenderHints = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    private final static RenderingHints imageRenderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    private final static RenderingHints colorRenderHints = new RenderingHints(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
    private final static RenderingHints interpolationRenderHints = new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    private final static RenderingHints renderHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

    public static void applyRenderHints(Graphics2D g2d) {
        g2d.setRenderingHints(textRenderHints);
        g2d.setRenderingHints(imageRenderHints);
        g2d.setRenderingHints(colorRenderHints);
        g2d.setRenderingHints(interpolationRenderHints);
        g2d.setRenderingHints(renderHints);
    }
}

Leave a Comment