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): void Start() { _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. } void FixedUpdate() { transform.Rotate(_dir * Time.deltaTime * _currentSpeed); Swing(); } private void Swing() { float angle = 0; float target = 0; angle = (transform.rotation.eulerAngles.x > 180) ? transform.rotation.eulerAngles.x - 360 : transform.rotation.eulerAngles.x; target = _rotateUntil.x; if (angle <= target) ChangeDir(); }
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): float MaxAngleDeflection = 30.0f; float SpeedOfPendulum = 1.0f; float angle = MaxAngleDeflection * Mathf.Sin( Time.time * SpeedOfPendulum); 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.
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!
Instead of Time.time, keep your on notion of float time; and then add to it each frame: Code (csharp): 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.
@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!
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
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.
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.
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.
@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.
@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