查找碰撞检测中的抖动原因

时间:2014-04-10 21:22:07

标签: java game-engine collision rounding detection

我在寻找使用碰撞检测时寻找抖动源的帮助。 我已经实现了一个java游戏(使用eclipse和slick2d)并且已经松散地遵循了本指南: http://katyscode.wordpress.com/2013/01/18/2d-platform-games-collision-detection-for-dummies

但当然在必要时改变位以适应我的游戏和java而不是cpp。

从我所做的研究中,我认为我的抖动的根本原因来自四舍五入的错误。 尽管这是我的主要嫌疑人,但我仍然无法确定它的发生地点。

很抱歉,如果缩进不是很正确,那么在使用代码块识别时会遇到一些麻烦。

基本上我在课堂上创建变量。 在init()中,我设置了大部分资源。 在render()中,所有绘图都会发生。注意图形转换,以便摄像机跟随播放器。 在更新中,我当然根据用户输入,重力和摩擦来更新播放器的位置。

我也用这种方法调用碰撞检测。 碰撞检测正在研究渗透分辨率方法。

(是的,我知道我会详尽地与每个世界对象进行比较。当我将更多基本问题排除在外时,我会提高AABB的效率。就像抖动一样!)

我的方法首先计算玩家期望在每个轴上移动多少,然后对于每个世界对象,它检查与玩家边界点的交叉点(浮点值表示玩家周围的坐标)。它在每个方向上检查这个并使用结果来确定碰撞发生在哪个轴上(如果有的话),以便采取适当的措施。

很抱歉这是一堆代码,但毕竟是碰撞检测,这不是一件小事。

这是我的Play课程,其中所有更新都继续进行游戏:

package GameFiles;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.newdawn.slick.*;
import org.newdawn.slick.state.*;
import java.awt.Rectangle;
//import java.awt.geom.Rectangle2D;

public class Play extends BasicGameState{

