Crosshair Sway

I am trying to find a way to randomize a camera’s rotation with certain parameters to simulate a crosshair sway while zooming in. I am currently using mathf.sin to keep the sway smooth and randomizing the amplitude every so often. This works fine, however, when I zoom in and start the script, my camera rotation snaps to wherever the curve is during that frame. It also seems that the sin curve never stops even if the script is disabled, so when i zoom in at a random time the rotation snaps to the curves position. I think it’s because I am editing the localRotation manually which makes it snap immediately. Is there anyway to start the curve from the center at 0,0,0 on enabling the script? Or is there a better way to go about smoothly randomizing camera rotation within parameters that I have overlooked?

using UnityEngine;
using System.Collections;

public class Sway : MonoBehaviour {

	public float speed = 0.7f;
	float maxRotationy = 1f;
	float maxRotationx = 1f;
	public float amplitude;
	public bool holdBreath = true;

	void OnEnable()
	{
		//make mathf.sin == transform.rotation.x/.y/.z?
	}

	void Update()
	{
		if (!holdBreath) {
			transform.localRotation = Quaternion.Euler (maxRotationx * Mathf.Sin (Time.time * speed * 2) * amplitude, maxRotationy * Mathf.Sin (Time.time * speed), 0f);

			if (transform.localRotation.eulerAngles.x <= 0.005f && transform.rotation.eulerAngles.x >= -0.005f) {
				amplitude = Random.Range (-0.25f, 0.5f);
			}
		}
	}
}

I also enable/disable the script completely with a “Zoom” button. But, even when the script is disabled the sin wave continues to run so when enabled the transform.rotation snaps to that wave position during that frame.

This is my first time answering a question on Unity Answers, so here goes.

The problem with the ‘sine wave continuing to run’ is that you are passing Time.time into the Mathf.Sin function.

One way to overcome this problem is to reset the camera’s local rotation to look straight forward when holdBreath is true. To make the transition seem smooth, we can set a float ‘fraction’ to multiply by maxRotationx/y, so when fraction==0 the camera is centered, and when fraction==1 it is at maximum sway.

Add these fields below [ public bool holdBreath = true; ]

public float returnTime = 0.5f;
private float fraction = 0f;

Then add these in Update()

    if (holdBreath)
        fraction = Mathf.Clamp(fraction - 1f/returnTime * Time.deltaTime, 0f, 1f);
    else
        fraction = Mathf.Clamp(fraction + 1f/returnTime * Time.deltaTime, 0f, 1f);

Then change the original [ if (!holdBreath) ] to

    if (fraction > 0f)

And then multiply the x and y components by [ fraction ] like so

        transform.localRotation = Quaternion.Euler(maxRotationx * Mathf.Sin(Time.time * speed * 2) * amplitude * fraction, maxRotationy * Mathf.Sin(Time.time * speed) * fraction, 0f);

And if the script is disabled, we need to re-center the camera. Replace OnEnabled() with this

    void OnDisable()
    {
        transform.localRotation = Quaternion.identity;
        fraction = 0f;
    }

The final script should look like this

using UnityEngine;

public class Sway : MonoBehaviour
{
    public float speed = 0.7f;
    float maxRotationy = 1f;
    float maxRotationx = 1f;
    public float amplitude;
    public bool holdBreath = true;
    
    public float returnTime = 0.5f;
    private float fraction = 0f;
    
    void OnDisable()
    {
        transform.localRotation = Quaternion.identity;
        fraction = 0f;
    }

    void Update()
    {
        if (holdBreath)
        {
            fraction = Mathf.Clamp(fraction - 1f/returnTime * Time.deltaTime, 0f, 1f);
        }
        else
        {
            fraction = Mathf.Clamp(fraction + 1f/returnTime * Time.deltaTime, 0f, 1f);
        }

        if (fraction > 0f)
        {
            transform.localRotation = Quaternion.Euler(maxRotationx * Mathf.Sin(Time.time * speed * 2) * amplitude * fraction, maxRotationy * Mathf.Sin(Time.time * speed) * fraction, 0f);

            if (transform.localRotation.eulerAngles.x <= 0.005f && transform.rotation.eulerAngles.x >= -0.005f)
            {
                amplitude = Random.Range(-0.25f, 0.5f);
            }
        }
    }
}

‘fraction’ could be smoothed with an AnimationCurve, but those are the basics.