【问题标题】:Detect Collision of multiple BufferedImages Java检测多个 BufferedImages Java 的冲突
【发布时间】:2014-04-09 00:38:10
【问题描述】:

我正在用 java 制作 2d rpg 游戏,但遇到了问题。我可以让玩家在舞台上移动,舞台上也有岩石、树木、墙壁等。我不知道如何检测碰撞并使其到达玩家无法穿过物体的地方。读取地图文件并在画布上绘制图像的代码如下:

public void loadLevel(BufferedImage levelImage){
    tiles = new int[levelImage.getWidth()][levelImage.getHeight()];
    for (int y = 0; y < h; y++) {
        for (int x = 0; x < w; x++) {
            Color c = new Color(levelImage.getRGB(x, y));
            String h = String.format("%02x%02x%02x", c.getRed(),c.getGreen(),c.getBlue());

            switch(h){
            case "00ff00"://GRASS Tile - 1
                tiles[x][y] = 1;
                break;
            case "808080"://Stone -2
                tiles[x][y] = 2;
                break;
            case "894627"://Dirt -3
                tiles[x][y] = 3;
                break;
            case "404040"://Rock on Grass -4
                tiles[x][y] = 4;
                break;
            case "00b700"://Tree -5
                tiles[x][y] = 5;
                break;
            case"000000"://Wall -6
                tiles[x][y] = 6;
                break;
            case "cccccc"://Rock on stone -7
                tiles[x][y] = 7;
                break;
            default:
                tiles[x][y] = 1;
                System.out.println(h);
                break;

            }
        }
    }
}

而播放器类如下:

public class Player {

private int x,y;
public int locx,locy;
private Rectangle playerR;

private ImageManager im;
public boolean up =false,dn = false,lt=false,rt=false,moving = false,canMove = true;
private final int SPEED =2;

public Player(int x, int y, ImageManager im){
    this.x = x;
    this.y = y;
    this.im = im;
    locx = x;
    locy = y;
    playerR = new Rectangle(x,y,16,16);
}                                       

public void tick(){
    if (up) {
        if(canMove){
        y -= SPEED;
        locx = x;
        locy = y;
        playerR.setLocation(locx, locy);
        moving = true;
        }
        else{
            y += 1;
            canMove=true;
        }
    }
    if (dn) {

        y +=SPEED;
        locx = x;
        locy = y;
        moving = true;
        }
    }
    if (lt) {
        x -= SPEED;
        locx = x;
        locy = y;
        moving = true;

    }
    if (rt) {
        x+=SPEED;
        locx = x;
        locy = y;
        moving = true;
        }
    }
    if(moving){
        System.out.println("PLAYER\tX:"+locx+" Y:"+locy);
        moving = false;
    }
}

public void render(Graphics g){
    g.drawImage(im.player, x, y, Game.TILESIZE*Game.SCALE, Game.TILESIZE*Game.SCALE, null);
}   

}

我真的不知道如何进行碰撞,但是我用谷歌搜索了它,人们说要为玩家和玩家应该碰撞的所有对象制作一个矩形,并且每次玩家移动时,移动玩家的矩形.这是正确的方法吗?

编辑编辑编辑编辑

碰撞为真时的代码:

if (rt) {
        x+=SPEED;
        locx = x;
        locy = y;
        playerR.setLocation(locx, locy);
        for(int i = 0;i<Level.collisions.size();i++){
            if(intersects(playerR,Level.collisions.get(i))==true){
                x-=SPEED;
                locx = x;
                playerR.setLocation(locx, locy);
            }
        }
        moving = true;
    }

相交方法如下:

private boolean intersects(Rectangle r1, Rectangle r2){
    return r1.intersects(r2);
}

