1
votes

I am working on a 2D platformer in Java for an assignment. The assignment specifies I must use an abstract shape class to draw shapes. The problem I am having is getting my collision detection to work with multiple Rectangle Objects which I am using as platforms - I store these in a list. At the moment my collision detection will move my player regardless of which platform I have collided with, so if I collide with a platform from the right it will move me to the top of that platform because it's still checking another platform below me, and will assume I've hit that, therefore moving me to the top of the platform. I was wondering how I can change this so that my collision detection also detects which platform I have collided with and collide relative to that platform (as opposed to every platform).

Here's what is currently happening: enter image description here

Expected Output:

My player should stop where it is when colliding with platforms.

My App class:

package A2;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;

public class App extends JFrame {

    public App() {
        final Player player = new Player(200, 100);

        final ArrayList<Rectangle> platforms = new ArrayList<>();

        platforms.add(new Rectangle(100, 500, 400 ,10));
        platforms.add(new Rectangle(500, 100, 10 ,400));




        JPanel mainPanel = new JPanel() {
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                player.draw(g);

                for(Rectangle r : platforms){
                    r.draw(g);
                }

            }
        };

        mainPanel.addKeyListener(new InputControl(this, player, platforms));
        mainPanel.setFocusable(true);
        add(mainPanel);

        setLayout(new GridLayout());
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setSize(600, 600);
    }

    public static void main(String[] args) {
        App app = new App();
        app.setVisible(true);
    }
}



abstract class Shape {
    public void draw(Graphics g) { }
}

My input handling class:

package A2;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;

import static java.awt.event.KeyEvent.*;

public class InputControl implements ActionListener, KeyListener {
    App app;
    Player player;
    Timer time = new Timer(5, this);
    ArrayList<Rectangle> platforms;
    ArrayList<Integer> keyList = new ArrayList<>();

    boolean keyReleased = false;
    boolean keyPressed = false;
    boolean[] keyUp = new boolean[256];
    boolean[] keyDown = new boolean[256];

    boolean topCollision = false;
    boolean rightCollision = false;
    boolean leftCollision = false;
    boolean botCollision = false;
    boolean onGround = false;

    private static final double GRAVITY = 2;
    private static final int MAX_FALL_SPEED = 5;

    double Xoverlap = 0;
    double Yoverlap = 0;
    boolean collision = false;



    public InputControl(App app, Player player, ArrayList<Rectangle> platforms) {
        this.app = app;
        this.player = player;
        this.platforms = platforms;
        time.start();
    }

    public void gravity(){
        if(player.yVelocity > MAX_FALL_SPEED){
            player.yVelocity = MAX_FALL_SPEED;
        }
        player.yVelocity += GRAVITY;
    }

    public void momentum(){}


    public void actionPerformed(ActionEvent e) {

        Rectangle2D offset = player.getOffsetBounds();

        for(int i = 0; i < platforms.size(); i++) {
            Rectangle r = platforms.get(i);
            //If collision
            if (offset.intersects(r.obj.getBounds2D())) {
                //Collision on Y axis
                if (offset.getX() + offset.getWidth() > r.obj.getX() &&
                        offset.getX() < r.obj.getX() + r.obj.getWidth() &&
                        offset.getY() + offset.getHeight() > r.obj.getY() &&
                        offset.getY() + player.yVelocity < r.obj.getY() + r.obj.getHeight()) {
                    player.y -= (offset.getY() + offset.getHeight()) - r.obj.getY();
                }
                //Collision on X axis
                if (offset.getX() + offset.getWidth() + player.xVelocity > r.obj.getX() &&
                        offset.getX() + player.xVelocity < r.obj.getX() + r.obj.getWidth() &&
                        offset.getY() + offset.getHeight() > r.obj.getY() &&
                        offset.getY() < r.obj.getY() + r.obj.getHeight()) {
                    player.x -= (offset.getX() + offset.getHeight()) - r.obj.getX();

                }
            }
            else {
                player.x += player.xVelocity;
                player.y += player.yVelocity;
            }
        }


        player.x += player.xVelocity;
        player.y += player.yVelocity;

        app.repaint();
    }

    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() >= 0 && e.getKeyCode() < 256) {
            keyDown[e.getKeyCode()] = true;
            keyUp[e.getKeyCode()] = false;
            keyPressed = true;
            keyReleased = false;
        }
        if (keyDown[VK_UP]) {
            player.yVelocity = -1;

        }
        if (keyDown[VK_RIGHT]){
            player.xVelocity = 1;

        }
        if (keyDown[VK_LEFT]) {
            player.xVelocity = -1;

        }
        if (keyDown[VK_DOWN]) {
            player.yVelocity = 1;

        }
    }

    public void keyReleased(KeyEvent e) {
        if(e.getKeyCode() >= 0 && e.getKeyCode() < 256) {
            keyDown[e.getKeyCode()] = false;
            keyUp[e.getKeyCode()] = true;
            keyPressed = false;
            keyReleased = true;
        }
        if(keyUp[VK_RIGHT] || keyUp[VK_LEFT]){
            player.xVelocity = 0;
        }
        if(keyUp[VK_UP]){
            player.yVelocity = 0;
        }

    }
    public void keyTyped(KeyEvent e) { }



}

My Player class:

package A2;

import java.awt.*;
import java.awt.geom.Rectangle2D;

public class Player extends Shape {
    public double xVelocity;
    public double yVelocity;
    public double x;
    public double y;