    static int tileSize = 32;
    File levelMap;
    SpriteSheet spriteSheet;
    int[][] currentMap;
    Image[] groundTiles = new Image[4];
    //List<Rectangle2D> levelBounds = new ArrayList<Rectangle2D>();
    List<Rectangle> levelBounds = new ArrayList<Rectangle>();
    Player player;
    float playerX, playerY;
    int dir;
    float acc, mov, friction, gravity;
    float runSpeed;
    float maxAcc;
    boolean inAir, jumping, keyDown;
    boolean exitFlag;
    int mapHeight, mapWidth;
    float mapX, mapY;
    float speedX, speedY;
    int worldObjectCount;
    int iterations;

public Play(int state){

}

public void init(GameContainer gc, StateBasedGame sbg) throws SlickException{
    playerX = Game.scrWidth/2;
    playerY = Game.scrHeight - tileSize - tileSize;
    player = new Player(playerX, playerY);
    levelMap = new File("maps/lvl1.txt");
    spriteSheet = new SpriteSheet("res/tiles/tilesets/DungeonCrawlTilesetBW.png", tileSize, tileSize);

    try 
    {
        currentMap = readMap(levelMap);
    } 
    catch (IOException e) 
    {
        System.out.println("Array size mismatch when copying arrays.");
        e.printStackTrace();
    }

    levelBounds.clear();
    for(int x = 0; x < mapWidth; x++)
    {
        for(int y = 0; y < mapHeight; y++){
            if(currentMap[x][y] == 1){
                levelBounds.add(new Rectangle(x*tileSize, Game.scrHeight - mapHeight*tileSize + y*tileSize, tileSize, tileSize));
                //levelBounds.add(new Rectangle2D.Float(x*tileSize, Game.scrHeight - mapHeight*tileSize + y*tileSize, tileSize, tileSize));
                System.out.println("Added new bounding box: " + (x*tileSize) + ", " + (Game.scrHeight - mapHeight*tileSize + y*tileSize) + ", " + tileSize);
            }
        }
    }
    worldObjectCount = levelBounds.size();
    System.out.println("World object count: " + worldObjectCount);

    groundTiles[0] = spriteSheet.getSubImage(4, 16);
    groundTiles[1] = spriteSheet.getSubImage(13, 19);

    dir = 1;
    acc = 0.0f;
    mov = 0.0f;
    friction = 4f;
    gravity = 4f;
    runSpeed = 0.6f;
    maxAcc = -1f;
    inAir = false;
    jumping = false;
    keyDown = false;
    exitFlag = false;
    speedX = 0.0f;
    speedY = 0.0f;
    iterations = 3;
}

public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException{

    //determine cameraX and cameraY
    float cameraX, cameraY;
    cameraX = player.getX() - Game.scrWidth/2;
    cameraY = player.getY() - (Game.scrHeight/2 - tileSize - tileSize);
    g.translate(-cameraX, -cameraY);
    player.render(g);

    for(int x = 0; x < mapWidth; x++)
    {
        for(int y = 0; y < mapHeight; y++){
            if(currentMap[x][y] == 1){
                groundTiles[0].draw(x*tileSize, Game.scrHeight - mapHeight*tileSize + y*tileSize);
            }
        }
    }
    g.translate(cameraX, cameraY);
}

public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException{
    Input input = gc.getInput();
    float secondsElapsed = delta/1000.0f;
    checkCollisions(secondsElapsed);


    player.setX((player.getX() + speedX));
    player.setY((player.getY() - speedY));

    //check inputs
    checkKeyEvents(input);

    //slow down / friction
    if(!keyDown){
        if(mov < 0)
            mov += friction * secondsElapsed;
        else
            mov -= friction * secondsElapsed;
    }
    speedX = mov;
    if (speedX > 0 && speedX < friction * secondsElapsed) speedX = 0;
    if (speedX < 0 && speedX > -friction * secondsElapsed) speedX = 0;


    //jump or fall
    acc -= gravity * secondsElapsed;
    if (acc < maxAcc){
        acc = maxAcc;
    }
    speedY = acc;

    //exit when exitFlag true
    if(exitFlag){
        gc.exit();
    }
}

public void checkCollisions(float secondsElapsed){
    boolean contactX = true, contactYbottom = true, contactYtop = true;
    // Keep iterating the contact solver until the maximum number of iterations is reached
    // or no collisions are detected
    for (int iteration = 0; iteration < iterations && (contactX || contactYbottom || contactYtop); iteration++)
    {
        float nextMoveX = speedX * secondsElapsed;
        float nextMoveY = speedY * secondsElapsed;
        contactX = contactYbottom = contactYtop = false;
        float projectedMoveX, projectedMoveY, originalMoveX, originalMoveY;
        originalMoveX = nextMoveX;
        originalMoveY = nextMoveY;
        for (int o = 0; o < worldObjectCount && !contactX && !contactYbottom && !contactYtop; o++)
        {
            for (int dir = 0; dir < 6; dir++)
            {
                //top, bottom, left, left, right, right.
                if (dir == 0 && nextMoveY < 0) continue;
                if (dir == 1 && nextMoveY > 0) continue;
                if (dir == 2 && nextMoveX > 0) continue;
                if (dir == 3 && nextMoveX > 0) continue;
                if (dir == 4 && nextMoveX < 0) continue;
                if (dir == 5 && nextMoveX < 0) continue;
                projectedMoveX = (dir >= 2? nextMoveX : 0);
                projectedMoveY = (dir <  2? nextMoveY : 0);

                float[][] collisionPoint = player.getBounds();

                Rectangle curRect = new Rectangle(levelBounds.get(o).x, levelBounds.get(o).y, levelBounds.get(o).width, levelBounds.get(o).height);
                //Rectangle2D curRect = levelBounds.get(o).getBounds2D();

                while (curRect.contains(collisionPoint[dir][0] + projectedMoveX, collisionPoint[dir][1] + projectedMoveY)
                        || curRect.intersects(collisionPoint[dir][0] + projectedMoveX, collisionPoint[dir][1] + projectedMoveY, 1, 1))
                {
                    if (dir == 0) projectedMoveY += 0.05f; //top collision
                    if (dir == 1) projectedMoveY -= 0.05f; //bottom collision

                    if (dir == 2) projectedMoveX += 0.05f; //left collision
                    if (dir == 3) projectedMoveX += 0.05f;

                    if (dir == 4) projectedMoveX -= 0.05f; //right collision
                    if (dir == 5) projectedMoveX -= 0.05f;
                }

                if (dir >= 2 && dir <= 5) 
                    nextMoveX = projectedMoveX;
                if (dir >= 0 && dir <= 1) 
                    nextMoveY = projectedMoveY;
            }

            if (nextMoveY > originalMoveY && originalMoveY != 0)
            {
                contactYtop = true;
            }

            if (nextMoveY < originalMoveY && originalMoveY != 0)
            {
                contactYbottom = true;
            }

            if (Math.abs(nextMoveX - originalMoveX) > 0.01f)
            {
                contactX = true;
            }
            if (contactX && contactYtop && speedY > 0)
                speedY = nextMoveY = 0;
        }
        if (contactYbottom || contactYtop)
        {
            player.setY(player.getY() + nextMoveY);
            speedY = 0;
            acc = 0;
            if (contactYbottom)
                jumping = false;
        }

        if (contactX)
        {
            player.setX(player.getX() + nextMoveX);
            speedX = 0;
            mov = 0;
        }
    }//end collisions
}

public int[][] readMap(File level) throws IOException, SlickException{

    BufferedReader br = new BufferedReader(new FileReader(level));

    mapWidth = Integer.parseInt(br.readLine());
    mapHeight = Integer.parseInt(br.readLine());
    int[][] map = new int[mapWidth][mapHeight];

    for(int row = 0; row < mapHeight; row++)
    {
        String line = br.readLine();
        if(line == null || line.isEmpty())
        {
            System.out.println("Line is empty or null");
        }
        else
        {
            String[] tileValues = line.split(",");
            for(int col = 0; col < mapWidth; col++)
            {
               map[col][row] = Integer.parseInt(tileValues[col]);  

            }
        }
    }
    br.close();
    return map;
}

public void checkKeyEvents(Input input){
    //key input events
    if(input.isKeyPressed(Input.KEY_DOWN)){

    }
    if(input.isKeyPressed(Input.KEY_UP)){
        if(!jumping){
            acc = 1f;
        }
        jumping = true;
    }
    if(input.isKeyDown(Input.KEY_LEFT) && !input.isKeyDown(Input.KEY_RIGHT)){
        keyDown = false;
        mov -= 0.006f;
        if (mov < -runSpeed){
            mov = -runSpeed;
        }

    }
    if(input.isKeyDown(Input.KEY_RIGHT) && !input.isKeyDown(Input.KEY_LEFT)){
        keyDown = false;
        mov += 0.006f;
        if (mov > runSpeed){
            mov = runSpeed;
        }
    }
    if(input.isKeyPressed(Input.KEY_ESCAPE)){
        exitFlag = true;
    }
}

public int getID(){
    return 1;
}

}

由于我无法预测潜在帮助者可能需要的更多信息,我现在就将其留在那里,但当然我可以在需要时提供更多信息。

谢谢, 学家

0 个答案:

没有答案