【问题标题】:How can I fix this collision detection block?如何修复此碰撞检测块?
【发布时间】:2013-05-07 02:10:04
【问题描述】:

我一直在使用 LibGDX 框架在 Java 中开发这款破砖游戏。这是我的第一个java游戏。我对其他语言有一点经验,但遇到了一些麻烦。我已经设置了碰撞检测,它在大多数情况下都有效。球有时会以错误的方向弹跳,有时它会撞到不应该撞到的挡块。我一直在搜索,但很难将其转化为我的游戏。

我的代码真的很烂,因为球只能以 45 度角移动。不太现实,这将是我修复此问题后的下一步。

public void checkBrickCollision()
{
    for (int i=0;i<level.brickCount;i++) {

            if (level.bricks[i].GetVisible() == true) {
                if (level.bricks[i].getRect().overlaps(ball.getRect()))
                {



        int xd = (int) Math.abs( (ball.ballRect.x + ball.ballRect.width - level.bricks[i].brickRect.x - level.bricks[i].getRect().width) /2 );
        int yd = (int) Math.abs( (ball.ballRect.y + ball.ballRect.height - level.bricks[i].brickRect.y - level.bricks[i].getRect().height) /2 );

        if (xd > yd)
        {
            // Collision on top or bottom, reverse y velocity
            ball.ballSpeedY = -ball.ballSpeedY;
            Score score = new Score(level.bricks[i].getScore(),(int)level.bricks[i].brickRect.x,(int)level.bricks[i].brickRect.y);
            scoreList.add(score);
            level.bricks[i].Destroy();

            System.out.println("Top/Bottom");
            return;
        }


        if (yd > xd)
        {
            // Collision on left or right, reverse x velocity
            ball.ballSpeedX = -ball.ballSpeedX;
            Score score = new Score(level.bricks[i].getScore(),(int)level.bricks[i].brickRect.x,(int)level.bricks[i].brickRect.y);
            scoreList.add(score);
            level.bricks[i].Destroy();

            System.out.println("Sides");
            return;
        }

        if (xd == yd)
        {
            // Collision on corners, reverse both
            ball.ballSpeedX = -ball.ballSpeedX;
            ball.ballSpeedY = -ball.ballSpeedY;
            Score score = new Score(level.bricks[i].getScore(),(int)level.bricks[i].brickRect.x,(int)level.bricks[i].brickRect.y);
            scoreList.add(score);
            level.bricks[i].Destroy();

            System.out.println("Corners");
            return;
        }           



                }       
}
}
}

