Search code examples
java2dphysicsrectangles

How to stop the normal force from being too small on a 2D rigidbody? (Java 2D)


I am trying to create a 2D game engine in Java with LWJGL 3. For now the objects are only rectangles with box colliders. For the collision detection I change the edge of the rectangles in to lines with the y = ax + b structure. One of the rectangles has a rigidbody component, that gives it the ability to move and interact with environment. The idea for now is to give the rigidbody a force at the start, gravity turned off, no friction, and a perfect bounce (bounciness=1). It all works very well, till it hits a rectangle with no vertical and horizontal edges. I found out that if the non-rigidbody objects are rotated by an angle other than 90 or 180 degrees the normal force is wrong (too small), how wrong depends on the rotation. The rotation of the rigidbody doesn't contribute to the problem.

Vector2 rotSurface = new Vector2(-collision.surface.ToVector().y, collision.surface.ToVector().x).getNormalized();

System.out.println("Angle: "+Physics.Angle(rotSurface, rb.force));

Vector2 normalForce = rotSurface.multiplyBy(rotSurface.multiplyBy(rb.force).getMagnitude());

rb.force = rb.force.add(normalForce).multiplyBy(1f - rb.friction).add(normalForce.multiplyBy(rb.bounciness));
                           
System.out.println("Angle: "+Physics.Angle(rotSurface, rb.force));
System.out.println("===");

Am I doing something wrong here with calculating the new rigidbody force or the normal force itself? If you need more information to help me solve this problem please ask.

Classes that I use in the code above:

public class Collision {
    Collider collider;
    ArrayList<Vector2> contactPoints = new ArrayList<Vector2>();
    Vector2 center = Vector2.zero;
    Line surface;
    
    public Collision(Collider collider, ArrayList<Vector2> contactPoints, Line surface) {
        this.collider = collider;
        this.contactPoints = contactPoints;
        for(int i = 0; i < contactPoints.size(); i++) {
            center = contactPoints.get(i).add(center).divideBy(2);
        }
        this.surface = surface;
    }
}

class Line {
    Vector2 begin, eind;
    public float rc = 0;
    public float b = 0;
    public float x, y;
    public boolean vertical = false;
    public boolean horizontal = false;
    
    public Line(Vector2 begin, Vector2 eind) {
        this.begin = begin;
        this.eind = eind;
        if(begin.y == eind.y) {
            this.y = begin.y;
            this.horizontal = true;
        }
        else if(begin.x == eind.x){
            this.x = begin.x;
            this.vertical = true;
        } else {
            this.rc = (eind.y - begin.y) / (eind.x - begin.x);
            this.b = GetB(rc, begin);
        }
    }
    
    public float GetB(float rc, Vector2 punt) {
        return punt.y - (rc * punt.x);
    }
    
    public Vector2 GetIntersection(Line l2) {
        float x_ = rc - l2.rc;
        if(vertical) {
            x_ = x;
            if(l2.horizontal) {
                return new Vector2(x, l2.y);
            }
            else if(!l2.vertical){
                //System.out.println("gert");
                return new Vector2(x, l2.rc * x + l2.b);
            }
        } else if(horizontal) {
            if(l2.vertical) {
                return new Vector2(l2.x, y);
            } else if(!l2.horizontal) {
                return new Vector2((y-l2.b) / l2.rc, y);
            }
        } else {
            if(l2.vertical) {
                return new Vector2(l2.x, rc * l2.x + b);
            } else if(l2.horizontal) {
                return new Vector2((l2.y - b) / rc, l2.y);
            }
        }
        if(x_ == 0) {
            return null;
        }
        float getal = l2.b - b;
        x_ = getal / x_;
        float y_ = rc * x_ + b;
        return new Vector2(x_, y_);
    }
    
    public Vector2 ToVector() {
        return eind.substract(begin);
    }
    
    public float GetDisTo(Vector2 point) {
        Vector2 point1 = begin.add(eind).divideBy(2);
        return (float) Math.sqrt(Math.pow(point1.x - point.x, 2) + Math.pow(point1.y - point.y, 2));
    }
    
    public boolean Overlaps(Line line) {
        if(horizontal && y == line.y) {
            if(((line.begin.x > begin.x && line.begin.x < eind.x) || (line.eind.x > begin.x && line.eind.x < eind.x)) ||
               ((begin.x > line.begin.x && begin.x < line.eind.x) || (line.eind.x > line.begin.x && eind.x < line.eind.x)))
            return true;
        } else if(vertical && x == line.x) {
            //return true;
        }
        return false;
    }
}

class Vector2 {
    float x, y;
    public static Vector2 zero = new Vector2(0, 0);
    
    public Vector2(float x, float y) {
        this.x = x;
        this.y = y;
    }
    
    public Vector2 multiplyBy(Vector2 vector) {
        return new Vector2(x * vector.x, y * vector.y);
    }
    
    public Vector2 multiplyBy(float getal) {
        return new Vector2(x * getal, y * getal);
    }
    
    public Vector2 divideBy(Vector2 vector) {
        return new Vector2(x / vector.x, y / vector.y);
    }
    
    public Vector2 divideBy(float getal) {
        return new Vector2(x / getal, y / getal);
    }
    
    public Vector2 add(Vector2 vector) {
        return new Vector2(x + vector.x, y + vector.y);
    }
    
    public Vector2 substract(Vector2 vector) {
        return new Vector2(x - vector.x, y - vector.y);
    }
    
    public float getMagnitude() {
        return (float)Math.sqrt(x*x + y*y);
    }
    
    public Vector2 getNormalized() {
        return divideBy(getMagnitude());
    }
}

Solution

  • Fixed it by using a sinus. I replaced

    Vector2 normalForce = rotSurface.multiplyBy(rotSurface.multiplyBy(rb.force).getMagnitude());
    

    with

    Vector2 normalForce = rotSurface.multiplyBy((float)Math.sin(Math.toRadians((double) (90f - Physics.Angle(rotSurface, force)))) * force.getMagnitude());