简体   繁体   English

2D Platformer游戏中的碰撞错误

[英]Collision Bug in 2D Platformer Game

I am currently developing a 2D Mario-Like Platformer Game. 我目前正在开发2D Mario-Like Platformer游戏。 I ran into a collision problem i've been trying to solve for a while now, but nothing seems to work :/ 我遇到了我一直试图解决一段时间的碰撞问题,但似乎没有任何效果:/

Basicly, i have a CenterLayer, which stores at which Position what kind of Tile is. 基本上,我有一个CenterLayer,它存储哪种Tile是什么位置。 Then i have some Sprites and a Player, which should collide with these Tiles. 然后我有一些精灵和一个播放器,它们应该与这些图块碰撞。

Because these Tiles can be triangular shaped (or any other kind of convex polygon), i decided to handle collision via SAT (Seperating Axis Theorem). 因为这些图块可以是三角形的(或任何其他种类的凸多边形),所以我决定通过SAT(分离轴定理)来处理碰撞。 This works great, but when it comes to collision with the floor where many tiles are adjacent to eachother and the sprite is moving left, it pickes the wrong edge and moves the Sprite to the right, but expected result would be moving it up. 这种方法效果很好,但是当与许多瓷砖彼此相邻且精灵向左移动的地板碰撞时,它将拾取错误的边缘并将精灵向右移动,但是预期的结果是将其向上移动。 This causes the sprite to get stuck. 这会导致精灵卡住。

This is the code im currently using: 这是我当前正在使用的代码:

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

Any Ideas? 有任何想法吗?

Tom 汤姆

I had this bug too , it happens when there are parallel edges on a poly.The easy way to fix this is to project the difference between the polygon centers on the found axis.If the result is negative you would just multiply the axis by -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);
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM