You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

游戏昼夜循环:时分转弧度的正确实现方案问询

问题描述

我为游戏添加了昼夜循环系统,希望根据当前时间更新阴影效果。此前找到的时分转弧度代码无法正常工作,阴影出现严重抖动。

编辑补充:
找到可用代码后,又出现阴影短暂旋转后卡顿,随后恢复旋转并再次卡顿的问题,疑似是舍入误差导致。除该卡顿问题外,代码其余功能正常。

最初的问题代码:

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()
    {
        
    }
}

解决方案

你的核心问题有两个:

  1. RotateAround使用错误:每次调用都累加旋转角度,而非设置到目标角度,导致分钟更新时叠加旋转量,出现卡顿跳变。
  2. 时间转换精度损耗:用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 17:55:08