1
votes

I'm just starting with physics, so I'm not always sure about what I'm doing. It's a 2D project but I'm using 3D physical objects like SphereCollider etc..

What I have:

Objects floating in space and affecting each other through gravity:

protected virtual IEnumerator OnTriggerStay(Collider other) {
    yield return new WaitForFixedUpdate();

    if(other.attachedRigidbody) {
        Vector3 offsetVector = this.transform.position - other.transform.position;
        float distance = offsetVector.magnitude;
        float gravityForce = (other.rigidbody.mass * mass) / Mathf.Pow(distance, 2);
        // Clamp gravity.
        if(gravityForce > 1.0F) {
            gravityForce = 1.0F;
        }
        other.attachedRigidbody.constantForce.force = offsetVector.normalized * gravityForce;
    }
}

There are controllable objects on which the player can click and drag a line away from the object in order to give it a force (shoot) in the opposite direction.

What I want to achieve:

The player should see a rough prediction of the way while aiming. That means that the way-prediction needs to take in account the current velocity, the force which would be applied when the player release the mouse button and the gravity of the surrounding objects.

What I have tried so far:

For testing purposes I just save the computed/predicted positions in an array and draw those positions in OnDrawGizmos().

I wrote a method which returns the gravity influence for a certain position called computeGravityForPosition(Vector3 position).

And thats how I try to calculate the positions:

private void drawWayPrediction() {
    Vector3 pos = this.transform.position;
    // The offsetVector for the shooting action.
    Vector3 forceVector = pos - Camera.main.ScreenToWorldPoint(Input.mousePosition);
    forceVector.z = 0.0F;

    // The predicted momentum scaled up to increase the strength.
    Vector3 force = (forceVector.normalized * forceVector.magnitude);

    // 1. I guess that this is wrong, but don't know how to do it properly.
    momentum = this.rigidbody.velocity + force;

    for(int i = 0; i < predictionPoints.Length; i++) {
        float t = i * Time.fixedDeltaTime;
        momentum += computeGravityForPosition(pos);
        pos += momentum * t * t;
        predictionPoints[i] = pos;
    }
}

At the beginning, when the objects just slowly approaching each other it looks okay. After the first shot, the prediction is completely wrong. I guess it is because of 1. in the code. Just adding the force to the velocity is probably horrible wrong.

Thank you very much for your time.

EDIT:

I removed seemingly unnessecary parts.

I still think that the main problem lays in 1. in the code. I just don't know how to mix up the current movement of the object (from which I only have the current velocity as far as I know the physics engine of unity) with the new created force:

Vector3 forceVector = pos - Camera.main.ScreenToWorldPoint(Input.mousePosition); Vector3 force = (forceVector.normalized * forceVector.magnitude);

2

2 Answers

3
votes

So if you are using a new version of unity probably above 2018, you can use the nice method

Physics.Simulate(dt); // delta time, dt, is the amount of time to simulate.

By using this function you can manually advance the simulation. This method should be applied to a different physics scene. Therefore I suggest that when you click you will simulate a few physics steps (the more you will simulate the more accurate indication the player will get), with every step you store the position of the object and when you are done simulating draw a line between all the points. In my opinion, it should run quite fast if done correctly.

The code should look something like this:

public PhysicsScene physicsScene;

GameObject actualBall;
GameObject simulatedBall;

OnClick() {
    simulatedBall.SetPosition(actualBall.transform.position);

    if (!physicsScene.IsValid())
        return; // do nothing if the physics Scene is not valid.


    for (int i=0; i < 10; i++) {
        physicsScene.Simulate(Time.fixedDeltaTime);
        // store the position.
        myPoints.append(simulatedBall.rb.position);
    }

    // draw a line from the stored points.

} 

In addition there is this video that I hope will help, good luck https://www.youtube.com/watch?v=GLu1T5Y2SSc

I hope I answered your question and if not tell me :)

1
votes

Disclaimer : Unfortunately I suck at math so can't provide any code for the calculations.

Now that the legal stuff is out of the way :)

In my opinion you are looking at this all wrong. What you need is to calculate the curve (path of the objects trajectory) and then simply plot the curve in OnDrawGizmos with a line renderer.

You don't need to simulate the behaviour of the object. Not only is this a LOT faster but it's also simpler in terms of TimeScale shenanigans. By changing the TimeScale you are also affecting the TimeScale of your trajectory simulation which will most likely look and feel weird.

By doing a basic trajectory calculation you will not have this issue.

PS: This link might help.