问题描述
我正在尝试在单游戏中编写一些简单的 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 和 speed 是用户输入的起始位置和速度。 重力只是一个浮点数 = 9.82f;
现在除了设置球的起始位置外,我不会对 posX 做任何事情。下一步将是实现投掷动作。
编辑: 摩擦 = 0.001f; t 是增量时间。
解决方法
我了解了您的逻辑并准备了示例代码。请在阅读之前阅读以下内容。
-
为了模拟现实生活中的运动,您需要准确地实现物理。尽管您实现的速度和位置似乎大部分是正确的,但重力需要被视为加速度,因此将其值添加到位置(如您的代码中所做的那样)是不正确的。我认为这就是您没有得到预期结果的原因,因为位置 Y 分量的增量值远大于应有的值。
-
与其保持
C
,PosX
为位置,PosY
,Velocity_X..()
为速度,我建议您使用Velocity_Y..()
作为下面显示在我的代码中,它包含在 Monogame 框架中,并且内置了更多帮助功能。这将有助于使您的代码更短、更简洁。 -
我不明白您为什么在 X 和 Y 分量的 Velocity 实现中使用给定角度的余弦。我下面的代码忽略了这一点。
你可以在下面看到我的代码。此处弹跳对象 struct Vector2
属于 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,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,(int)Pos.Y);
}
}
函数所示,Update()
正在被外部加速(在这种情况下为重力,但您可以添加自定义外力),它需要达到的速度/位置为内部计算。
弹跳逻辑很简单:速度的 Y 分量被反转。
此处的“裁剪”过程可确保 PhyObj Box
不会越过 Box
对象,即使向下加速度作用在其上也是如此。
由于摩擦值(由 Ground
在内部完成),下一次后续弹跳的高度会降低。