Sin function : the trigonometric workhorse

When animating, if you need a position changing in a smooth way, like if it was riding the inside of a circle, you may need the Sin function.

the bucket and the balls are moving along sin waves

In the example above, the game Peggle use this method extensively. Here is how this scene works :

Basics first : 2D games use Two-dimensional space like this one :

You can look smart by saying “a bi-dimensional Cartesian coordinate system”.

Two perpendicular axes, usualy labeled “x” and “y” are used to determine the position of each point in this space.
In the example below, the point in the upper half is at position (3,7).

To move an object, we need to change its position over time. Look at this graph function :

Its still a two-dimensional space, but the difference is it use time for the horizontal axis. By doing this, if we relate y to time, we can visualize the y value changing over time.

Below, the value of y is now the double of the elapsed time :

    private void FixedUpdate()
    {
        Vector2 newPosition = new Vector2();
        newPosition.y = Time.time * 2;

        transform.position = newPosition;
    }

For the y value, the result of this function on a graph is a linear movement:

Result if we apply this on an object in Unity :

The object go up for ever. But it can do back and forth movement with this little hack :

    void Update()
    {
        float y = Mathf.PingPong(Time.time, 5);
        Vector3 newPosition = new Vector3(0, y, 0);

        transform.position = newPosition;
    }

Result in Unity :

Here the problem is exposed. The behavior is not smooth, the object always has the same speed when it changes direction. If we want to reproduce the bucket’s nice movement in peggle, we need the Sin function.
The Sin function is one of the trigonometric functions that are used to relate angles and lengths of right-angle triangles. Only 3 of the trigonometric functions are wildly used in mathematics (Sin, Cos, Tan), and Sin is the most used in the mechanics of video games animations.
These functions simply return the ratio between one side of a right-angle triangle over one other side.

Here below is a unit circle, a circle with a radius of one unit, in which all the possibles shapes of a right-angle triangle can be drawn.

In this circle, the Sin function simply return a normalized ratio between one side of the triangle over one other side.
Here is how to use the Sin function with code and Unity :

 float ratio = Mathf.Sin(angle);

This will return a value between -1 and 1. You just need to provide an angle (look at the center of the unit circle) and it returns a ratio. The angle can be over 360°, it will simply loop. With your smartphone’s calculator, the Sin function certainly works with angles in degrees. In unity, the Sin function works with angle in radians. They are more mathematically corrects.
One radian corresponds to an angle of 57.3 degrees. In a circle it gives an arc-length equals to the radius.

The object can move with the Sin function, by plugging in elapsed time as parameter as if it was an increasing angle :

    void Update()
    {
        Vector3 newPosition = new Vector3();

        newPosition.y = Mathf.Sin(Time.time);
        transform.position = newPosition;
    }

On the object in Unity :

The object is moving smoothly back and forth on its y axis. It return to the same location every 3.14 seconds. Why ? Look again at the Unit circle above, the Sin return 0 at every 180°, or every 3.14 radian. It’s a half turn. This behaviour is highlighted in this graph function :

This is a Sin wave. A period is corresponding to the time needed to loop the repeating shape of the wave :

A complete circle represent an angle of 2pi radian, so here the period is 6.28 seconds.
To normalize the period to one second, the radian parameter need to be 6.28 when one second is elapsed. So just do :

    void Update()
    {
        Vector3 newPosition = new Vector3();
        float angleInRadian = Time.time / period;
        newPosition.y = Mathf.Sin(Time.time * Mathf.PI * 2);
        transform.position = newPosition;
    }

Result on the graph :

In Unity :

Working with length of one unit is a good start. Just scale it by whatever you need. If we want the object moving on a amplitude of five units, just multiply the result of Sin by 5 :

    void Update()
    {
        Vector3 newPosition = new Vector3();
        float amplitude = 5;
        newPosition.y = Mathf.Sin(Time.time * Mathf.PI * 2) * amplitude;
        transform.position = newPosition;
    }

On graph :

In unity :

The bucket in peggle moves on the horizontal axis. Just swap x and y.

    void Update()
    {
        Vector3 newPosition = new Vector3();
        float amplitude = 5;
        newPosition.x = Mathf.Sin(Time.time * Mathf.PI * 2) * amplitude;
        transform.position = newPosition;
    }

Result in Unity :

It’s still to fast for a bucket. The desired period is something more like 4 seconds. It can be obtened by divided time by the desired period :

    void Update()
    {
        Vector3 newPosition = new Vector3();
        float period = 4;
        float angleInRadian = Time.time / period;
        float amplitude = 5;
        newPosition.x = Mathf.Sin(angleInRadian * Mathf.PI * 2) * amplitude;
        transform.position = newPosition;
    }

Result in Unity :

In the game, the bucket is not moving in the middle of the screen, but slighly below. The position on the y axis need to be lowered  :

    void Update()
    {
        Vector3 newPosition = new Vector3();
        float period = 4;
        float angleInRadian = Time.time / period;
        float amplitude = 5;
        float offset = -4;
        newPosition.x = Mathf.Sin(angleInRadian * Mathf.PI * 2) * amplitude;
        newPosition.y += offset;
        transform.position = newPosition;
    }

Result in Unity :

The bucket’s animation is completed.

Now the balls in the “infinite” symbol. They are moving with one Sin waves for each axis : x and y. The y period is the double of the x period, and the y amplitude is the half of the x amplitude.
Let’s start with one ball :

    void Update()
    {
        Vector2 newPosition = new Vector2();
        float periodX = 13;
        float amplitude = 5;
        float radian = Time.time / periodX;
        newPosition.x = Mathf.Sin(radian * Mathf.PI * 2) * amplitude;
        newPosition.y = Mathf.Sin(radian * 2 * Mathf.PI * 2) * amplitude / 2;

        transform.position = newPosition;
    }

Result in Unity :

Now we need to instanciate multiple balls, then create an offset equal to the period divided by the number of balls. In a Sin wave, this offset is called a “phase shift” :

using UnityEngine;

public class Balls : MonoBehaviour
{
    public GameObject prefabBall;
    int ballsQuantity = 5;
    float periodX = 13f;
    float amplitude = 5f;
    GameObject[] balls;

    void Start()
    {
        balls = new GameObject[ballsQuantity];
        for (int i = 0; i < ballsQuantity; i++)
        {
            balls[i] = Instantiate(prefabBall);
        }
    }

    void Update()
    {
        for (int i = 0; i < balls.Length; i++)
        {
            Vector2 newPosition = new Vector2();
            float radian = Time.time / periodX;
            float phaseShift = (1f / balls.Length) * i;
            newPosition.x = Mathf.Sin((radian + phaseShift) * Mathf.PI * 2) * amplitude;
            newPosition.y = Mathf.Sin((radian + phaseShift) * 2 * Mathf.PI * 2) * amplitude / 2;

            balls[i].transform.position = newPosition;
        }
    }
}

And the result in Unity :

Final result :

The Sin function is used here to move objects, but you can do more :

  • – rotate objects
  • – smoothing all type of values. (scores, levels difficulty, colors…)
  • – draw circles
  • – displace vertices of a mesh in shaders…