Search Unity

Simple pendulum movement

Discussion in 'Scripting' started by mrCharli3, Dec 8, 2018.

  1. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    976
    I have a ball at the end of a string, the string is attached to a cube with the following script on it. The ball moves back and forth kind of like a pendulum, but with a constant speed. How can I make it so the speed is more like a pendulum? i.e slow down before changing direction.

    Here is the relevant logik I use, only for the x-axis to keep it short:

    Code (CSharp):
    1.  
    2. void Start()
    3.     {
    4.         _rotateUntil = transform.rotation.eulerAngles * -1; //sets the point of changing direction, i.e if I start at 30 degress, I change dir at -30 degrees.
    5.     }
    6.  
    7. void FixedUpdate()
    8.     {
    9.         transform.Rotate(_dir * Time.deltaTime * _currentSpeed);
    10.         Swing();
    11.     }
    12.  
    13. private void Swing()
    14.     {
    15.         float angle = 0;
    16.         float target = 0;
    17.         angle = (transform.rotation.eulerAngles.x > 180) ? transform.rotation.eulerAngles.x - 360 : transform.rotation.eulerAngles.x;
    18.          target = _rotateUntil.x;
    19.          if (angle <= target)
    20.               ChangeDir();
    21. }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,686
    What you want is simple harmonic motion:

    https://en.wikipedia.org/wiki/Simple_harmonic_motion

    You can trivially get close to this in Unity by simply using the Mathf.Sin() of a steadily advancing variables, rather than pingponging linearly between to limits.

    I would just put a local pivot into the gameObject hierarchy and drive that directly.

    If you wanted that pivot to swing along the +Z axis, this would be the code:

    Code (csharp):
    1. float MaxAngleDeflection = 30.0f;
    2. float SpeedOfPendulum = 1.0f;
    3.  
    4. float angle = MaxAngleDeflection * Mathf.Sin( Time.time * SpeedOfPendulum);
    5. myPivotTransform.localRotation = Quaternion.Euler( 0, 0, angle);
    Speed of 1.0 implies a period of 2 * pi. Speed of Mathf.PI * 2 would be a period of one second.
     
  3. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    976
    I knew there had to be some way that didnt involve over 100 lines of code. This is exactly what I was looking for. You are the best!
     
    Kurt-Dekker likes this.
  4. AvGaz

    AvGaz

    Joined:
    Dec 21, 2012
    Posts:
    82
    Anyway to set the initial angle?
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,686
    Instead of Time.time, keep your on notion of
    float time;
    and then add to it each frame:

    Code (csharp):
    1. time += Time.deltaTime;
    That way you can set its initial time to whatever you want to get the desired angle, and you can use
    Mathf.Asin()
    to back-calculate the desired time for an angle.
     
  6. AvGaz

    AvGaz

    Joined:
    Dec 21, 2012
    Posts:
    82
    Thanks for the quick answer, you're a genius! And I got it to work!
     
    Last edited: Nov 16, 2020
    Kurt-Dekker likes this.
  7. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @AvGaz I'm interested in your solution, could you contact me? thx
     
  8. Maximo123213

    Maximo123213

    Joined:
    Dec 12, 2018
    Posts:
    6
    @Kurt-Dekker Hi! I am currently using your script and it works great, but how can I set the starting angle? It always starts at 0. Thank you!
     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,686
    Please read the post above, post #5
     
  10. ajay_govind

    ajay_govind

    Joined:
    Apr 5, 2020
    Posts:
    1
    Hi mrChali3,

    I'm too looking for a similar solution like yours, exactly the same I could say, simulating a simple pendulum.

    I hope you understood the code snippet of Kurt-Dekker, unfortunately I'm a beginner to Unity and I not able to understand it properly here.

    Can you pls. give me a walkthrough like what all parameters are involved and how to implement it on the code side by manipulating those parameters.

    I've just built a pivot and attached a string with a bob using a hinge joint in the pivot.


    Thanks in advance :)
     
  11. Maverickjw

    Maverickjw

    Joined:
    Oct 21, 2020
    Posts:
    6
    I was using a Hinge Joint at first but with kurt dekker ´s code you wont need it, not even a rigidbody. In this picture you can see i changed the sprite setting of the wire that holds the pendulum, set it to be on Top. The hook and the blade are just childs and follows the movement of its parent.

    As for the code just this:

    [SerializeField] private float maxAngleDeflection = 30.0f;
    [SerializeField] private float pendulumSpeed = 1.0f;

    void FixedUpdate()
    {
    float angle = maxAngleDeflection * Mathf.Sin(Time.time * pendulumSpeed);
    gameObject.transform.localRotation = Quaternion.Euler(0, 0, angle);
    }
    Attach it to the pivot gameobject.
    upload_2022-6-14_12-17-58.png
     
  12. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,450
    You will need a Rigidbody2D if you're using a 2D collider. Never ever change the Transform when using 2D physics. The collider here will be completely recreated each time you modify the Transform because it's a Static collider connected to the implicit static ground-body. I'm not even sure why you're doing the above in the FixedUpdate because that'll give you a low--frequency update.

    You should add a Rigidbody2D set to Kinematic body-type and use MovePosition. That way, you'll also get interpolation (etc) and no collider recreates.
     
    Bunny83 likes this.
  13. colinsauce00

    colinsauce00

    Joined:
    Jul 13, 2021
    Posts:
    9
    In some situations making further code to set an initial angle is not needed, just create a new game object, set your initial angle inside, put all objects you need swinging inside as children (object should be at initial rotation now), create another gameobject that is a parent of all, rotation should be set at 0,0,0, (Nothing should have moved.) use the original script, even if your z rotation is at -10 initial for instance, swinging it back and forth 20 degrees works with that initial using this method because the equation used is just setting the rotation of your parent object which would technically add to the initial rotations as wished.


    So hierarchy would be:

    Game Object 1: Rotation at 0,0,0, has script,
    Game Object 2: Rotation set at initial angle you want
    All your actual objects at whatever rotations and positions you want them at.


    Note your GameObject 1 and 2 should be at the same position and this position should be wherever you want the pivot to be at for your swinging object.
     
  14. ThomasSoto

    ThomasSoto

    Joined:
    Aug 7, 2013
    Posts:
    26
    @Kurt-Dekker thank you for your awesome answers! I'm currently using the code you used and have been expanding it a bit to serve my purpose for a grapple swing similar to metroid prime. What I can't manage to calculate is how to use Mathf.Asin() to back-calculate time from an angle. Could you point me in the right direction?

    At the moment what I'm using is currentAngle/MaxAngleDeflection but that gives me a linear interpolation value instead of using the sine smoothing which isn't ideal.
     
    Last edited: Jul 30, 2023
  15. pepip

    pepip

    Joined:
    Feb 16, 2020
    Posts:
    1
    @ThomasSoto you can just do the algebra!

    Reorganising

    float angle = MaxAngleDeflection * Mathf.Sin( time * SpeedOfPendulum);


    to get time with respect to angle results in

    float time = (Mathf.Asin(angle / MaxAngleDeflection)) / SpeedOfPendulum;


    Then you can insert whatever value of 'angle' you want, and it will return the 'time' you want to use