首页 > 解决方案 > 多个矩形的 Java 2D Platformer 碰撞检测

问题描述

我正在使用 Java 开发 2D 平台游戏以完成任务。作业指定我必须使用抽象形状类来绘制形状。我遇到的问题是让我的碰撞检测与我用作平台的多个矩形对象一起工作 - 我将它们存储在一个列表中。目前,无论我与哪个平台发生碰撞,我的碰撞检测都会移动我的玩家,所以如果我从右侧与一个平台发生碰撞,它会将我移动到该平台的顶部,因为它仍在检查我下方的另一个平台,并且会假设我已经做到了,因此将我移到平台的顶部。我想知道如何更改这一点,以便我的碰撞检测也检测到我与哪个平台发生碰撞并相对于该平台(而不是每个平台)发生碰撞。

这是目前正在发生的事情: 在此处输入图像描述

预期输出:

我的播放器在与平台碰撞时应该停在原地。

我的应用类:

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) { }
}

我的输入处理类:

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) { }



}

我的播放器类:

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);


    }
}

我的矩形类:

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);

    }
}

标签: javacollision-detectionintersectionrectangles

解决方案


首先,定义ColorRectangle扩展Shape并提供绘图逻辑的类:

// 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);
    }
}

其次,为表示Player和定义单独的类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);
    }
}

第三,定义包含演示应用逻辑的类。实际上,你可以拆分 demo 的逻辑和 board 的逻辑(包括 actiona 和 key listeners),但是这些类非常简单,所以在给定的情况下,不需要拆分它。

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));
    }
}

推荐阅读