2D平台游戏中的碰撞Bug

时间:2012-05-28 12:36:29

标签: java 2d polygon collision

我目前正在开发2D Mario-Like Platformer游戏。我遇到了碰撞问题,我现在试图解决一段时间,但似乎没有任何作用:/

基本上,我有一个CenterLayer,它在哪个位置存储什么样的Tile。 然后我有一些Sprites和一个Player,它们应该碰撞这些Tiles。

因为这些瓷砖可以是三角形(或任何其他类型的凸多边形),我决定通过SAT(分离轴定理)处理碰撞。这很有效,但是当与地板碰撞时,许多瓷砖彼此相邻并且精灵向左移动,它会选择错误的边缘并将精灵移动到右边,但预期结果会将其向上移动。这会导致精灵卡住。

这是我目前使用的代码:

package level;

import java.awt.Polygon;

import tiles.Tile;
import sprites.*;

public class Collider { 
/** Collide Sprite (or Player) with CenterLayer **/ 
public static void collide(Sprite s, CenterLayer c){
    CollisionPolygon ps = s.getPolygon();

    //Get blocks to collide with
    int startXTile = (int) (s.getX() / CenterLayer.TILE_WIDTH) - 1;
    int endXTile = (int) Math.ceil((s.getX() + s.getWidth()) / CenterLayer.TILE_WIDTH) + 1;
    int startYTile = (int) (s.getY() / CenterLayer.TILE_HEIGHT) - 1;
    int endYTile = (int) Math.ceil((s.getY() + s.getHeight()) / CenterLayer.TILE_HEIGHT) +1;

    //limit to level boundaries
    if(startXTile < 0) startXTile = 0;
    if(endXTile > c.LEVEL_WIDTH) endXTile = c.LEVEL_WIDTH;
    if(startYTile < 0) startYTile = 0;
    if(endYTile > c.LEVEL_HEIGHT) endYTile = c.LEVEL_HEIGHT; 

    int sizeX = endXTile - startXTile;
    int sizeY = endYTile - startYTile;

    //loop through tiles and collide
    for(int xc = 0; xc < sizeX; xc++)
    for(int yc = 0; yc < sizeY; yc++){
        int xblock = xc + startXTile;
        int yblock = yc + startYTile;

        Tile t = c.getTile(xblock, yblock);
        if(t!=null){ //if tile == null --> tile is air
            CollisionPolygon pt = t.getPolygon(xblock, yblock);

            double[] projection = PolygonCollision(ps, pt);             

            //if collision has happened
            if(projection[0] != 0 || projection[1] != 0){
                //collide
                s.moveBy(projection[0], projection[1]);

                //update sprites polygon to new position                    
                ps = s.getPolygon();
            }
        }
    }
}

public static double dotProduct(double x, double y, double dx, double dy) {
    return x * dx + y * dy;
}

// Calculate the projection of a polygon on an axis (ax, ay)
// and returns it as a [min, max] interval
public static double[] ProjectPolygon(double ax, double ay, Polygon p) {
    double dotProduct = dotProduct(ax, ay, p.xpoints[0], p.ypoints[0]);
    double min = dotProduct;
    double max = dotProduct;
    for (int i = 0; i < p.npoints; i++) {
            dotProduct = dotProduct(p.xpoints[i], p.ypoints[i], ax, ay);
            if (dotProduct < min) {
                    min = dotProduct;
            } else if (dotProduct > max) {
                    max = dotProduct;
            }
    }
    return new double[] { min, max };
}

// Calculate the distance between [minA, maxA](p1[0], p1[1]) and [minB, maxB](p2[0], p2[1])
// The distance will be negative if the intervals overlap
public static double IntervalDistance(double[] p1, double[] p2) {
    if (p1[0] < p2[0]) {
        return p2[0] - p1[1];
    } else {
        return p1[0] - p2[1];
    }
}

public static double[] PolygonCollision(CollisionPolygon p1, CollisionPolygon p2){
    boolean intersection = true;

    int edgeCount1 = p1.npoints;
    int edgeCount2 = p2.npoints;

    double projectionX = 0;
    double projectionY = 0;
    double projectionDist = Double.POSITIVE_INFINITY;

    //loop through all the edges
    for(int edgeIndex = 0; edgeIndex < edgeCount1 + edgeCount2; edgeIndex++){
        //find edges

        double[] axis;

        if(edgeIndex < edgeCount1){
            axis = p1.getAxis(edgeIndex);
        } else {
            axis = p2.getAxis(edgeIndex - edgeCount1);
        }

        double axisX = axis[0];
        double axisY = axis[1];

        //System.out.println("edge: " +axisX + ", "+ axisY);

        //find the projection of both polygons on current axis
        final double[] proj1 = ProjectPolygon(axisX, axisY, p1);
        final double[] proj2 = ProjectPolygon(axisX, axisY, p2);

        //Check if polygons are intersecting, if not end loop
        double id = IntervalDistance(proj1, proj2); 
        if(id > 0){
            intersection = false;
            break;
        }

        //Check if projection would be shorter than previous one
        id = Math.abs(id);
        if(id < projectionDist){
            projectionDist = id;
            projectionX = axisX;
            projectionY = axisY;


            //check if hit from "false" side
            double d1x = p1.getCenterX();
            double d1y = p1.getCenterY();
            double d2x = p2.getCenterX();
            double d2y = p2.getCenterY();

            double midx = d1x - d2x;
            double midy = d1y - d2y;

            double dot = dotProduct(midx, midy, projectionX, projectionY);

            if(dot < 0){
                projectionX = -projectionX;
                projectionY = -projectionY;
            }
        }

    }

    double[] result = new double[]{0, 0};

    if(intersection){
        //System.out.println("colliison: " + projectionX +"; "+ projectionY + ", " + projectionDist);
        result[0] = projectionX * projectionDist;
        result[1] = projectionY * projectionDist;   
    }

    return result;
}
}

任何想法?

汤姆

1 个答案:

答案 0 :(得分:1)

我也有这个错误,当聚合物上有平行边缘时会发生这种情况。修复此问题的简单方法是在找到的轴上投影多边形中心之间的差异。如果结果为负,则只需乘以轴乘-1。

  Vector aMidPoint = new Vector();
  Vector bMidPoint = new Vector();
  for ( Vector v : aVerts) {
     aMidPoint = aMidPoint.add(v);
  }
  for ( Vector v : bVerts) {
     bMidPoint = bMidPoint.add(v);
  }
  aMidPoint = aMidPoint.scalarDivision(aVerts.size());
  bMidPoint = bMidPoint.scalarDivision(bVerts.size());

  Vector ba = aMidPoint.subtract(bMidPoint);

  if (ba.dotProduct(minOverlapVector) < 0) {
     minOverlapVector = minOverlapVector.scalarMultiplication(-1);
  }