当前位置: 首页 > 知识库问答 >
问题:

C#XNA-突破式球物理

景凌
2023-03-14

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

我有桨和球代码工作良好,以及一个简单的角速度的基础上,球击中桨,但我的问题是当球击中砖块。代码大多有效,似乎完美无瑕,但当球做以下两件事之一时,情况会发生变化:

  • 击中砖块的一个角可能会错误地扭转球的速度轴
  • 同时击中两块砖块(在它们的角落)会导致球穿过它们并移走它们;即使他们有完全的健康。

我使用的代码在这里(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;
}

这些方法是从级别调用的。cs的更新方法:

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个答案

冯阳华
2023-03-14

我认为这两个问题都可以通过解决沿其浅轴的碰撞来解决。考虑这种情况:

在您当前的代码中,这种碰撞将沿Y轴解决,因为有一个偏好,尽管碰撞显然发生在X轴上。

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

宗政法
2023-03-14

我假设每次调用更新方法时,通过添加按帧时间缩放的速度矢量,将球移动到一个新位置,然后测试新位置。这是正确的吗?

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

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

正如你所发现的,当你到达角落时,错误真的会咬你。如果球的新位置与街区的角落重叠,那么确定正确的动作就变得更加困难,因为街区最近的边缘可能不是你穿过的边缘(概念上)到达你最终到达的地方。

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

最简单的方法——尽管肯定不是最快的——可能是使用像布雷森汉姆算法这样的东西来绘制你的球在运动线上占据的位置。布雷森汉姆算法会给你一个合理的整数步运动线——每像素一步。这些步骤转化为坐标,你可以用它来检测碰撞。

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

要使其完美,还有很多工作要做,而要使其足够接近,还有相当多的工作要做。谷歌连续碰撞检测和游戏物理隧道以获取有关主题的更多信息。

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

 类似资料:
  • 我正在创建一个突破游戏来熟悉Java。目前我已经把一切都做好了,但我注意到球似乎是朝着同一个方向飞的。 我已经对它进行了编程,当球碰到球拍时,Y速度是相反的。这是可行的,但球似乎遵循相同的路线。。。 如何让碰撞看起来更真实? 当球击中桨的左侧时,我想将其重新定向到左侧,但我不确定在哪个角度反射它。有人能提供一个公式来帮助我计算出当球接触桨的左侧或右侧时需要引导的角度吗?

  • 我正在创建一个突破游戏,我之前在这里问了一个问题,但没有提出解决方案。我有一个简单的物理过程来反转球与桨碰撞时的速度。但我需要实现一个更先进的物理系统,例如,当球击中桨的左侧和右侧时,等等。。。但我该如何计算碰撞后球的方向呢?突破的标准是在与球拍左侧碰撞时将球指向左侧,如果碰撞是向右的,则指向右侧,等等。。。 我如何计算在哪里击球?我有很多变量可以使用,比如桨叶的各个方面(宽度、高度、X位置)、球

  • 我目前正在尝试使用java和libgdx制作一个突破性的克隆。我现在很难让球以合适的角度从挡块上弹起。简而言之,我遇到的问题是,球每帧移动12个像素,并不总是与砖的边缘对齐。如果有人对更好的移动球的方法或其他检查碰撞的方法有任何建议,我们将不胜感激! 主要游戏类 Ball类 砖块代码以防万一

  • 我正在读《java的艺术与科学:计算机科学导论》一书学习java。练习程序之一是创建突破游戏的简单克隆。 我目前能够加载游戏,但球的物理问题。我正在使用最简单的物理可能,不明白为什么它不起作用。 当球撞到墙上时,它会正常反弹,但当它撞到桨或砖块时,它不会。我在碰撞检测中使用相同的代码来更改我在墙上使用的方向。检测到碰撞后,我添加了一个println并观察了控制台处理碰撞事件的过程,但方向没有发生变

  • 我目前正在学习C#和XNA,我在让碰撞检测正常工作方面遇到了一些问题。当敌人物体相交时,我希望我的玩家消失。 下面显示了我在Obj类中的代码 我正在循环查看objList中的每个项目,看它们是否相交。在这种情况下,如果敌人与我的玩家相交,我希望玩家消失,但这并没有发生。 此代码来自我的Player类 如果它与敌人相交,它应该消失,但这似乎没有发生。

  • 我已经在 http://www.petercollingridge.co.uk/pygame-physics-simulation/ 上看到了Peter Colling Ridge编写的非常棒的教程,我正在扩展PyParticles脚本 代码可以在站点上获得(免费),我使用的是pyparticles4.py 粒子类 具有半径、质量、速度、位置的圆形2D物体 Spring类 捆绑2个物体(粒子)并利