碰撞检测仅适用于墙的顶部 - Java

Collision detection only working on top side of wall - Java

我有一个任务要创建一个 2D 游戏,这个游戏必须使用抽象的 class Shape 来绘制形状。我决定制作一个 2D 平台游戏,但这是我第一次做这样的事情。我正在尝试实施碰撞检测,并决定创建我的播放器对象的偏移量以查看它是否与我的矩形对象发生碰撞,如果发生碰撞则停止移动。这只适用于顶部,但在右侧、左侧和底部,玩家将穿过矩形,我不明白为什么。我希望碰撞检测适用于所有方面。我想知道如何更改我的碰撞检测以适用于矩形的所有边。

这是一个 gif 显示发生了什么:

我的应用程序 class:

package A2;

import javax.swing.*;
import java.awt.*;
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.TreeSet;

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

public class App extends JFrame {

    private static final int GRAVITY = 10;
    private static final int MAX_FALL_SPEED = 30;

    public App() {

        final Player player = new Player(200, 300);
        final Rectangle rectangle = new Rectangle(100, 400);
        JPanel mainPanel = new JPanel() {
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                player.draw(g);
                rectangle.draw(g);
            }
        };

        mainPanel.addKeyListener(new controlHandler(this, player, rectangle));
        mainPanel.setFocusable(true);
        add(mainPanel);

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


    class controlHandler implements ActionListener, KeyListener {
        int keyCode;
        App app;
        Player player;
        Rectangle rectangle;
        TreeSet<Integer> keys = new TreeSet<>();
        Timer time = new Timer(5, this);
        boolean collision = false;


        public controlHandler(App app, Player player, Rectangle rectangle) {
            this.app = app;
            this.player = player;
            this.rectangle = rectangle;
            time.start();
        }

        public void actionPerformed(ActionEvent e) {
            player.x += player.xVelocity;
            player.y += player.yVelocity;

            Rectangle2D offset = player.getOffsetBounds();

            if(offset.intersects(this.rectangle.wallObj.getBounds2D())){
                collision = true;
                player.xVelocity = 0;
                player.yVelocity = 0;
            }
            else collision = false;

            repaint();
        }

        public void keyPressed(KeyEvent e) {
            keyCode = e.getKeyCode();
            keys.add(keyCode);


            switch (keyCode) {
                case VK_UP:
                    moveUp();
                    break;
                case VK_DOWN:
                    moveDown();
                    break;
                case VK_LEFT:
                    moveLeft();
                    break;
                case VK_RIGHT:
                    moveRight();
                    break;
            }
        }

        public void keyReleased(KeyEvent e) {
            released();
        }
        public void keyTyped(KeyEvent e) { }

        public void moveUp() {
            player.xVelocity = 0;
            player.yVelocity = -2;
        }

        public void moveDown() {
            if(!collision) {
                player.xVelocity = 0;
                player.yVelocity = 2;
            }
        }

        public void moveLeft() {
            player.xVelocity = -2;
            player.yVelocity = 0;
        }

        public void moveRight() {
            player.xVelocity = 2;
            player.yVelocity = 0;
        }

        public void released() {
            player.xVelocity = 0;
            player.yVelocity = 0;
        }
    }

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

abstract class Shape {
    public double x;
    public double y;

    public void draw(Graphics g) { }
}

玩家class:

package A2;

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

public class Player extends Shape {
    public double xVelocity;
    public double yVelocity;
    public double length = 60;
    public double top;
    public double right;
    public double left;
    public double bottom;
    public  Rectangle2D playerObj;


    public Player(double x, double y) {
        this.x = x;
        this.y = y;
        playerObj = new Rectangle2D.Double(x, y, length, length);
    }

    public Rectangle2D getOffsetBounds(){
        return new Rectangle2D.Double(x + xVelocity , y + yVelocity , length, length);
    }

    public void draw(Graphics g) {
        playerObj = new Rectangle2D.Double(x, y, length, length);
        g.setColor(Color.BLACK);
        Graphics2D g2 = (Graphics2D)g;
        g2.fill(playerObj);

    }
}

矩形class(将是一个平台):

package A2;

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

public class Rectangle extends Shape {
    public double width = 400;
    public double height = 30;
    public double top;
    public double right;
    public double left;
    public double bottom;
    public Rectangle2D wallObj;

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

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

    }
}

因此,根据我的理解,您的碰撞检测过程基本上是创建对象的 "virtual" 实例并确定它是否会发生碰撞。

您的代码实际上是有效的,您可以通过物体 "stops" 在碰撞后移动这一事实来判断,但您没有做的是在它之后重置物体位置。

我们看一下原代码...

public void actionPerformed(ActionEvent e) {
  player.x += player.xVelocity;
  player.y += player.yVelocity;

  Rectangle2D offset = player.getOffsetBounds();

  if (offset.intersects(this.rectangle.wallObj.getBounds2D())) {
    collision = true;
    player.xVelocity = 0;
    player.yVelocity = 0;
  } else {
    collision = false;
  }

  repaint();
}
  • 更新对象的位置
  • 创建对象的虚拟实例,将速度应用于对象的当前位置...再次?
  • 判断物体是否碰撞
  • 停止运动
  • ...您在哪里重置对象的位置以使其不再发生碰撞?!?

相反,在确定是否发生碰撞之前,您不应设置对象的位置,例如...

public void actionPerformed(ActionEvent e) {

  Rectangle2D offset = player.getOffsetBounds();

  if (offset.intersects(this.rectangle.wallObj.getBounds2D())) {
    collision = true;
    player.xVelocity = 0;
    player.yVelocity = 0;
  } else {
    player.x += player.xVelocity;
    player.y += player.yVelocity;
    collision = false;
  }

  repaint();
}

不过,这种方法的问题是,如果速度足够大,物体看起来不会碰撞,但会在它之前停止一点,因为变化量大于剩余间隙。

您需要做的是,一旦您检测到碰撞,计算您想要移动的量与可用量 space 之间的差值,然后仅按该量移动对象