Search code examples
c#physicsgame-physics

Simulating Gravity in C# : Error with my approach or implementation?


I am trying to make a simple 2D physics engine. The engine defines the gravity to be 10 units at 90 degrees (which is straight down). In the universe, there are physics entities that each have a velocity made up of speed and direction. Each time the entities update, I add the gravity vector to the velocity vector of each entity.

If the entity is stationary or is moving parallel to the gravity vector it falls down, otherwise it half works. What I mean by that is, say and object is shot at a 45 degree angle above the surface, it will go up, curve to the vertex, and then continue parallel to the surface. The gravity seems to change the velocity as it goes up, but does not make the object ever fall back down.

I was wondering if there was something wrong with the approach I am taking to solve this, or if there is something wrong with the implementation of my solution in the code.

Update Function:

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++;
        }
    }

Add Vector Function:

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;
    }

EDIT: I removed the if statements as suggested by some of the comments, but the result is still the same.

SOLVED: The error with the code was pointed out in the comments, and I updated the add function to reflect the changes needed.


Solution

  • Not sure why you need to test for the direction. The gravity vector should be added, no matter how the object is moving. If it is moving up, it will be slowed down. If it moving down, it will accelerate. If it moving horizontally or not moving at all, it will start falling. In any case this is achieved by adding the gravity vector.

    The speed should be a vector as well. It should have an x and a y component. Then all these "direction" stuff could just be dropped. Consequently use vectors defined by x and y everywhere. All speed calculations will then be reduced to simple vector additions. This makes all the math much simpler than using angles.

    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})";
        }
    }
    

    There is also no need to calculate the magnitude and the angle after each speed change. Just get these values from the vector when you need them from appropriate properties. (Just added a Magnitude property as an example)

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

    Another useful operator overload is the multiplication of a vector by a constant

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