C#XNA - 突破式球类物理

时间:2013-11-19 22:26:38

标签: c# xna physics bounce breakout

我正在为大学任务创建一个项目,我遇到了一些障碍。这个问题很小,最有可能被忽视,但它仍然困扰着我,并且在完成工作方面是一个程序性的人,在解决之前我不能继续做任何事情。

我的球拍和球码工作正常,以及根据球击中球拍的位置的简单角速度,但我的问题是当球撞到砖块时。代码主要起作用并且看起来完美无缺,但是当球完成以下两件事之一时,这会发生变化:

  • 在其中一个角上击打砖块可能会错误地反转球速度的错误轴
  • 同时击中两块砖(在角落里)会使球穿过它们并将它们移走;即使他们身体健康。

我正在使用的代码(Ball.cs):

编辑(更新代码):

public bool Touching(Brick brick)
{
    var position = Position + Velocity;
    return position.Y + Size.Y >= brick.Position.Y &&
              position.Y <= brick.Position.Y + brick.Size.Y &&
              position.X + Size.X >= brick.Position.X &&
              position.X <= brick.Position.X + brick.Size.X;
}

public bool Collide(Brick brick)
{
    //Secondary precaution check
    if (!Touching(brick)) return false;

    //Find out where the ball will be
    var position = Position + Velocity;

    //Get the bounds of the ball based on where it will be
    var bounds = new Rectangle((int)position.X, (int)position.Y, Texture.Width, Texture.Height);

    //Stop the ball from moving here, so that changes to velocity will occur afterwards.
    Position = Position - Velocity;

    //If the ball hits the top or bottom of the brick
    if (bounds.Intersects(brick.Top) || bounds.Intersects(brick.Bottom))
    {
        Velocity = new Vector2(Velocity.X, -Velocity.Y); //Reverse the Y axe of the Velocity
    }

    //If the ball hits the left or right of the brick
    if (bounds.Intersects(brick.Left) || bounds.Intersects(brick.Right))
    {
        Velocity = new Vector2(-Velocity.X, Velocity.Y); //Reverse the X axe of the Velocity
    }

    return true;
}

这些方法是从Level.cs的Update方法调用的:

public override void Update(GameTime gameTime)
{
    player.Update(gameTime);
    balls.ForEach(ball => ball.Update(gameTime));
    foreach (var brick in bricks)
    {
        foreach (var ball in balls)
        {
            if (ball.Touching(brick))
            {
                if (!collidingBricks.Contains(brick)) collidingBricks.Add(brick);
            }
        }
        brick.Update(gameTime);
    }

    if (collidingBricks.Count > 0)
    {
        foreach (var ball in balls)
        {
            Brick closestBrick = null;
            foreach (var brick in collidingBricks)
            {
                if (closestBrick == null)
                {
                    closestBrick = brick;
                    continue;
                }
                if (Vector2.Distance(ball.GetCenterpoint(brick), ball.GetCenterpoint()) < Vector2.Distance(ball.GetCenterpoint(closestBrick), ball.GetCenterpoint()))
                {
                    closestBrick = brick;
                }else
                {
                    brick.Health--;
                    if (brick.Health > 0) brick.Texture = Assets.GetBrick(brick.TextureName, brick.Health);
                }
            }

            if (ball.Collide(closestBrick))
            {
                closestBrick.Health--;
                if (closestBrick.Health > 0) closestBrick.Texture = Assets.GetBrick(closestBrick.TextureName, closestBrick.Health);
            }
        }
        collidingBricks = new List<Brick>();
    }

    bricks.RemoveAll(brick => brick.Health <= 0);
    base.Update(gameTime);
}

球的碰撞方法决定了球的新速度值。如你所见,它完全基于球的位置,而不是球的位置。另外,我对碰撞球最接近的砖进行碰撞检查,但是我减少了球接触的所有砖块的健康状况(因此,速度只会根据更新最近的砖块,而不是所有碰撞砖块。“

我明白为什么它会给我带来麻烦,而这可能是因为球在同一时间击中两侧(因为它碰到了角落)。

就像我说的那样,这是一个小问题,但是甚至可以对游戏体验产生影响,特别是当你希望球在逻辑上向一个方向前进而只是为了让它在相反的方向上旅行。

2 个答案:

答案 0 :(得分:1)

我假设每次调用你的Update方法,你通过添加由帧时间缩放的速度向量将球移动到新位置,然后测试新位置。这是对的吗?

在该模型中,球占据一系列点位置而不穿过中间空间。由于称为“隧道效应”的效果,这种类型的运动引起各种问题。当一个物体移动得足够快以至于部分或完全穿过物体时,会发生这种情况。

想象一下,您的球以每帧3个像素垂直移动,目前距离块边缘1个像素。下一帧,球将移动到块内<2>像素的位置。由于球和块不能重叠,这是一个错误。也许不是巨大的错误,但仍然不对。

正如您所发现的,当您到达角落时,错误确实会让您感到困惑。如果球的新位置与块的角重叠,则确定正确的动作变得更加困难,因为块的最近边缘可能不是您通过(概念上)到达最终结果的位置。

对此的解决方案是称为连续碰撞检测。每次移动球时,都​​必须检查整个运动路径以确定是否发生碰撞。

最简单的方法 - 虽然肯定不是最快的 - 可能是使用类似Bresenham's Algorithm的东西来绘制你的球沿着运动线占据的位置。 Bresenham会给你一个合理的运动线,整数步长 - 每像素一步。这些步骤转换为可用于检测碰撞的坐标。

通过预先计算是否可能来发生碰撞,你可以稍微加快速度,如果是这样的话。如果屏幕上有100个块,则不希望每个步骤检查每个块。您可以通过计算总移动的矩形边界框(当前位置到结束位置)来缩小范围,并构建所有块和列在框内的其他对象的列表。没有结果意味着没有碰撞,所以这是一个很好的方法来缩短昂贵的操作,同时最终使它们更便宜。

有很多工作要做,以使其完美,并有相当大的数量,以使它足够接近。有关这些主题的更多信息,请访问Google continuous collision detectiongame physics tunneling

如果你可以逃脱它,像[Box2D]或Farseer这样的东西会给你一些有趣的选择来处理它。当然,您可能花费大量时间在物理引擎周围重新设计游戏,就像解决原始问题一样:P

答案 1 :(得分:0)

我认为通过沿着浅轴解决碰撞可以解决这两个问题。考虑一下这种情况:

在你当前的代码中,这个碰撞会沿着Y轴解析,因为它有一个偏好,尽管X轴上明显发生了碰撞。

碰撞的一般方法是首先获得碰撞深度(这可以通过使用Rectangle.Intersect静态方法创建一个新矩形并使用其宽度和高度来轻松实现。然后你必须检查两个轴的交叉点有多深,在你的情况下,还要检查球的速度矢量的反向浅轴分量。最后一步是检查两个相交对象的相对位置,并沿着浅轴移动其中一个 - 在给定的示例中,球位于砖的右侧,因此必须通过交叉深度向右移动。 / p>