Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Optimizing Physics-Based Circular Motion?

Discussion in 'Physics' started by stackdynamic, Mar 18, 2018.

  1. stackdynamic

    stackdynamic

    Joined:
    Mar 18, 2018
    Posts:
    13
    Hello,

    Currently, I am working on a small project where I require physics-based circular motion (as in, with forces, rigidbodies, etc.) While I have gotten it to work just fine, I really dislike how sloppy/inefficient my code is right now, and would really appreciate anyone good with Unity helping me out a bit. So, here is my current thought process:

    Circular motion requires 1) a centripetal force equal to mv^2/r, and 2) a tangential velocity. For organizational purposes, I split these into two scripts.

    Centripetal Force

    For simplicity's sake, I will define the center of the circle as (0, 0, 0) moving forward, but have left this changeable, as can be seen with "center" in the code:
    Code (CSharp):
    1.  
    2. public class Centrifugal : MonoBehaviour
    3. {
    4.     Rigidbody2D rb; float sqrRadius; public Vector3 center;
    5.     // Use this for initialization
    6.     void Start()
    7.     {
    8.         rb = gameObject.GetComponent<Rigidbody2D>();
    9.     }
    10.  
    11.     // Update is called once per frame
    12.     void FixedUpdate()
    13.     {
    14.         sqrRadius = Vector3.SqrMagnitude(gameObject.transform.position - center);
    15.         rb.AddForce(rb.mass * rb.velocity.sqrMagnitude * -rb.position / sqrRadius);
    16.     }
    17. }
    So, since the magnitude of the force is mv^2/r, I figured that the best way to find the needed force vector would be to multiply the unit vector with the correct direction by that magnitude. The unit vector is equivalent to the object's position vector divided by the radius, multiplied by negative one because the force is towards the center. Since the position vector is just the object's position because the center is at the origin, this is simplified to -rb.position/radius. Multiplying the direction vector by the magnitude gives the result shown in the code. I intentionally set this up so that sqrMagnitude, rather than the more consuming Magnitude function is used every frame, hence this simplification.

    Tangential Velocity

    Having a velocity tangent to the circle is equivalent to having a velocity vector perpendicular to the radius. As such, the unit vector with the corresponding direction is found by taking the negative reciprocal of the unit vector with the same direction as the radius (the position vector), so the horizontal velocity is the vertical position/radius, and the vertical is the negative horizontal position/radius. "Speed" is my defined magnitude.
    Code (CSharp):
    1. public class TangentialVelocity : MonoBehaviour {
    2.     public float speed; Rigidbody2D rb; Vector2 velocity;
    3.     // Use this for initialization
    4.     void Start () {
    5.         rb = gameObject.GetComponent<Rigidbody2D>();
    6.         CalibrateVelocity();
    7.     }
    8.    public void CalibrateVelocity()
    9.     {
    10.         velocity = rb.velocity;
    11.         velocity.x = gameObject.transform.position.y / gameObject.transform.position.magnitude * speed;
    12.         velocity.y = -gameObject.transform.position.x / gameObject.transform.position.magnitude * speed;
    13.         rb.velocity = velocity;
    14.     }
    15. }
    I don't just have the code in "CalibrateVelocity" for no reason, the project I am working on has the object "teleport" to new positions, and it still needs to move in a circle once teleported. The function is called at Start and whenever it teleports because of this.

    To clarify a few things:
    1. I am aware I could just make the object a child of an empty GameObject at the origin with a rotational velocity. Due to some other interactions and things in my scene, I need this to be physics based.
    2. The motion needs to always be circular, even when it abruptly changes position, and the magnitude of the velocity has to be the same.
    3. This currently works fine, it's just not efficient. The problem isn't getting it to work, it's getting it to work better.
    TL;DR: My code is obviously awful, and I would appreciate any help fixing it.

    This is also my first time posting, so if I am doing anything wrong, I would appreciate anyone pointing it out. Thanks for reading, and sorry for the kinda long post.
     
    Last edited: Mar 18, 2018
    Deeeds likes this.
  2. stackdynamic

    stackdynamic

    Joined:
    Mar 18, 2018
    Posts:
    13
    Something weird I also found: if I call CalibrateVelocity() in FIxedUpdate, with a higher speed or lower radius, the object eventually flings out of the circle and oscillates between two spiraling positions rapidly, kind of "flickering" between two circles of a differing radius. I'm assuming this is a problem with how Unity does physics calculations since it works fine with a lower speed or bigger radius. Would anyone know how to solve this? I've tried tampering with the Physics2D settings already, namely velocity iterations, but I still get the same problem. Thanks!
     
  3. Deeeds

    Deeeds

    Joined:
    Mar 15, 2018
    Posts:
    739
    Have you seen
    Code (csharp):
    1.  LateUpdate()
    ?

    You might be able to use this to check for unwanted activity and make corrections.