【问题标题】:Simulating Gravity in C# : Error with my approach or implementation?在 C# 中模拟重力:我的方法或实现出错?
【发布时间】:2016-05-26 15:37:55
【问题描述】:

我正在尝试制作一个简单的 2D 物理引擎。引擎将重力定义为 90 度(垂直向下)的 10 个单位。在宇宙中,存在物理实体,每个实体都具有由速度和方向组成的速度。每次实体更新时,我都会将重力向量添加到每个实体的速度向量中。

如果实体静止或平行于重力矢量移动,它会掉下来,否则它会工作一半。我的意思是,假设物体在表面上方 45 度角拍摄,它会上升,弯曲到顶点,然后继续平行于表面。重力似乎会随着它的上升而改变速度,但不会使物体再次落下。

我想知道我解决这个问题的方法是否有问题,或者我的解决方案在代码中的实现是否有问题。

更新功能:

public static void UpdateAll()
    {

        foreach (var entity in Entities)
        {
            entity.Velocity.AddVector(Universe.Gravity.Speed, Universe.Gravity.Direction);
        }

        var iterator = 1;
        foreach (var entity in Entities)
        {

            for (var index = iterator; index < Entities.Count; index++)
            {
                //Collision Detection
            }

            entity.Update();

            iterator++;
        }
    }

添加向量函数:

public void AddVector(float speed, int direction)
    {
        var radiansA = Angles.DegreesToRadians(SetDirection);
        var radiansB = Angles.DegreesToRadians(direction);

        var vAx = SetSpeed * (float) Math.Cos(radiansA);
        var vAy = SetSpeed * (float) Math.Sin(radiansA);
        var vBx = speed * (float) Math.Cos(radiansB);
        var vBy = speed * (float) Math.Sin(radiansB);

        var magX = vAx + vBx;
        var magY = vAy + vBy;

        var magnitude = (float) Math.Sqrt(Math.Pow(magX, 2) + Math.Pow(magY, 2));

        var theta = Angles.RadiansToDegrees((float) Math.Atan2(magY, magX));

        SetSpeed = magnitude;
        SetDirection = theta;
    }

编辑:我删除了一些 cmets 建议的 if 语句,但结果仍然相同。

已解决:在 cmets 中指出了代码错误,我更新了 add 函数以反映所需的更改。

【问题讨论】:

  • 如果你使用 vectors 开始,它实际上要容易得多。不知道为什么需要if 的东西
  • 将速度作为大小和方向是项目规范的一部分。 if 是在对象没有速度的情况下存在的。
  • @Jedi_Maseter_Sam 如果您使用向量,您仍然可以获得向量运行时的大小和方向。您可以使用属性使其透明。
  • 我现在无法正确测试,但请检查 Math.Atan2 而不是 Math.Acos 以恢复您的最终角度。此外,一些适当的测试用例也是一个好主意。
  • @Jedi_Maseter_Sam “将速度作为大小和方向是项目规范的一部分” - 你认为向量是什么?

标签: c# physics game-physics


【解决方案1】:

不确定为什么需要测试方向。无论物体如何移动,都应添加重力矢量。如果它向上移动,它会放慢速度。如果它向下移动,它会加速。如果它水平移动或根本不移动,它将开始下降。无论如何,这是通过添加重力矢量来实现的。

速度也应该是一个向量。它应该有一个 x 和一个 y 分量。那么所有这些“方向”的东西都可以被丢弃。因此,到处使用由 x 和 y 定义的向量。然后,所有速度计算都将简化为简单的矢量加法。这使得所有数学运算都比使用角度简单得多。

public struct Vector
{
    public Vector(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }

    public int X { get; }
    public int Y { get; }

    public static Vector operator +(Vector a, Vector b)
    {
        return new Vector(a.X + b.X, a.Y + b.Y);
    }

    public float Magnitude
    {
        get
        {
            int d = X * X + Y * Y;
            if (d == 1 || d == 0) {
                return d;
            } else {
                return (float)Math.Sqrt(d);
            }
        }
    }

    public override string ToString()
    {
        return $"Vector({X}, {Y})";
    }
}

也无需计算每次速度变化后的幅度和角度。当您需要从适当的属性中获取这些值时,只需从向量中获取这些值。 (只是添加了一个 Magnitude 属性作为示例)

Vector speed, gravity;
...
//Calculate new speed: 
speed += gravity;

另一个有用的运算符重载是向量乘以常数

public static Vector operator *(double c, Vector a)
{
    return new Vector((int)Math.Round(c * a.X), (int)Math.Round(c * a.Y));
}

【讨论】:

  • 我将此标记为已接受的答案,因为将来尝试这样做的任何其他人都会发现您的解决方案非常有用。它对我的特定问题没有帮助,但我肯定会把它添加到我的速度类中。
  • 在这里使用int 表示向量的组件似乎非常不合适。 floatdouble 会是更好的选择。
  • 我看到您的错误的三个可能来源。 1. 计算中存在逻辑错误。 2. 某处的整数数学将某些内容截断为 0,例如1/2*5 == 0,因为 1/2 以整数除法执行并产生 0。3. 碰撞检测检测到不应该发生的碰撞。
  • 错误是我使用Math.Acos 而不是Math.Atan2
  • 我一定忘记了我的数学,但是向量不需要以某种方式指定方向吗?单个 x,y 坐标是如何实现的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多