C#XNA-突破式球物理

C#XNA-突破式球物理,c#,xna,physics,bounce,breakout,C#,Xna,Physics,Bounce,Breakout,我正在为一项大学作业创建一个项目,但我遇到了一点障碍。这个问题很小,很可能被当作不被注意的问题,但它仍然困扰着我,而且,作为一个完成工作的程序性人员,在问题解决之前,我不能继续做任何事情 我有桨和球代码工作良好,以及一个简单的角速度的基础上,球击中桨,但我的问题是当球击中砖块。代码基本上是有效的,而且似乎完美无瑕,但是当球执行以下两项操作之一时,代码会发生变化: 在砖块的一个角上击打砖块可能会错误地反转球的速度轴 同时击打两块砖块(在它们的角上)将使球穿过它们并将它们移走;即使他们身体健康

我正在为一项大学作业创建一个项目,但我遇到了一点障碍。这个问题很小,很可能被当作不被注意的问题,但它仍然困扰着我,而且,作为一个完成工作的程序性人员,在问题解决之前,我不能继续做任何事情

我有桨和球代码工作良好,以及一个简单的角速度的基础上,球击中桨,但我的问题是当球击中砖块。代码基本上是有效的,而且似乎完美无瑕,但是当球执行以下两项操作之一时,代码会发生变化:

  • 在砖块的一个角上击打砖块可能会错误地反转球的速度轴
  • 同时击打两块砖块(在它们的角上)将使球穿过它们并将它们移走;即使他们身体健康
我使用的代码如下(Ball.cs):

编辑(更新代码):

public bool触碰(砖块)
{
var位置=位置+速度;
返回位置.Y+大小.Y>=砖块位置.Y&&
位置Y=砖块位置X&&

position.X我假设每次调用
Update
方法时,通过添加按帧时间缩放的速度向量,将球移动到新位置,然后测试新位置。这是否正确

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

假设您的球以每帧3像素的速度垂直移动,并且当前距离块的边缘1像素。下一帧,球将移动到块内2像素的位置。由于球和块不能重叠,这是一个错误。可能不是很大的错误,但仍然不正确

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

解决方法是调用连续碰撞检测。每次移动球时,必须检查整个运动路径以确定是否发生碰撞

最简单的方法——虽然肯定不是最快的——可能是使用类似于绘制球沿运动线所占据的位置的方法。Bresenham的方法将为你提供一条合理的运动线,每像素一步。这些步骤转换为坐标,你可以用它来检测碰撞

您可以通过预先计算是否可能发生碰撞以及发生碰撞的原因来略微加快速度。如果屏幕上有100个块,您不想检查每个步骤的每个块。您可以通过计算总移动(当前位置到结束位置)的矩形边界框来缩小范围并建立一个包含所有块和其他对象的列表。没有结果意味着没有碰撞,因此这是一个很好的方法,可以缩短昂贵的操作,同时最终降低它们的成本

要使它完美,还有很多工作要做,要使它足够接近,还需要相当大的工作量。谷歌
连续碰撞检测
游戏物理隧道
了解有关主题的更多信息


如果你能侥幸逃脱,比如[Box2D]或者Farseer会给你一些有趣的选择来处理这个问题。当然,你可能会花费与解决原始问题一样多的时间围绕物理引擎重新调整游戏:P

我假设每次调用

更新
方法时,你都会通过添加速度向量将球移动到一个新位置或按帧时间缩放,然后测试新位置。是否正确

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

假设您的球以每帧3像素的速度垂直移动,并且当前距离块的边缘1像素。下一帧,球将移动到块内2像素的位置。由于球和块不能重叠,这是一个错误。可能不是很大的错误,但仍然不正确

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

解决方法是调用连续碰撞检测。每次移动球时,必须检查整个运动路径以确定是否发生碰撞

最简单的方法——虽然肯定不是最快的——可能是使用类似于绘制球沿运动线所占据的位置的方法。Bresenham的方法将为你提供一条合理的运动线,每像素一步。这些步骤转换为坐标,你可以用它来检测碰撞

您可以通过预先计算是否可能发生碰撞以及发生碰撞的原因来稍微加快速度。如果屏幕上有100个块,您不想检查每个步骤的每个块。您可以通过计算总移动(当前位置)的矩形边界框来缩小范围
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;
}
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);
}