【问题标题】:Physics Simulation Ball falling into floor [duplicate]物理模拟球落入地板[重复]
【发布时间】:2021-04-12 00:16:11
【问题描述】:

过去几天我一直在使用 MonoGame 和 C# 对球进行物理模拟。球在重力作用下弹跳得很好,但是当球的弹跳太低时,球就会掉到地板上。有什么办法可以阻止这种情况发生吗? 我已经尝试过降低重力常数,改变碰撞的工作方式等,但似乎没有任何效果。 (我对使用图形还很陌生,所以简单的解释会很有帮助)

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Particle_Simulation
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Texture2D particleTexture;

        SpriteFont font;

        Rectangle particle;

        Vector2 velocity = new Vector2(4, 0);
        Vector2 acceleration = new Vector2(0, 0.25f);

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            //graphics.PreferredBackBufferWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
            //graphics.PreferredBackBufferHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
            Content.RootDirectory = "Content";
        }
        
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            //Load your game content here
            particleTexture = Content.Load<Texture2D>("ball");
            font = Content.Load<SpriteFont>("textFont");
            particle = new Rectangle(200, 200, 60, 60);
        }

        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            //Add your update logic here
            velocity.X += acceleration.X;
            velocity.Y += acceleration.Y;
            
            particle.X += (int)velocity.X;
            particle.Y += (int)velocity.Y;

            if (particle.Top <= 0 || particle.Bottom >= GraphicsDevice.Viewport.Height)
                velocity.Y = -velocity.Y;
            if (particle.Left <= 0 || particle.Right >= GraphicsDevice.Viewport.Width)
                velocity.X = -velocity.X;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            //Add your drawing code here
            spriteBatch.Begin();

            spriteBatch.Draw(particleTexture, particle, Color.White);
            spriteBatch.DrawString(font, "X: " + velocity.X.ToString() + " Y: " + velocity.Y.ToString(), new Vector2(0, 0), Color.White);

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

【问题讨论】:

  • 您的帖子中没有足够的信息确切地了解为什么碰撞不会阻止物体穿过地板。但是,重复的帖子中很好地描述了几种不同的可能性。如果您在查看这些内容后仍然遇到问题,请发布一个新问题,其中您提供了一个很好的 minimal reproducible example 和更详细的问题说明、到目前为止您尝试解决的问题以及具体 i> 你需要帮助。
  • @PeterDuniho 我已经查看了其他问题,但它们似乎正在处理 Unity 中预先构建的碰撞项目。这些和这个问题之间的区别在于我正在创建自己的碰撞检测,而不是使用 Unity 的内置碰撞。
  • “我正在创建自己的碰撞检测,而不是使用 Unity 的内置碰撞”——嗯,首先要改变的是,不要那样做。您正在使用具有精细物理引擎和碰撞检测系统的平台。它至少和你要实现的任何东西一样有效。或者,做与 Unity3d 相同的事情并为您的物理使用固定更新(实际上至少在其中一个副本中对此进行了解释)。

标签: c# physics monogame


【解决方案1】:

这是碰撞检测的经典问题之一,碰撞发生在对象路径上的某个点,但对象移动得足够远,以至于下一个刻度无法解决问题,留下对象 - 你的球在这个case - 永远落在视口之外。

要解决这个问题,您不仅需要改变垂直速度,还要让球从碰撞点反射回来。您已经在弹跳速度矢量,您只需要添加位置反射。简单的方法是只取过冲量并从限制中减去它,将球放回边界:

if (particle.Top < 0)
{
    velocity.Y = -velocity.Y;
    particle.Y = -particle.Y;
}
else if (particle.Bottom >= GraphicsDevice.ViewPort.Height)
{
    velocity.Y = -velocity.Y;
    particle.Y = GraphicsDevice.ViewPort.Height - (particle.Bottom - GraphicsDevice.ViewPort.Height + particle.Height);
}

对速度和位置的X 部分执行相同操作。在大多数情况下,即使是边角穿透,最终结果也应该没问题。

由于您只是反映了整个速度,因此您会得到一些累积误差,这些误差会导致每次击球后球弹得“更高”。想象一个球直接落下,但目前刚好在表面上方。在没有碰撞的情况下,加速后球的速度(大致)是正确的。但是......如果碰撞发生在行进距离的 10% 处,那么只有一部分加速度会被添加到该点的速度上,然后路径的其余部分会减慢,因为现在垂直速度与加速度。在极端情况下 - 底部上方的低高度、非常低的速度和高“重力” - 你仍然可能最终将球嵌入地面。

这会让你的碰撞代码更难一些,但至少你不会因为加速错误的累积而导致球飞出屏幕。

从您拥有的加速度和速度代码开始,然后如果发生碰撞,您需要在确定碰撞发生的路径多远后返回并再次执行此操作。您可以从简单地使用它作为一个比率开始,但如果您的目标是真正的准确性,那么您需要做一些有趣的数学来让它真正发挥作用。需要一些微积分来计算真实的撞击速度和反弹后的重力影响,但要找到所有这些东西的参考应该不难。

您可能想要做的另一件事是在撞击后降低速度。完美的弹性碰撞在碰撞后给出相同的速度,但在现实世界中,物体的一些动量被浪费为热量、声音等。您可以通过对反向速度使用一个小乘数来表示这一点 - 乘以 @987654323 @ 或其他东西 - 在碰撞中增加一些损失。

由于您几乎永远无法在错误中获得完美的平衡,因此球要么加速要么减速,最终逃出或落在地板上。确保您有应对这两种情况的策略。

当你遇到任意角度的碰撞时……数学又变得更有趣了。


哦,别忘了用gameTime 缩放你的加速度值。 ElapsedGameTime 属性将告诉您自上次调用 Update 以来已经过了多长时间,并且它会不一致。如果一个帧需要很长时间来处理,那么球的加速度就会变慢。同样,短帧时间会使球移动得更快。

【讨论】:

    猜你喜欢
    • 2012-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多