    public double width = 60;
    public double height = 60;
    public double weight = 5;

    public  Rectangle2D obj;


    public Player(double x, double y) {
        this.x = x;
        this.y = y;
        obj = new Rectangle2D.Double(x, y, width, height);
    }

    public Rectangle2D getOffsetBounds(){
        return new Rectangle2D.Double( x, y, width, height);

    }

    public void draw(Graphics g) {
        Color c =new Color(1f,0f,0f,0.2f );
        obj = new Rectangle2D.Double(x, y, width, height);
        g.setColor(Color.BLACK);
        Graphics2D g2 = (Graphics2D)g;
        g2.fill(obj);
        g.drawString("(" + String.valueOf(x) + ", " + String.valueOf(y) + ")", 10, 20);
        g.drawString("X Speed: " + String.valueOf(xVelocity) + " Y Speed: " + String.valueOf(yVelocity) + ")", 10, 35);
        g.setColor(c);


    }
}

My Rectangle class:

package A2;

import java.awt.*;
import java.awt.geom.Rectangle2D;

public class Rectangle extends Shape {
    public double width;
    public double height;
    public double x;
    public double y;
    boolean hasCollided = false;

    public Rectangle2D obj;

    public Rectangle(double x, double y, double width, double height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        obj = new Rectangle2D.Double(x, y, width, height);
    }

    public void draw(Graphics g) {
        g.setColor(Color.ORANGE);
        Graphics2D g2 = (Graphics2D)g;
        g2.fill(obj);

    }
}
1
What exactly you want to achieve?oleg.cherednik
For my collision detection to work correctly, as in when my player moves into a platform, it stops where it is.Sean2148
I do not see that Rectangle has draw method. What is package?oleg.cherednik
I have updated the question, didn't realise I forgot to add the Rectangle class.Sean2148

1 Answers

0
votes

First, define ColorRectangle class that extends Shape and provide logic for drawing:

// extends java.awt.Shape
abstract class ColorRectangle extends Rectangle {

    private static final long serialVersionUID = -3626687047605407698L;
    private final Color color;

    protected ColorRectangle(int x, int y, int width, int height, Color color) {
        super(x, y, width, height);
        this.color = color;
    }

    public void draw(Graphics2D g) {
        g.setColor(color);
        g.fillRect(x, y, width, height);
    }
}

Second, define separate class for represent Player and Platform:

public final class Player extends ColorRectangle {

    private static final long serialVersionUID = -3909362955417024742L;

    public Player(int width, int height, Color color) {
        super(0, 0, width, height, color);
    }
}

public final class Platform extends ColorRectangle {

    private static final long serialVersionUID = 6602359551348037628L;

    public Platform(int x, int y, int width, int height, Color color) {
        super(x, y, width, height, color);
    }

    public boolean intersects(Player player, int offsX, int offsY) {
        return intersects(player.x + offsX, player.y + offsY, player.width, player.height);
    }
}

Third, define class that contains logic of demo application. Actually, you could split logic for demo and logic for board (including actiona and key listeners), but these classes are very simple, so in given case, no need to split it.

public class CollisionDetectionDemo extends JFrame {

    public CollisionDetectionDemo() {
        setLayout(new GridLayout());
        add(new MainPanel());
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setSize(600, 600);
    }

    private static class MainPanel extends JPanel implements ActionListener, KeyListener {

        private static final long serialVersionUID = 8771401446680969350L;
        private static final int OFFS = 5;

        private final Player player = new Player(60, 60, Color.BLACK);
        private final List<Platform> platforms = Arrays.asList(
                new Platform(100, 500, 400, 10, Color.ORANGE),
                new Platform(500, 100, 10, 410, Color.RED),
                new Platform(150, 300, 100, 10, Color.BLUE),
                new Platform(150, 100, 100, 10, Color.GREEN));

        private MainPanel() {
            player.x = 200;
            player.y = 200;
            new Timer(5, this).start();

            setFocusable(true);
            addKeyListener(this);
        }

        private void drawPlayerPosition(Graphics g) {
            g.setColor(Color.BLACK);
            g.drawString(String.format("(%d, %d)", player.x, player.y), 10, 20);
        }

        private void movePlayer(int offsX, int offsY) {
            if (!intersects(player, offsX, offsY)) {
                player.x += offsX;
                player.y += offsY;
            }
        }

        private boolean intersects(Player player, int offsX, int offsY) {
            for (Platform platform : platforms)
                if (platform.intersects(player, offsX, offsY))
                    return true;

            return false;
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);

            Color color = g.getColor();
            drawPlayerPosition(g);
            player.draw((Graphics2D)g);
            platforms.forEach(platform -> platform.draw((Graphics2D)g));
            g.setColor(color);
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            repaint();
        }

        @Override
        public void keyTyped(KeyEvent event) {
        }

        @Override
        public void keyPressed(KeyEvent event) {
            if (event.getKeyCode() == VK_UP)
                movePlayer(0, -OFFS);
            else if (event.getKeyCode() == VK_DOWN)
                movePlayer(0, OFFS);
            else if (event.getKeyCode() == VK_LEFT)
                movePlayer(-OFFS, 0);
            else if (event.getKeyCode() == VK_RIGHT)
                movePlayer(OFFS, 0);
        }

        @Override
        public void keyReleased(KeyEvent event) {
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new CollisionDetectionDemo().setVisible(true));
    }
}