Java矩形碰撞检测混淆

4
我用Bucky的Java教程做了一个简单的2D状态变化游戏,现在我修改了这个游戏,想在地图上设置碰撞,以便我的玩家不能穿过地图上的房子。我认为我有点明白碰撞是如何工作的:
你可以使用以下代码制作两个矩形:
public Rectangle getBounds() {
 return new Rectangle(x, y, width, height);

}

对玩家和障碍物分别创建一个矩形,您应该如何将此放入代码中,以及如何告诉Java障碍物的矩形与玩家矩形不同?
然后,在创建这两个矩形之后,您可以设置一个if语句,例如:如果相交,则执行某些操作...
希望在这之后能够正常工作。关于游戏的更多信息,它是一款状态变化游戏,具有几种方法,例如init、render和update(我应该将我的矩形和if语句放在update方法中吗?)。此外,它是一款俯视视角的游戏,类似于口袋妖怪。如果您需要我的代码,请告诉我,我不想在这里放太多内容。
package javagame;

import org.newdawn.slick.*;
import org.newdawn.slick.state.*;

public class Play extends BasicGameState{

    Animation bucky, movingUp, movingDown, movingLeft, movingRight, movingBL, movingBR, movingFL, movingFR;
    Image worldMap;
    boolean quit = false;//gives user to quit the game
    int[] duration = {200, 200};//how long frame stays up for
    float buckyPositionX = 0;
    float buckyPositionY = 0;
    float shiftX = buckyPositionX + 320;//keeps user in the middle of the screem
    float shiftY = buckyPositionY + 160;//the numbers are half of the screen size

    public Play(int state){
    }   
    public void init(GameContainer gc, StateBasedGame sbg) throws SlickException{
          worldMap = new Image("res/world.png");
          Image[] walkUp = {new Image("res/b.png"), new Image("res/b.png")}; //these are the images to be used in the "walkUp" animation
          Image[] walkDown = {new Image("res/f.png"), new Image("res/f.png")};
          Image[] walkLeft = {new Image("res/l.png"), new Image("res/l.png")};
          Image[] walkRight = {new Image("res/r.png"), new Image("res/r.png")};
          Image[] walkBL = {new Image("res/bl.png"), new Image("res/bl.png")};
          Image[] walkBR = {new Image("res/br.png"), new Image("res/br.png")};
          Image[] walkFL = {new Image("res/fl.png"), new Image("res/fl.png")};
          Image[] walkFR = {new Image("res/fr.png"), new Image("res/fr.png")};

    movingUp = new Animation(walkUp, duration, false);
    movingDown = new Animation(walkDown, duration, false);  
    movingLeft = new Animation(walkLeft, duration, false);  
    movingRight = new Animation(walkRight, duration, false);
    movingBL = new Animation(walkBL, duration, false);
    movingBR = new Animation(walkBR, duration, false);
    movingFL = new Animation(walkFL, duration, false);
    movingFR = new Animation(walkFR, duration, false);
    bucky = movingDown;//facing screen initially on startup
    }


    public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException{
    worldMap.draw(buckyPositionX, buckyPositionY);//position 0,0
    bucky.draw(shiftX, shiftY);//makes him appear at center of map
    g.drawString("Suraj's X: "+buckyPositionX+"\nSuraj's Y: "+buckyPositionY,400,20);//tells us the position

    if(quit==true){
        g.drawString("Resume(R)", 250, 100);
        g.drawString("Main(M)", 250, 150);
        g.drawString("Quit Game(Q)", 250, 200);
        if(quit==false){
            g.clear();//wipe off everything from screen
        }
    }
    }

