球在每次碰撞中获得越来越多的能量

Ball is getting more and more energi for each collision

我正在尝试在 monogame 中编写一些简单的 2d 物理。 我以给定的速度从给定的起始位置释放一个球,我希望它在与地板碰撞时反弹回来。

我的问题是每次弹跳我似乎都给了球更多的能量,即每次与地板碰撞时它弹得越来越高。应该是反过来的。

我有:

float get_VelocityX(float _speed, double _angle)
{
    return velocity_x = velocity_x +_speed * (float)Math.Cos(_angle);
}

public float get_VelocityY(float _speed, double _angle, float _t, float gravity)
{
    return velocity_y = velocity_y + _speed * (float)Math.Cos(_angle); // - (float)(-gravity * _t);
}

在我的更新功能中我有这个:

            if (speed > 0)
            {
                timeCount += (float)gameTime.ElapsedGameTime.TotalSeconds;
                t += timeCount;
            }
            else
            {
                return;
            }

            Vx = ball.get_VelocityX(speed, angle);
            Vy = ball.get_VelocityY(speed, angle, t, gravity);
           
            if (posX >= windowMAX)
            {
                posX = posX + -Vx * friction * t;
            }
            if (posY > windowMIN)
            {
                posY = posY + -Vy * friction * t;
            }
            else
            {
                posY += gravity;
            }

            ballRect.X = (int)posX;
            ballRect.Y = (int)posY;

其中 posX、posY 和速度是用户输入的起始位置和速度。 重力只是一个浮点数 = 9.82f;

现在除了设置球的起始位置外,我没有对 posX 做任何事情。下一步将是实现投掷动作。

编辑: 摩擦力 = 0.001f; t 是增量时间。

我分析了你的逻辑并准备了示例代码。请在阅读之前阅读以下内容。

  1. 为了模拟现实生活中的运动,您需要准确地实现物理。尽管您实施的速度和位置似乎大部分是正确的,但重力需要被视为加速度,因此将其值添加到位置(如您的代码中所做的那样)是不正确的。我认为这就是您没有得到预期结果的原因,因为位置的 Y 分量上的增量值远大于应有的值。

  2. 我建议您不要使用 PosXPosY 表示位置,Velocity_X..()Velocity_Y..() 表示速度,而是使用 struct Vector2 如下所示在我的代码中,它包含在 Monogame 框架中并且内置了更多的帮助功能。这将有助于使您的代码更短更清晰。

  3. 我不明白为什么在 X 和 Y 分量的 Velocity 实现中使用给定角度的余弦。我下面的代码忽略了这一点。

你可以在下面看到我的代码。这里的弹跳对象 Box 属于 struct PhyObj 类型,其中实现了所有需要的运动物理。

    public class Game1 : Game
    {
        private SpriteBatch _batch;
        internal Texture2D Texture;
        public static Vector2 GravityAcceleration => new Vector2(0, 9.8f);//Constant accerleration along Y-Axis
        internal Rectangle Ground;
        internal PhyObj Box;
        public Game1()
        {
            _ = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            IsMouseVisible = true;
        }

        protected override void LoadContent()
        {
            Point center = Window.ClientBounds.Center;
            Box = new PhyObj(new Vector2(center.X, 0), Vector2.Zero, 30, 30);
            Ground = new Rectangle(0, center.Y, center.X * 2, 10);
            _batch = new SpriteBatch(GraphicsDevice);
            Texture = new Texture2D(GraphicsDevice, 1, 1);
            Texture.SetData(new[] { Color.White });
        }
        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();
            Box.Accelerate(GravityAcceleration, gameTime);
            if (Box.Pos.Y > Ground.Top - Box.Dest.Height)//Check if bounce needed
            {
                Box.Pos.Y = Ground.Top - Box.Dest.Height;//Clipping
                Box.Vel.Y *= -1;                         //Bouncing
            }
            base.Update(gameTime);
        }
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            _batch.Begin();
            _batch.Draw(Texture, Ground, Color.Black);
            _batch.Draw(Texture, Box.Dest, Color.White);
            _batch.End();
            base.Draw(gameTime);
        }
    }
    public struct PhyObj
    {
        internal static float friction => 0.005f;
        public PhyObj(Vector2 x, Vector2 v, int width, int height)
        {
            Pos = x;
            Vel = v;
            Dest = new Rectangle(0, 0, width, height);
            (Dest.X, Dest.Y) = ((int)Pos.X, (int)Pos.Y);
        }
        internal Vector2 Pos, Vel;
        internal Rectangle Dest;
        public void Accelerate(Vector2 acc, GameTime time)
        {
            Vel += acc - Vel * friction;
            Pos += Vel * (float)time.ElapsedGameTime.TotalSeconds;
            (Dest.X, Dest.Y) = ((int)Pos.X, (int)Pos.Y);
        }
    }

Update()函数所示,PhyObj Box是在外部加速(本例为重力,但你可以添加自定义外力),而velocity/position它需要达到的目标是内部计算的。

弹跳逻辑很简单:速度的 Y 分量被反转。

此处的“裁剪”过程确保 Box 不会越过 Ground 对象,即使向下加速度作用于它也是如此。

由于摩擦值,接下来的后续弹跳高度降低(由 struct PhyObj 内部完成)。