【问题讨论】:

  • 我会一一检查球的角是否与砖块相撞。然后基于此,我会检查球应该去哪里。在让球跳下之前,我还会检查所有相交的砖块的碰撞情况。这将防止球在击中超过 1 个砖块时向奇怪的方向移动。这种技术的问题在于,如果球快,它就行不通(但你现在拥有的也行不通)。
  • 这就是我遇到的问题。弄清楚如何检查。我尝试了很多不同的方法,但都不能正常工作。我明白它的原理。它只是把它变成代码。
  • 我不确定矩形在 Java 中是如何工作的,但原则上,要检查一个点是否在矩形内,你应该这样做:` boolean colliding =((point.x>rect.x&&point. xrect.y&&point.y

标签: java libgdx collision-detection


【解决方案1】:

我建议您将 level.bricks[] 数组放在 ArrayList 列表中,这样您就可以便宜地从列表中删除()和/或销毁()它们飞,避免在 level.bricks 数组中的每次迭代检查空值(除非您的设置不介意数组中的空值)。另外,它将使您不必在每个渲染周期中检查数组的大小、每次迭代、所有砖块的大小......

List<Brick> brickList = new ArrayList<Brick>();
for (brick: level.bricks) {
    brickList.add( new Brick(brick) );
}
//-------or-------
//if the brick object is a Sprite
for (brick: level.bricks) {
    brickList.add(brick);
}

我认为错误的砖块被检测为“击中”的问题与试图补偿球的纹理和边界为矩形有关。我建议使用 com.badlogic.gdx.math.Circle 类来定义球的边界。以下是 com.badlogic.gdx.math.Intersector 的脏自定义碰撞包装器,它应该适用于您在上面尝试的内容。它假设球的视觉像素延伸到纹理的边缘,并且球的纹理是正方形的:

public class Collider {
    private String bounceType = "";
    private boolean vertical = false;
    private boolean horizontal = false;

    // Segment Vertices of the Rectangle
    private Vector2 leftStart = new Vector2();
    private Vector2 leftEnd = new Vector2();
    private Vector2 topStart = new Vector2();
    private Vector2 topEnd = new Vector2();
    private Vector2 rightStart = new Vector2();
    private Vector2 rightEnd = new Vector2();
    private Vector2 bottomStart = new Vector2();
    private Vector2 bottomEnd = new Vector2();

    private Vector2 center = new Vector2();
    private Circle ballBounds = new Circle();

    // Pointers passed once during construction
    private Ball ball;
    private List<Brick> brickList;
    private List<Score> scoreList;

    /**
     * Constructor requires that ball and brickList pointers are given.
     * <p>
     * Runs the updateBallBounds() method after assigning the parameters
     *
     *@param ball points to the ball to be used for the collision calculations
     *@param brickList points to a list of brick objects to check against
     *@param scoreList points to a list of score objects to track the score
     */
    public Collider(Ball ball, List<Brick> brickList, List<Score> scoreList) {
        this.ball = ball;
        this.brickList = brickList;
        updateBallBounds(this.ball, this.ballBounds);
    }

    /**
     * Sets the position and radius of the bounding circle
     * for the given ball with a rectangular shape in order
     * to prepare it for bounds checking.
     *
     * @param ball The ball object to 
     * @param bounds The circle object that will store the bounds information
     */
    private void updateBallBounds(Ball ball, Circle bounds) {    
        bounds.set( (ball.ballRect.x + (ball.ballRect.width/2)),  //Center x pos
                    (ball.ballRect.y + (ball.ballRect.height/2)), //Center y pos
                    (ball.ballRect.width / 2) ); //Radius of ball
    }

    /**
     * Builds the start and end Vectors for each of the segments
     * of the provided rectangle. Also builds the center Vector for
     * the ball.
     * <p>
     * Used to prepare for finding which segments of the rectangle the 
     * ball is intersecting 
     *  
     * @param brickRect The rectangle to process the line segments from
     */
    private setVectors(Rectangle brickRect) {
        leftStart.set(brickRect.x, brickRect.y);
        leftEnd.set(brickRect.x, brickRect.height);
        topStart.set(brickRect.x, brickRect.height);
        topEnd.set(brickRect.width, brickRect.height);
        rightStart.set(Poyline( brickRect.width, brickRect.y);
        rightEnd .set(brickRect.width, brickRect.height);
        bottomStart.set(brickRect.x, brickRect.y);
        bottomEnd.set(brickRect.width, brickRect.y);

        center.set(ballBounds.x, ballBounds.y);
    }

    /**
     * Finds bricks in the list that the ball is currently
     * colliding with.
     * <p>
     * For every rectangle that the ball is currently colliding with,
     * the method calls the setVectors() method to prepare the start-end
     * vertices for the processCollision() method.
     * <p>
     * WARNING: this may not handle multiple collision very well. It
     * should work, but you most likely will not give very good results 
     * on multiple brick hit detected. You should think about including 
     * a break statement after the first collision is detected and let 
     * the Collider find an additional collision during the next render()
     * call.
     */  
    public void detectCollisions() {
        updateBallBounds(ball, ballBounds);
        for (brick: brickList) {
            if( Intersector.overlaps(ballBounds, brick.brickRect) ) {
                setVectors(brick.brickRect);
                processCollision(brick, brick.brickRect);
                //break;
            }
        }
    }

    /**
     * Detects how to handle the collision based on the segments being hit.
     *
     *@param brick the brick found by detectCollision() method. will be
     *             destroyed after collision is processed
     */  
    public void processCollision(Brick brick) {
        if ( Intersector.intersectSegmentCircle( topStart, topEnd,
                                                 center * center,
                                                 ballBounds.radius ) ||
             Intersector.intersectSegmentCircle( bottomStart, bottomEnd,
                                                 center * center,
                                                 ballBounds.radius )) {
            vertical = true;
        }

        if ( Intersector.intersectSegmentCircle( leftStart, leftEnd,
                                                 center * center,
                                                 ballBounds.radius ) ||
             Intersector.intersectSegmentCircle( rightStart, rightEnd,
                                                 center * center,
                                                 ballBounds.radius ) ) {
            horizontal = true;
        }

        // The following could return the value to a calling entity.
        // Then the game logic of what to do here would be decoupled.
        if (vertical && horizontal) {
            bounceType = "CORNER"
        } else if (vertical && !horizontal) {
            bounceType = "VERTICAL"
        } else if () {
            bounceType = "HORIZONTAL"
        } else {
            // The game blows up...
        }

        switch (bounceType) {
            case "VERTICAL":
                ball.ballSpeedY = -ball.ballSpeedY;
                break;
            case "HORIZONTAL":
                ball.ballSpeedX = -ball.ballSpeedX;
                break;
            case "CORNER":
                ball.ballSpeedY = -ball.ballSpeedY;
                ball.ballSpeedX = -ball.ballSpeedX;
                break;
            default: // Try not to blow up the game...
                break;
        }
        Score score = new Score(brick.getScore(), brick.x, brick.y)
        scoreList.add(score);
        brickList.remove(brick);
        brick.destroy();
    }
}

我确定这里有错误,它没有经过测试。除了前面的假设之外,它不会阻止砖块在您的数组中呈现(除非 destroy() 处理了这一点,并且希望不会在呈现时寻找空值......)。

基本结构可以做得更好...

  • 除了,尝试,抓住一些东西。
  • 您可以添加更多类型化构造函数和方法来覆盖更多形状。
  • 空间分区可以添加一个映射静态边界的方法 建造。然后你的动态对象可以记录他们的分区 当前占用且只查询对方的碰撞结果 记录在这些分区列表中的对象。
  • 我认为这个类的实现应该是解耦处理的 其他地方。
  • 这还可以让您创建一个碰撞类或事件来返回/触发。

希望这会有所帮助。


我还建议您研究 Box2D。您的要求似乎很简单,可以轻松学习如何使用它。 This page 可以向您展示它是如何与 LibGDX 一起配置和使用的。这将允许您快速实现物体的材料属性、球上的旋转、速度变化、角反弹......,以及自动魔法的各种好处,这将为您的大脑节省一些周期。物理引擎为您完成所有数学运算。您只需初始化并执行它,监听事件(如碰撞)。

祝你的游戏好运。

【讨论】:

    猜你喜欢
    • 2012-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多