    public void update(GameContainer gc, StateBasedGame sbg, int delta)throws SlickException{
    Input input = gc.getInput();
    //up
    if(input.isKeyDown(Input.KEY_UP)){
        bucky = movingUp;//changes the image to his back
        buckyPositionY += 10;;//increase the Y coordinates of bucky (move him up)
        if(buckyPositionY>162){//if I reach the top 
            buckyPositionY -= 10;//stops any further movement in that direction
        }
    }

    //down
    if(input.isKeyDown(Input.KEY_DOWN)){
        bucky = movingDown;
        buckyPositionY -= 10;
        if(buckyPositionY<-600){
            buckyPositionY += 10;//basically change the direction if + make -
    }}
    //left
    if(input.isKeyDown(Input.KEY_LEFT)){
        bucky = movingLeft;
        buckyPositionX += 10;
        if(buckyPositionX>324){
            buckyPositionX -= 10;//delta * .1f
    }}
    //right
    if(input.isKeyDown(Input.KEY_RIGHT)){
        bucky = movingRight;
        buckyPositionX -= 10;
        if(buckyPositionX<-840){
            buckyPositionX += 10;
    }}




    //2 key combos start here
    if(input.isKeyDown(Input.KEY_RIGHT) && input.isKeyDown(Input.KEY_UP)){
        bucky = movingBR;
        buckyPositionX -= delta * .1f;
        if(buckyPositionX<-840){
            buckyPositionX += delta * .1f;
            if(buckyPositionY>162){
                buckyPositionY -= delta * .1f;
            }}}
    if(input.isKeyDown(Input.KEY_LEFT) && input.isKeyDown(Input.KEY_UP)){
        bucky = movingBL;
        buckyPositionX -= delta * .1f;
        if(buckyPositionX>324){
            buckyPositionX -= delta * .1f;
            if(buckyPositionY>162){
                buckyPositionY -= delta * .1f;
            }}}
    if(input.isKeyDown(Input.KEY_RIGHT) && input.isKeyDown(Input.KEY_DOWN)){
        bucky = movingFR;
        buckyPositionX -= delta * .1f;
        if(buckyPositionY<-600){
            buckyPositionY += delta * .1f;
            if(buckyPositionX<-840){
                buckyPositionX += delta * .1f;
            }}}
    if(input.isKeyDown(Input.KEY_LEFT) && input.isKeyDown(Input.KEY_DOWN)){
        bucky = movingFL;
        buckyPositionX -= delta * .1f;
        if(buckyPositionY<-600){
            buckyPositionY += delta * .1f;
            if(buckyPositionX>324){
                buckyPositionX -= delta * .1f;
            }}}

     //escape
    if(input.isKeyDown(Input.KEY_ESCAPE)){
        quit=true;
    }
    //when the menu is up
    if(quit==true){//is the menu on the screen
        if(input.isKeyDown(Input.KEY_R)){
            quit = false;//resumes the game, makes menu dissapear
        }
        if(input.isKeyDown(Input.KEY_M)){
            sbg.enterState(0);//takes you to the main menu
        }
        if(input.isKeyDown(Input.KEY_Q)){
            System.exit(0);//quits the game
        }
    }

}

    public int getID(){
        return 1;
    }
}

这是我的Play类,我只有另外两个类,一个是main类,另一个是menu类。我无法想象把矩形方法放在main或menu类中,所以唯一剩下的就是Play类了,但我不知道如何在我已经编写的代码中创建2个不同的矩形(一个用于玩家,另一个用于房子)。如果您需要我的main和menu类,请告诉我。
编辑3:
我尝试了您所说的,并制作了一个Rectanglebase类,并将您发布的if语句放在其中,但是我遇到了错误,它要求我在我的player类中编写getX和getY方法,同时构造函数前面的public也有错误。
public Rectanglebase{}//the public is saying syntax error

我也按照你说的创建了一个Home和Player类,但是我有些困惑需要在其中放些什么内容。在Home类中我写了以下内容:
return Rectangle(100,100,100,100);

但我一直在遇到错误,不确定我的操作是否正确。此外,在玩家类的x、y位置中,我该如何从Play类设置我的浮点变量到玩家类?
2个回答

7
这是一个关于游戏循环/游戏逻辑和使用Rectangle2D#intersects(..)方法进行碰撞检测的示例。
它使用JPanel绘制所有内容,并使用Rectangle2D作为Entity类(任何需要绘制到GamePanel的对象)。
updateGame()方法中,您将找到碰撞检查:
    private void updateGame() {

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

您是玩家1,使用WASD移动。当您与玩家2相交时,println()将确认相交。

输入图像描述

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

1
在键绑定中加上+1;同时考虑在“实体”中使用“形状#相交()”。 - trashgod
@trashgod +1 感谢您的建议,我已经更新了代码,使用 Rectangle2D#intersects 替代了 JPanel(因为移动时它会导致重绘次数激增)。我没有使用 Shape,因为那些被覆盖的方法让我感到困惑,例如 pathIterator 等 :O) - David Kroukamp
1
太好了!如果不需要典型的Shape实现的完全通用性,那么Rectangle2D#intersects可能会更有效率。 - trashgod

0

if(house.getBounds().contains(player.getX(),player.getY()){//do something}

只要你的房子和玩家矩形在不同的类中定义,Java 就能区分它们

首先创建一个用于处理矩形的基类:

public class Rectanglebase{

public void getBounds(){//write method}

//write other methods you will need to use for both rectangles here

public Rectanglebase{//default constructor}

}//end class definition

现在编写房屋和玩家的类:
public class House extends Rectanglebase{
//getBounds() is inherited, so just write stuff to do with the graphics of the house here
}

当你在主代码中生成房子时,你可以自己创造:

House house = new House();

然后以类似的方式生成玩家的类,然后在你的主代码中构建它:

Player player = new Player()

houseplayer是不同的变量,这就是Java如何区分你的房子和玩家的方式。


我现在明白了if语句,谢谢你的回复。但是我不明白如何设置我的矩形,这是我的代码:(抱歉,将在下一篇帖子中发布) - Ben Dover
谢谢您的帮助,我添加了一个新答案,因为我的问题无法适应这个小框。以防您找不到它,它在页面顶部。 - Ben Dover

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接