静止物体与运动物体之间的碰撞

时间:2018-06-19 18:43:03

标签: java swing collision maze

我正在制作一个迷宫游戏,在这里,我有一个简单的地图...

直到没有问题,但我的问题是碰撞

这是我的地图

enter image description here

在这里,我使用以下代码自己构建了地图:

package maze;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;

public class Maze1 extends GameObject{

    public Maze1(int x, int y, ID id) {
        super(x, y, id);
    }

    public void tick() {

    }

    public void render(Graphics g){
        Font fnt = new Font("arial", 1, 25);
        g.setFont(fnt);
        g.drawString("Level : 1",165 , 433);

        //x, y, WIDTH, HEIGHT
        g.setColor(Color.darkGray); 
        g.fillRect(65, 65, 130 ,15); 
        g.fillRect(0, 130, 145 ,15); 
        g.fillRect(195, 195, 400 ,15);
        g.fillRect(65, 195, 80 ,15); 
        g.fillRect(130, 260, 80 ,15); 
        g.fillRect(65, 325, 195 ,15); 
        g.fillRect(325, 325, 80 ,15); 

        // x, y, WIDTH, HEIGHT

        g.fillRect(195, 130, 80 ,80); 
        g.fillRect(260, 0, 145 ,80);  

        //x, y, HEIGHT, WIDTH

        g.fillRect(65, 145, 15 ,130); 
        g.fillRect(195, 65, 15 ,195); 
        g.fillRect(260, 260, 15 ,130);
        g.fillRect(325, 130, 15 ,80); 
        g.fillRect(325, 260, 15 ,65); 


        g.setColor(Color.black);
        g.fillRect(0, -20, 500 ,32); //x, y, WIDTH, HEIGHT
        g.fillRect(0, 390, 500 ,15); 
        g.fillRect(0, Game.HEIGHT - 41, 500 ,30); 
        g.fillRect(-20, 0, 32 ,500);
        g.fillRect(Game.WIDTH -18, 0, 32 ,340);
        g.fillRect(Game.WIDTH -18, 400, 32 ,100);
    }
}

此碰撞代码用于右上角矩形

我尝试了两种不同类型的碰撞代码,但没有一种会起作用

冲突代码:

public void collision() {
    if(this.getX() >=229 && this.getY() <= 79) {
        if(this.getX() >=229) {
            this.setVelX(0);
            this.setVelY(0);
            this.setX(this.getX() -1);
        }

        if(this.getY() <= 79){
            this.setVelX(0);
            this.setVelY(0);
            this.setY(this.getY() +1);
        }       
    }

这里,当玩家从左侧击中矩形时,玩家将向下移动;当玩家从底部击中矩形时,玩家将向左侧移动。

我已经用if,else if语句尝试了相同的代码,但仍然无法正常工作。当玩家击打左侧时,效果很好,但是当玩家从底部击打矩形时,它会传送到矩形的边缘。

希望我的描述足够清楚,以便您了解我的情况。

提前谢谢!

1 个答案:

答案 0 :(得分:1)

对于碰撞检测,我建议将地图数据保留在矩形列表中。像这样:

public class Rect {
    public int topLeftX;
    public int topLeftY;
    public int width;
    public int height;

    public Rect(int x, int y, int w, int h){
        topLeftX = x;
        topLeftY = y;
        width = w;
        height = h;
    }


    //Ignore the code below here for now


    public boolean collidingWith(int x, int y, int width, int height){ 
        Rect r = new Rect(x, y, width, height);
        boolean rLeftInWidth = r.topLeftX > topLeftX && r.topLeftX < topLeftX + width;
        boolean rRightInWidth = r.topLeftX + r.width > topLeftX && r.topLeftX + width < topLeftX + width;
        boolean rTopInHeight = r.topLeftY > topLeftY && r.topLeftY < topLeftY + width;
        boolean rBottomInHeight = r.topLeftY + r.width > topLeftY && r.topLeftY + width < topLeftY + width;


        if(rLeftInWidth || rRightInWidth){
            //If there is horizantal overlap between the other rectangle and this one
            if(rTopInHeight || rBottomInHeight){
                //If there is also vertical overlap then we must be colliding
                return true;
            }
        }
        return false;
    }
}

然后将矩形保存在数组或某种类型的集合中,ArrayList可能最简单。

ArrayList<Rect> wallData = new ArrayList<Rect>();

然后您可以添加地图数据。这是从您的矩形画中提取的:

g.fillRect(65, 65, 130 ,15);

它变成具有相同参数的矩形。您甚至可以在填充矩形并重复使用之前执行此操作。

wallData.add(new Rect(65, 65, 130, 15));
Rect r = wallData.get(0);
g.fillRect(r.topLeftX, r.topLeftY, r.width, r.height);

那么,既然您已经有了这些数据,您将如何使用它?当您要检查碰撞时,只需遍历矩形列表并检查玩家是否在碰撞。

for(Rect rectangle: wallData){
    if(rectangle.collidingWith(<playerX>, <playerY>, <playerWidth>, <playerHeight>)){
        //Your player is colliding with a rectangle!
    }
}

还记得我之前告诉您忽略的那一部分吗?让我们现在来看一下。 我发誓,它比看起来简单!

    public boolean collidingWith(int x, int y, int width, int height){ 
        Rect r = new Rect(x, y, width, height);
        boolean rLeftInWidth = r.topLeftX > topLeftX && r.topLeftX < topLeftX + width;
        boolean rRightInWidth = r.topLeftX + r.width > topLeftX && r.topLeftX + width < topLeftX + width;
        boolean rTopInHeight = r.topLeftY > topLeftY && r.topLeftY < topLeftY + width;
        boolean rBottomInHeight = r.topLeftY + r.width > topLeftY && r.topLeftY + width < topLeftY + width;


        if(rLeftInWidth || rRightInWidth){
            //If there is horizantal overlap between the other rectangle and this one
            if(rTopInHeight || rBottomInHeight){
                //If there is also vertical overlap then we must be colliding
                return true;
            }
        }
        return false;
    }
}

