基于平铺的平台中的碰撞检测

时间:2014-05-05 00:09:04

标签: java collision-detection

我的平台游戏中有一套方法正在检测和解决实体与tilemap之间的冲突,但是它们的工作做得不好。

底部方法是被调用的方法,如下所示:

player.velocity = player.velocity.add(getFinalCollisionVector());

其中player.velocity是Vec2D。

private List<Rectangle2D> getCollidingTiles(){
    List<Rectangle2D> collidingTiles = new ArrayList<Rectangle2D>();
    for(int x = (int) (this.getX()/Tile.SIZE); x <= (int) (this.getX()/Tile.SIZE) + this.getRect2D().getWidth()/Tile.SIZE; x++){
        for(int y = (int) (this.getX()/Tile.SIZE); y <= (int) (this.getX()/Tile.SIZE) +  this.getRect2D().getHeight()/Tile.SIZE; y++){

            if(map.getTileAt(x, y).getAttribute(Attribute.SOLID))
                if(map.getCollisionBoxAt(x,y).isColliding(this.collisionBox))
                    collidingTiles.add(new Rectangle2D.Double(x, y, Tile.SIZE, Tile.SIZE));
        }
    }       

    return collidingTiles;
}
private List<Vec2D> getAllTileCollisionVectors(){
    List<Rectangle2D> collidingTiles = getCollidingTiles();
    List<Vec2D> collisionVectors = new ArrayList<Vec2D>();
    for(Rectangle2D rec : collidingTiles){
        collisionVectors.add(getCorrectionVector(rec));
    }
    return collisionVectors;
}
private Vec2D getCorrectionVector(Rectangle2D target)
{
    Vec2D ret = new Vec2D();

    double x1 = (this.getX() + this.getSize().x) - target.getX();
    double x2 = this.getX() - (target.getX() + target.getWidth());
    double y1 = (this.getY() + this.getSize().y) - target.getY();
    double y2 = this.getY() - (target.getY() + target.getHeight());
    // calculate displacement along X-axis
    if (x1 < x2)
    {
        ret.x = x1;
    }
    else if (x1 > x2)
    {
        ret.x = x2;
    }
    // calculate displacement along Y-axis
    if (y1 < y2)
    {
        ret.y = y1;
    }
    else if (y1 > y2)
    {
        ret.y = y2;
    }
    return ret;
}
protected Vec2D getFinalCollisionVector(){
    List<Vec2D> collisionVectors = getAllTileCollisionVectors();
    if(collisionVectors.size() < 1)
        return new Vec2D(0,0);

    Vec2D finalVector = new Vec2D();
    for(Vec2D vec : collisionVectors){
        finalVector = finalVector.add(vec);
    }       
    return finalVector;
}

我的代码中出错了什么?这是玩家显示的行为,在那里(由于重力)落到那一点,然后他冻结。 The character is stuck

1 个答案:

答案 0 :(得分:0)

OP在这里: 由于没有其他答案,我已经发布了自己的答案。

我已经放弃了旧的实施,从头开始,它现在有效。这是新的实现:

    public Corners getCornersAreSolid(double x, double y) {
    int leftTile = (int)(x / Tile.SIZE);
    int rightTile = (int)((x + moveData.collisionBox.getWidth()) / Tile.SIZE);
    int topTile = (int)(y / Tile.SIZE);
    int bottomTile = (int)((y + moveData.collisionBox.getHeight()) / Tile.SIZE);

    boolean topLeft = hasAttribute(map, Attribute.SOLID, topTile, leftTile);
    boolean topRight = hasAttribute(map, Attribute.SOLID, topTile, rightTile);
    boolean bottomLeft = hasAttribute(map, Attribute.SOLID, bottomTile, leftTile);
    boolean bottomRight = hasAttribute(map, Attribute.SOLID, bottomTile, rightTile);

    Corners solidCorners = new Corners();
    solidCorners.topLeft = topLeft;
    solidCorners.topRight = topRight;
    solidCorners.bottomRight = bottomRight;
    solidCorners.bottomLeft = bottomLeft;
    return solidCorners;
}
private boolean hasAttribute(GameMap map, Attribute attribute, int tileY, int tileX) {
      boolean result = false;

      if (tileX >= 0 && tileX < map.getWidthInTiles() && tileY >= 0 && tileY < map.getHeightInTiles()) {
        result = map.getTileAt(tileX, tileY).getAttribute(attribute);
      }
      return result;
    }
public Vec2D getNextPosition() {

    int currCol = (int) (getX() / Tile.SIZE);
    int currRow = (int) (getY() / Tile.SIZE);

    double destX = getX() + moveData.velocity.x;
    double destY = getY() + moveData.velocity.y;

    double tempX = getX();
    double tempY = getY();

    Corners solidCorners = getCornersAreSolid(getX(), destY);
    boolean topLeft = solidCorners.topLeft;
    boolean topRight = solidCorners.topRight;
    boolean bottomLeft = solidCorners.bottomLeft;
    boolean bottomRight = solidCorners.bottomRight;

    this.framesSinceLastCollision += 1;
    if(moveData.velocity.y < 0) {
        if(topLeft || topRight) {
            moveData.velocity.y = 0;
            tempY = currRow * Tile.SIZE;
            this.framesSinceLastCollision = 0;
        }
        else {
            tempY += moveData.velocity.y;
        }
    }
    else if(moveData.velocity.y > 0) {
        if(bottomLeft || bottomRight) {
            moveData.velocity.y = 0;
            tempY = (currRow + 1) * Tile.SIZE - moveData.collisionBox.getHeight() % Tile.SIZE - 1 ;
            this.framesSinceLastCollision = 0;
        }
        else {
            tempY += moveData.velocity.y;
        }
    }

    solidCorners = getCornersAreSolid(destX, getY());
    topLeft = solidCorners.topLeft;
    topRight = solidCorners.topRight;
    bottomLeft = solidCorners.bottomLeft;
    bottomRight = solidCorners.bottomRight;
    if(moveData.velocity.x < 0) {
        if(topLeft || bottomLeft) {
            moveData.velocity.x = 0;
            tempX = currCol * Tile.SIZE;
            this.framesSinceLastCollision = 0;
        }
        else {
            tempX += moveData.velocity.x;
        }
    }
    if(moveData.velocity.x > 0) {
        if(topRight || bottomRight) {
            moveData.velocity.x = 0;
            tempX = (currCol + 1) * Tile.SIZE - moveData.collisionBox.getWidth() % Tile.SIZE -1 ;
            this.framesSinceLastCollision = 0;
        }
        else {
            tempX += moveData.velocity.x;
        }
    }
    return new Vec2D(tempX, tempY);
}
private static class Corners{
    public boolean topLeft, topRight;
    public boolean bottomLeft, bottomRight;
    public Corners(){
        topLeft = false;
        topRight = false;
        bottomLeft = false;
        bottomRight = false;
    }       
}