物理模拟球落入地面
Physics Simulation Ball falling into floor
过去几天,我一直在使用 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);
}
}
}
这是碰撞检测的经典问题之一,碰撞发生在物体路径上的某个点,但物体移动得足够远以至于下一个刻度不能解决问题,留下物体 - 你的球在这种情况下 - 永远落在视口之外。
要解决这个问题,您不仅需要改变垂直速度,还需要让球从碰撞点反射回来。你已经在反弹速度矢量,你只需要添加位置反射。简单的方法是只取超调量并从限制中减去它,将球放回界内:
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
部分执行相同的操作。在大多数情况下,即使是角落穿透,最终结果也应该没问题。
由于您只是反映整个速度,因此您会得到一些累积误差,这些误差会导致球在每次撞击后弹跳 'higher'。想象一个球笔直下落但目前刚好在表面上方。加速后,如果没有碰撞,球的速度(大致)是正确的。但是...如果碰撞发生在行进距离的 10% 处,那么只有一部分加速度会添加到该点的速度上,然后路径的其余部分将会减慢,因为垂直速度现在与加速度。在极端情况下 - 底部高度低,速度非常低且高 'gravity' - 你仍然可能最终将球嵌入地面。
这会使你的碰撞代码更难一些,但至少你不会因为加速错误的累积而导致球飞出屏幕。
从您拥有的加速度和速度代码开始,然后如果发生碰撞,您需要返回并在弄清楚碰撞发生的路径有多远之后再做一次。您可以从简单地将其用作比率开始,但如果您的目标是真正的准确性,那么您需要做一些有趣的数学运算才能真正发挥作用。计算出撞击时的真实速度和反弹后重力的影响涉及一些微积分,但找到所有这些东西的参考应该不难。
您可能想要做的另一件事是在撞击后降低速度。完美的弹性碰撞在碰撞后给出相同的速度,但在现实世界中,物体的一些动量被浪费为热量、声音等。您可以通过对反向速度使用一个小的乘数来表示这一点 - 乘以 0.99
或其他东西 - 在碰撞中增加一些损失。
因为你几乎永远不会在你的失误中获得完美的平衡,球要么加速要么减速,最终逃脱或落在地板上。确保您有应对这两种情况的策略。
当你以任意角度发生碰撞时......数学又变得更有趣了。
哦,别忘了按 gameTime
缩放加速度值。 ElapsedGameTime
属性 会告诉您自上次调用 Update
以来已经过了多长时间,而且它会不一致。如果一帧需要很长时间来处理,那么球的加速度就会变慢。同样,较短的帧时间会使球移动得更快。
过去几天,我一直在使用 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);
}
}
}
这是碰撞检测的经典问题之一,碰撞发生在物体路径上的某个点,但物体移动得足够远以至于下一个刻度不能解决问题,留下物体 - 你的球在这种情况下 - 永远落在视口之外。
要解决这个问题,您不仅需要改变垂直速度,还需要让球从碰撞点反射回来。你已经在反弹速度矢量,你只需要添加位置反射。简单的方法是只取超调量并从限制中减去它,将球放回界内:
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
部分执行相同的操作。在大多数情况下,即使是角落穿透,最终结果也应该没问题。
由于您只是反映整个速度,因此您会得到一些累积误差,这些误差会导致球在每次撞击后弹跳 'higher'。想象一个球笔直下落但目前刚好在表面上方。加速后,如果没有碰撞,球的速度(大致)是正确的。但是...如果碰撞发生在行进距离的 10% 处,那么只有一部分加速度会添加到该点的速度上,然后路径的其余部分将会减慢,因为垂直速度现在与加速度。在极端情况下 - 底部高度低,速度非常低且高 'gravity' - 你仍然可能最终将球嵌入地面。
这会使你的碰撞代码更难一些,但至少你不会因为加速错误的累积而导致球飞出屏幕。
从您拥有的加速度和速度代码开始,然后如果发生碰撞,您需要返回并在弄清楚碰撞发生的路径有多远之后再做一次。您可以从简单地将其用作比率开始,但如果您的目标是真正的准确性,那么您需要做一些有趣的数学运算才能真正发挥作用。计算出撞击时的真实速度和反弹后重力的影响涉及一些微积分,但找到所有这些东西的参考应该不难。
您可能想要做的另一件事是在撞击后降低速度。完美的弹性碰撞在碰撞后给出相同的速度,但在现实世界中,物体的一些动量被浪费为热量、声音等。您可以通过对反向速度使用一个小的乘数来表示这一点 - 乘以 0.99
或其他东西 - 在碰撞中增加一些损失。
因为你几乎永远不会在你的失误中获得完美的平衡,球要么加速要么减速,最终逃脱或落在地板上。确保您有应对这两种情况的策略。
当你以任意角度发生碰撞时......数学又变得更有趣了。
哦,别忘了按 gameTime
缩放加速度值。 ElapsedGameTime
属性 会告诉您自上次调用 Update
以来已经过了多长时间,而且它会不一致。如果一帧需要很长时间来处理,那么球的加速度就会变慢。同样,较短的帧时间会使球移动得更快。