游戏昼夜循环:时分转弧度的正确实现方案问询
问题描述
我为游戏添加了昼夜循环系统,希望根据当前时间更新阴影效果。此前找到的时分转弧度代码无法正常工作,阴影出现严重抖动。
编辑补充:
找到可用代码后,又出现阴影短暂旋转后卡顿,随后恢复旋转并再次卡顿的问题,疑似是舍入误差导致。除该卡顿问题外,代码其余功能正常。
最初的问题代码:
public void UpdateWorldSun() { float totalMinutes = ((_currentTime.Hours * 60) + _currentTime.Minutes); float radians = (totalMinutes / 60f) * Mathf.Deg2Rad; worldSun.RotateAround(worldSunPivot.transform.position, new Vector3(0, 0, 1), radians); }
(注:原附卡顿效果GIF因大小限制展示不清,此处省略)
我认为问题出在转换逻辑上,游戏支持动态日长设置,当前测试时一天为15秒,而现有代码未适配该动态时长。请问如何实现适配动态日长的时分转弧度的正确方法?
当前完整代码:
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class WorldTime : MonoBehaviour { public event EventHandler<TimeSpan> WorldTimeChanged; [SerializeField] private float _dayLength; // in seconds private TimeSpan _currentTime; public Transform worldSun; public Transform worldSunPivot; private float _minuteLength => _dayLength / MinutesInADay; public const int MinutesInADay = 1440; private IEnumerator AddMinute() { _currentTime += TimeSpan.FromMinutes(1); WorldTimeChanged?.Invoke(this,_currentTime); UpdateWorldSun(); yield return new WaitForSeconds(_minuteLength); StartCoroutine(AddMinute()); } public void UpdateWorldSun() { float hourAndMinute = _currentTime.Hours + (((_currentTime.Minutes * 100) / 60) / 100); float radians = (((hourAndMinute * 360f) / 24f)) * Mathf.Deg2Rad; worldSun.RotateAround(worldSunPivot.transform.position, new Vector3(0, 0, 1), radians); } // Start is called before the first frame update void Start() { StartCoroutine(AddMinute()); } // Update is called once per frame void Update() { } }
解决方案
你的核心问题有两个:
RotateAround使用错误:每次调用都累加旋转角度,而非设置到目标角度,导致分钟更新时叠加旋转量,出现卡顿跳变。- 时间转换精度损耗:用
TimeSpan时分做整数运算丢失精度,且未结合动态日长的实时进度。
修复后的完整代码:
using System; using System.Collections; using UnityEngine; public class WorldTime : MonoBehaviour { public event EventHandler<TimeSpan> WorldTimeChanged; [SerializeField] private float _dayLength; // 一天的实际时长(秒) private TimeSpan _currentTime; public Transform worldSun; public Transform worldSunPivot; public const int MinutesInADay = 1440; // 计算当前游戏时间占全天的比例 private float GetDayProgress() { float totalSecondsPassed = (float)_currentTime.TotalSeconds; float totalDaySeconds = MinutesInADay * 60f; return totalSecondsPassed / totalDaySeconds; } private IEnumerator AddMinute() { _currentTime += TimeSpan.FromMinutes(1); WorldTimeChanged?.Invoke(this, _currentTime); yield return new WaitForSeconds(_dayLength / MinutesInADay); StartCoroutine(AddMinute()); } private void UpdateWorldSun() { // 根据全天进度计算目标旋转角度(0-360度对应完整一天) float dayProgress = GetDayProgress(); float targetAngle = dayProgress * 360f; // 直接设置旋转角度,避免累加导致的跳变 worldSun.localRotation = Quaternion.Euler(0, targetAngle, 0); // 若需围绕pivot做圆周运动,可替换为以下逻辑(需先设置初始偏移) // worldSun.position = worldSunPivot.position + new Vector3(10, 0, 0); // worldSun.RotateAround(worldSunPivot.position, Vector3.up, targetAngle); } void Start() { StartCoroutine(AddMinute()); } void Update() { // 每一帧实时更新太阳角度,消除卡顿感 UpdateWorldSun(); } }
关键修复点说明:
- 实时刷新角度:将
UpdateWorldSun移到Update中调用,保证每一帧都同步太阳位置,避免分钟间隔带来的卡顿。 - 直接设置目标角度:不再用
RotateAround累加旋转,而是根据全天进度计算目标角度后直接赋值,彻底解决跳变问题。 - 精确进度计算:用
TotalSeconds计算已流逝时间占全天的比例,避免整数运算的精度损耗,完美适配动态日长。 - 简化逻辑:通过进度比例直接映射到360度旋转,无需复杂的时分转弧度计算,逻辑更清晰可控。
内容的提问来源于stack exchange,提问作者Krythic
相关产品推荐
相关产品推荐