【问题讨论】:

  • 一般来说是的,最简单的 2D 碰撞检测是用矩形或圆形物体完成的。这些需要附加到屏幕上移动的对象上,以便计算碰撞检测。看看 JBox2D,它是一个开源物理引擎。 jbox2d.org
  • 有没有办法在不使用任何外部引擎或任何东西的情况下做到这一点?
  • 当然可以。看起来您已经开始使用 Player。确保每次玩家移动时更新矩形。您还需要将一个矩形添加到您可能想知道玩家是否与之发生碰撞的任何地方。最后,每一帧都需要遍历所有可碰撞的对象,并查看玩家的矩形是否与对象的矩形相交。这是一个非常粗略的算法,但它是一个很好的起点。
  • 如何在第一种方法中为每个“草地上的岩石”制作一个矩形?我想将矩形存储在 ArrayList 中,但我不确定我将如何进行编码?或者如果有更好的方法。另外,为了检查碰撞,我是否会在 Player 类的 tick 方法开始处使用 for 循环,如果发生碰撞,将 CanMove 设置为 false?最后一件事,为了检查碰撞,我使用 .intersects 对吗?
  • 保存所有可碰撞矩形的 ArrayList 是完全可以接受的。在创建对象时添加它们。至于检测碰撞,请像往常一样移动播放器。如果发生碰撞,请将玩家向后移动。您必须移动玩家以确定是否存在碰撞。如果您想变得花哨,只需将玩家向后移动足够多,这样就不会再发生碰撞。使用 Rectangle.intersects() 会起作用,或者如果您想自己动手学习它是如何工作的,请创建一个新方法 private boolean intersects(Rectangle r1, Rectangle r2)

标签: java collision-detection bufferedimage game-physics


【解决方案1】:

我将专注于您的 tick 方法,因为这是大部分逻辑的发展方向。这里有几个变化。最值得注意的是,我们只在检查碰撞之前移动矩形。然后循环遍历关卡中的所有可碰撞对象。一旦找到一个,我们重置我们的 x 和 y 并跳出循环(查看任何其他对象没有意义,因为我们已经找到了我们碰撞的那个)。然后我们更新我们的玩家位置。通过这样做,我集中了代码,因此不会重复。如果你看到自己重复代码,很有可能它可以被拉到一个普通的地方,或者一个方法。

public void tick() {

    if (up) {
        y -= SPEED;
    } else if (dn) {
        y += SPEED;
    } else if (lt) {
        x -= SPEED;
    } else if (rt) {
        x += SPEED;
    }

    playerR.setLocation(x, y);

    for (Rectangle collideable : Level.collisions) {
        if (intersects(playerR, collideable)) {
            x = locx;
            y = locy;
            playerR.setLocation(x, y);
            break;
        }
    }

    locx = x;
    locy = y;

}

【讨论】:

  • 我试过了,不幸的是它没有用。玩家正在穿过物体。还有其他想法吗?
  • Level 类循环的 x 和 y 值与画布的 x 和 y 的比例不同吗?我曾经在我的地图文件中读取的 for 循环仅转到 x 20 和 y 20,因为地图文件是 20 像素 x 20 像素。有没有办法获得整个瓷砖的坐标?因为我发现发生了碰撞,但仅在左上角的瓷砖中。这不是玩家应该与之碰撞的对象所在的位置。
  • 忽略我刚才所说的一切。我忘记将 x 和 y 乘以我拥有的比例,所以一切都合适。对不起,感谢您的帮助!
【解决方案2】:

有不同的方法可以做到这一点。当您谈论“rpg”时,我认为您的视图是等距的(自上而下 45°)。
我会以纯 90° 自上而下的方式进行碰撞检测,因为它更容易,恕我直言,更现实。
我们有两种可能:

  1. 将您的Player 移动到下一个位置。如果发生碰撞,请重置他的位置。
  2. 计算下一个位置,如果有碰撞不要移动。

如果您想要“滑动”碰撞响应,您必须检查碰撞将发生在哪个轴上,并仅停止/重置该轴的运动。

要进行更有效的碰撞检测,仅检查可能发生碰撞的附近物体。 通过将平方“dangerRadius”与玩家和对象之间的平方距离进行比较来做到这一点:

if ((player.x - object.x)² + (player.y - object.y)² <= dangerRadius²)
     // Check for intersection

