I tried to make a script to have the sun in my scene travel through the sky, and I the sun wouldn’t move, nor will the light change. What am I doing wrong?
using UnityEngine;
using System.Collections;
public class SunSpinner : MonoBehaviour {
// How many second between day/night ticks (3600 per day-night cycle)
[SerializeField] private float daySpeed = 0.05f;
// Days that have passed
[SerializeField] private float daysPassed = 0f;
// Holds the sun's transform
[SerializeField] private Transform sunTransform;
// When was day/night last updated
private float lastTimeTick = 0f;
// Complete rotation of the sun
private Quaternion solarRotation = new Quaternion(90f, 0f, 0f, 1f);
// Update is called once per frame
void Update () {
if (Time.realtimeSinceStartup - lastTimeTick >= daySpeed) {
// Rotates the sun by 0.01 degrees
solarRotation.x = (solarRotation.y + 0.1f);
//Sees if a day has passed
if (solarRotation.x >= 360f) {
solarRotation.x = 0f;
daysPassed ++;
}
//Rotate the sun very slightly on the X axis
solarRotation.z = solarRotation.x + 0.00001f;
// Prepares for next rotation and updates the game object
sunTransform.rotation = solarRotation;
lastTimeTick = Time.realtimeSinceStartup;
}
}
public void Reset () {
lastTimeTick = Time.realtimeSinceStartup;
}
}
As @LazyElephant stated, the problem is misuse of Quaternions, but what may seem like the simplest solution does not actually function as easily as it could. Euler angles may seem correct at first, but the way Unity handles them comes second to directly-Quaternion-based functions.
One possible way of accounting for this is, rather than using the sun’s position to determine the time, use the time to determine the sun’s position. For instance:
// C#
[SerializeField] private float dayCycleTime = 180.0f; // 3600 * 0.05
[SerializeField] private float rotationAngleOffset = -90; // Starting rotation from (0, 0, 0), where a time of "0" is midnight, offset rotation by 90 degrees
[SerializeField] private Vector3 sunRotationAxis = Vector3.forward; // Rise in the east, set in the west
private float sunSpeedScale; // 360 degree rotation occurs in "dayCycleTime" seconds.
// This scale will accommodate this using the formula (360 / dayCycleTime)
void Start()
{
// Correct for dayCycleTime <= 0
if(dayCycleTime <= 0)
{
dayCycleTime = 0.0001f;
}
sunSpeedScale = 360.0f / dayCycleTime;
}
void Update()
{
// use of Time.realtimeSinceStartup may be replaced in order to choose when time starts, or to be able to pause time as desired
sunTransform.rotation = Quaternion.AngleAxis((Time.realtimeSinceStartup * sunSpeedScale) + rotationAngleOffset, sunRotationAxis);
}
This version should not find itself caught in the data-type conversion between euler angles and Quaternion rotations which prevent high values from being utilized.
Long story short, changing the x, y, z, w variables in the quaternion doesn’t do what I think you were expecting. Changing those variables directly generally isn’t a good idea unless you really know the math behind the scenes, because it’s not the same as simply adjusting the x/y/z rotations. You can make your script work by changing your solarRotation variable from a Quaternion
to a Vector3
and by changing line 33 to sunTransform.rotation = Quaternion.Euler(solarRotation);