我们首先确定是否存在水平重叠。例如,以下内容是水平重叠的,但不是垂直重叠的。

=-----=
-     -
-     -
-------
   +------
   -     -
   -     -
   -------

您可以知道,因为带有加号的角位于带有等号的角之间。四个布尔值仅用于确定我们的拐角是否位于另一个对象的拐角之间。

boolean rLeftInWidth = r.topLeftX > topLeftX && r.topLeftX < topLeftX + width;

如果我们的左上角位于其他矩形的左上角和右上角之间,则这变为true。然后,我们也会在右下角执行该操作。如果其中任何一个是正确的,那我们就水平重叠了!

从那里,我们仅检查它是否在水平和垂直方向上重叠。如果是这样,它就会像这样碰撞!

-------
-     -
-   ------
-----    -
    -    -
    ------

现在,当您意识到播放器正在碰撞时,您会怎么做?这需要一些工作,但没什么特别的。在通过将播放器的速度添加到位置来移动播放器之前,只需保存其位置即可。

private int playerX;
private int playerY;

<method where you change it>{
    playerX = <playerX>;
    playerY = <playerY>;
    <change their location according to velocity>
}

现在,如果玩家与墙壁碰撞,只需将其位置设置回刚才保存的值,并将其速度设置为0!

设置起来有点复杂,但是一旦完成,这是一个不错的解决方案。

在这个答案中,我在<>之间放了一堆东西。那只是意味着您需要该值,或者这就是它所说的。

希望对您有所帮助!如果您有任何疑问,请随时提出,我很乐意提供更多帮助。碰撞检测是一个复杂的主题,有很多方法。通常,这是我发现最简单的应用程序之一。

与您的原始方法相比,这种方法的优点:

  • 您可以在绘制之前设置矩形,并将其用于绘制以及碰撞检测。

如果创建了矩形列表,则可以使用循环将其绘制:

for(Rect r: wallData){
    g.fillRect(r.topLeftX, r.topLeftY, r.width, r.height);
}
  • 易于编辑:如果您决定更改地图,则只需更改一些矩形的宽度,高度和位置,而不必经过一堆if语句来尝试确定是否合适。更改。 (我之前已经讲过)

  • 容易出错:为每个墙设置一些矩形要比设置一堆if语句容易得多。

这种方法与您的原始方法的缺点

  • 这是效率较低的-有更多操作可以检查播放器是否与矩形碰撞,而不是2 ifs。另外,可能会有玩家无法与之碰撞的矩形。但是,效率损失非常小,尤其是在地图中的矩形数量较少的情况下。

  • 这个比较复杂。最好了解您使用的代码,而不是盲目地复制粘贴。如果您需要进行更改,那么如果您不了解,就会更加困难。 (不是您不知道,这可能会有点难。)