这将通过使用以下简单计算来整理出大部分对象:

  • 2 减法
  • 1 次添加
  • 3 次乘法(²)
  • 1 个比较 (

在您的游戏中,您应该将逻辑和视图分开。因此,基本上您不会检测到两个图像是否重叠,但您会检查逻辑中的对象是否重叠。然后在正确的位置绘制图像。

希望这会有所帮助。

编辑: 重要提示:如果您更新角色,根据最后一帧和这一帧之间的时间 (1/FPS),您必须限制最大时间步长。为什么?因为如果出于某种原因(可能是慢速设备?)FPS 非常低,则角色可能会在 2 帧之间移动很远,因此他可能会在 1 帧中穿过一个对象。
此外,如果您只是在碰撞时重置运动,或者只是不移动您与对象之间的距离,则对于低 FPS 可能会很大。对于正常的 FPS 而不是高移动速度,这不会发生/明显。

【讨论】:

    【解决方案3】:

    我个人对 Java 还很陌生,尽管我过去曾使用过 C#。我正在制作一个类似的游戏,对于碰撞检测,我只检查玩家和对象的位置:

    if (z.gettileX() == p.gettileX()){
                    if (z.gettileY() == p.gettileY()){
        System.out.println("Collision!");
            }
        }
    

    如果玩家 (p) 与 z(坏人) 的 X 坐标和 Y 坐标相等,它将发送此消息并确认两者实际上已经发生碰撞。如果您可以使其在 z 后面的实际类中固有,以检查坐标是否相等,您可以创建无限数量的游戏内对象,以相同的方式检测碰撞并做出反应,即墙壁。

    【讨论】:

      【解决方案4】:

      这可能就是您要找的。我专门为多个对象的碰撞和单独的侧面碰撞制作了这个类。

      abstract class Entity {
      
          private Line2D topLine;
          private Line2D bottomLine;
          private Line2D leftLine;
          private Line2D rightLine;
          private Rectangle rectangle;
      
          private Entity entity;
      
          protected boolean top;
          protected boolean bottom;
          protected boolean left;
          protected boolean right;
      
          protected int x;
          protected int y;
      
          protected int width;
          protected int height;
      
          public Entity(int x, int y, int width, int height) {
              this.x = x;
              this.y = y;
              this.width = width;
              this.height = height;
              updateLinesAndRects();
          }
      
          public void updateLinesAndRects() {
              topLine = new Line(x + 1, y, width - 2, 0);
              bottomLine = new Line(x + 1, y + height, width - 2, height);
              leftLine = new Line(x, y + 1, 0, height - 2);
              rightLine = new Line(x + width, y + 1, 0, height - 2);
      
              rectangle = new Rectangle(x, y, width, height)
          }
      
          public void setCollision(Entity entity) {
              this.entity = entity;
              top = isColliding(new Line2D[]{topLine, bottomLine, leftLine, rightLine});
              bottom = isColliding(new Line2D[]{bottomLine, topLine, leftLine, rightLine});
              left = isColliding(new Line2D[]{leftLine, topLine, bottomLine, rightLine});
              right = isColliding(new Line2D[]{rightLine, topLine, bottomLine, leftLine});
          }
      
          public void updateBounds() {
              if(top) y = entity.y + entity.height;
              if(bottom) y = entity.y - height;
              if(left) x = entity.x + entity.width;
              if(right) x = entity.x - width;
          }
      
          public boolean isColliding() {
              return rectangle.intersects(entity.rect);
          }
      
          private boolean isLinesColliding(Line2D[] lines) {
              Rectangle rect = entity.getRectangle();
              return lines[0].intersects(rect) && !lines[1].intersects(rect) && !lines[2].intersects(rect) && !lines[3].intersects(rect);
          }
      
          private Line2D line(float x, float y, float width, float height) {
              return new Line2D(new Point2D.Float(x, y), new Point2D.Float(x + width, x + height));
          }
      
          public Rectangle getRectangle() {
              return rectangle;
          }
      }
      

      例子:

      class Player extends Entity{
      
          Entity[] entities;
      
          public Player(int x, int y, int width, int height) {
              super(x, y, width, height);
          }
      
          public void update() {
              updateLinesAndRects();
              for(Entity entity : entities) {
                  setCollision(entity);
                  if(top) system.out.println("player is colliding from the top!");
                  if(isColliding()) system.out.println("player is colliding!");
                  updateBounds(); // updates the collision bounds for the player from the entities when colliding.
              }
          }
      
          public void setEntities(Entity[] entities) {
              this.entities = entities;
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-14
        • 2013-03-14
        • 2015-10-05
        • 2013-03-06
        • 1970-01-01
        相关资源
        最近更新 更多