当前位置: 首页 > news >正文

Unity Time类深度解析:帧率无关运动、暂停与时间缩放实战指南

1. 项目概述:为什么Unity开发者必须吃透Time类?

如果你刚开始接触Unity,或者已经写过一些简单的移动、旋转脚本,那你肯定用过Time.deltaTime。但你可能也遇到过一些“灵异事件”:为什么我的物体移动时快时慢?为什么物理模拟在某些设备上会抽搐?为什么暂停游戏后,UI动画还在动?这些问题的根源,十有八九都出在对Unity的Time类理解不透彻上。

Time类远不止一个简单的计时器。它是Unity游戏世界的心脏,负责协调游戏逻辑、物理模拟、动画更新与真实世界时间流逝之间的关系。理解它,是写出稳定、流畅、跨平台表现一致的游戏代码的基石。今天,我们就抛开官方文档那冰冷的列表,从一个一线开发者的视角,彻底拆解Time类的每一个核心属性,并结合那些热搜词里提到的“坑”(比如性能优化、面试题、打包问题),告诉你它们在实际项目中到底怎么用,以及怎么避坑。

2. Time类核心属性深度解析与实战场景

Unity的Time类提供了近20个静态属性,乍一看令人眼花缭乱。我们可以把它们分为几个核心家族来理解:增量时间族累计时间族时间缩放族特殊用途族。记住这个分类,学习起来会清晰很多。

2.1 增量时间族:驱动每帧运动的核心

增量时间,顾名思义,就是上一帧到这一帧所经过的时间。这是实现与帧率无关的平滑运动的关键。

1. Time.deltaTime: 最常用,也最容易用错这是你实现物体移动、旋转、插值等操作时最应该使用的属性。它的值是上一帧完成到当前帧开始所经过的秒数。

// 标准用法:每秒移动10个单位 transform.Translate(Vector3.forward * 10f * Time.deltaTime);

为什么必须乘以Time.deltaTime假设你的游戏在60FPS的机器上运行,deltaTime大约是0.0167秒。在30FPS的机器上,deltaTime大约是0.0333秒。通过乘以deltaTime,你实际上是在说“每秒钟移动10个单位”。那么:

  • 在60FPS下,每帧移动10 * 0.0167 ≈ 0.167个单位。
  • 在30FPS下,每帧移动10 * 0.0333 ≈ 0.333个单位。 虽然单帧移动距离不同,但经过一秒(60帧或30帧)后,总移动距离都是10个单位。这就实现了帧率无关的运动。如果不乘,物体在高速设备上会移动得飞快,在低速设备上则慢如蜗牛。

实操心得:在Update()方法中进行任何与位置、旋转、进度(Lerp的t值)相关的计算,99%的情况都应该使用Time.deltaTime。这是新手和老手最显著的区别之一。

2. Time.unscaledDeltaTime: 不受时间缩放影响的“真实”增量这个属性是deltaTime的“老实人”兄弟。它不受Time.timeScale的影响,总是返回基于真实时间的帧间隔。

// 即使游戏暂停(timeScale = 0),这个计时器也会正常累加 float pauseMenuTimer += Time.unscaledDeltaTime;

典型应用场景

  • 游戏暂停菜单的动画:即使游戏世界暂停了,菜单的弹出、按钮的高亮动画仍需流畅播放。
  • UI特效:比如一个不受游戏内“子弹时间”影响的UI粒子效果。
  • 网络心跳检测:需要基于真实时间判断超时,而不是游戏内变慢的时间。

3. Time.smoothDeltaTime: 平滑但滞后的增量Unity会计算一个过去几帧deltaTime的平均值,使其更加平滑。这能减少因单帧卡顿导致的运动抖动。但请注意,平滑意味着延迟。它反映的不是最新鲜的时间间隔,而是一个历史平均值。

// 适用于对瞬时波动不敏感,但要求极度平滑的运动,如摄像机跟随 cameraFollowPosition = Vector3.Lerp(cameraFollowPosition, targetPosition, smoothSpeed * Time.smoothDeltaTime);

使用建议:在摄像机跟随、某些平滑的UI移动中可以考虑使用。但对于需要快速响应的玩家控制角色移动,使用标准的deltaTime更合适。

4. Time.fixedDeltaTime: 物理世界的固定心跳这是物理更新(FixedUpdate)的固定时间步长,默认是0.02秒(即50Hz)。FixedUpdate的调用频率与帧率无关,Unity会根据需要在一个渲染帧内调用多次FixedUpdate来“追赶”真实时间。

// 在FixedUpdate中,使用fixedDeltaTime(虽然在此方法内deltaTime的值也等于fixedDeltaTime) void FixedUpdate() { rigidbody.AddForce(Vector3.up * 10f * Time.fixedDeltaTime); }

核心要理解FixedUpdate是物理模拟的圣地。所有对Rigidbody的操作、物理力的施加,都应放在这里,并使用fixedDeltaTimeTime.deltaTime(在FixedUpdate内两者等价)。如果你在Update里修改RigidbodyvelocityAddForce,可能会引发不可预知的物理抖动,这也是很多“物理抽搐”问题的根源。

5. Time.maximumDeltaTime: 应对卡顿的“安全阀”这是一个非常重要的保护性参数。当某一帧因为复杂计算、加载等原因耗时极长(比如deltaTime达到了0.5秒),如果把这个巨大的值直接用于物理和逻辑计算,可能会导致物体“穿越”墙壁、动画瞬间跳变等灾难性后果。maximumDeltaTime(默认0.333秒)就是deltaTime的上限。当一帧耗时超过此值,Unity会将其钳制在这个最大值内。超出的时间会被“丢弃”吗?不,物理引擎会通过增加单帧内FixedUpdate的调用次数来尝试弥补,但这可能导致CPU峰值。这也是为什么性能优化(热搜词:unity性能优化)如此重要——避免触发这个安全阀,保持游戏体验的连贯性。

2.2 累计时间族:游戏世界的时钟

这类属性告诉你游戏已经运行了多久。

1. Time.time: 游戏世界的标准时间从游戏开始到现在所经过的秒数,Time.timeScale影响。当timeScale为0(暂停)时,Time.time停止增长;当timeScale为2时,它流逝的速度是真实时间的两倍。

// 计时3秒后触发事件 if (Time.time - startTime > 3f) { DoSomething(); }

2. Time.unscaledTime: 墙上的挂钟从游戏开始到现在所经过的真实秒数,完全不受Time.timeScale影响。即使游戏暂停,它也在默默计时。

// 记录玩家在游戏中实际度过了多少时间(包括暂停时间) totalRealPlayTime = Time.unscaledTime;

3. Time.realtimeSinceStartup: 最“真实”的时钟这个属性与Time.unscaledTime类似,但有一个关键区别:它计算的是从Unity播放器启动(或应用启动)到现在所经过的真实秒数,即使在编辑器暂停时也会继续增加。而Time.timeTime.unscaledTime在编辑器暂停时会停止。

// 测量一段代码执行的精确真实耗时 float start = Time.realtimeSinceStartup; PerformComplexCalculation(); float duration = Time.realtimeSinceStartup - start; Debug.Log($"计算耗时:{duration} 秒");

注意事项realtimeSinceStartup在游戏刚开始的几帧可能不稳定,且其值在应用长时间运行后可能会非常大。通常用于短时间测量,而非长时间计时。

4. Time.fixedTime 与 Time.timeSinceLevelLoad

  • Time.fixedTime: 最后一次FixedUpdate开始时的Time.time值。在FixedUpdate中使用它,可以获取与物理步调一致的时间戳。
  • Time.timeSinceLevelLoad: 当前关卡加载完成后所经过的时间(受timeScale影响)。用于关卡内的计时非常方便。

2.3 时间缩放族:掌控游戏节奏的魔法棒

Time.timeScale: 游戏世界的变速齿轮这是Time类中最强大、也最危险的属性之一。它缩放的是Time.timeTime.deltaTime的流逝速度。

  • timeScale = 1.0f: 正常速度。
  • timeScale = 0.0f: 完全暂停。Time.time停止,Time.deltaTime为0。所有基于deltaTime的运动和动画都会停止。UpdateFixedUpdate仍然会每帧调用!只是deltaTime为0。
  • timeScale = 0.5f: 慢动作,速度减半。
  • timeScale = 2.0f: 快进,速度加倍。

实现“子弹时间”效果:

public void EnterBulletTime() { Time.timeScale = 0.3f; // 世界变慢 // 同时,可能需要提高FixedUpdate的频率,让物理模拟在慢速下依然平滑 Time.fixedDeltaTime = 0.02f * Time.timeScale; // 保持物理迭代次数 }

重大陷阱与解决方案

  1. UI动画暂停:当你设置Time.timeScale = 0暂停游戏时,所有使用Time.deltaTime的UI动画也会停止。解决方案是UI动画使用Time.unscaledDeltaTime
  2. 协程(Coroutine)中的 WaitForSecondsWaitForSecondstimeScale影响。如果你在暂停时启动一个WaitForSeconds(2)的协程,它会一直等待。需要使用WaitForSecondsRealtime
  3. 物理模拟异常:大幅改变timeScale可能导致物理不稳定。一个最佳实践是同时按比例调整Time.fixedDeltaTime,以保持物理更新的实际频率相对稳定。

2.4 特殊用途与高级属性

1. Time.frameCount: 帧号从游戏开始到现在渲染的总帧数。常用于需要每N帧执行一次的操作,性能开销远小于基于时间的判断。

// 每10帧执行一次昂贵的操作,而不是每0.1秒 if (Time.frameCount % 10 == 0) { UpdateExpensiveDebugInfo(); }

2. Time.captureDeltaTime / captureFramerate: 制作平滑慢镜头或定格动画这两个属性用于手动控制时间,通常用于录制固定帧率的视频或制作“矩阵”式慢镜头。

// 设置以每秒60帧的速度“模拟”时间,用于录制 Time.captureFramerate = 60; // 此时,Time.deltaTime 会被锁定为 1/60 ≈ 0.0167 // Update和FixedUpdate仍被调用,但游戏时间被精确控制。

注意:设置此属性后,Time.deltaTime将变得固定,Time.timeScale失效。完成录制后务必将其设回0。

3. Time.inFixedTimeStep: 判断当前是否在物理更新中这是一个只读属性,在FixedUpdate及其调用的函数中返回true,在Update中返回false。这在一些共享工具函数中非常有用,可以判断当前上下文并选择合适的时间增量。

public float GetAppropriateDeltaTime() { return Time.inFixedTimeStep ? Time.fixedDeltaTime : Time.deltaTime; }

3. 核心应用场景与实战代码剖析

理解了属性,我们来看看如何把它们组合起来,解决实际开发中的经典问题。

3.1 场景一:实现一个稳健的游戏计时器

游戏内计时器(如技能CD、倒计时)需要能在游戏暂停时停止,在慢动作时变慢。

public class GameTimer : MonoBehaviour { private float _duration; // 总时长 private float _remainingTime; // 剩余时间 private bool _isRunning; public void StartTimer(float duration) { _duration = duration; _remainingTime = duration; _isRunning = true; } void Update() { if (!_isRunning) return; // 使用受timeScale影响的deltaTime,这样计时器会随游戏世界一起暂停或变慢 _remainingTime -= Time.deltaTime; if (_remainingTime <= 0f) { _remainingTime = 0f; _isRunning = false; OnTimerComplete?.Invoke(); } } // 获取剩余时间的百分比(0到1),用于UI进度条 public float GetNormalizedTime() { return Mathf.Clamp01(_remainingTime / _duration); } }

如果要一个在游戏暂停时也能走的“真实世界”计时器(比如网络连接超时),只需把Time.deltaTime换成Time.unscaledDeltaTime

3.2 场景二:制作一个暂停菜单,并让菜单动画不受暂停影响

这是面试常考题,也是实战高频需求。

public class PauseMenuManager : MonoBehaviour { public GameObject pauseMenuPanel; public Animator menuAnimator; // 菜单自身的Animator void Update() { if (Input.GetKeyDown(KeyCode.Escape)) { TogglePause(); } } public void TogglePause() { bool isPausing = Time.timeScale > 0f; if (isPausing) { // 暂停游戏世界 Time.timeScale = 0f; // 显示菜单 pauseMenuPanel.SetActive(true); // 播放菜单入场动画。Animator默认使用Unscaled Time,所以动画不受影响。 // 如果Animator被误设置为Scaled Time,需在Animator组件或代码中设置: // menuAnimator.updateMode = AnimatorUpdateMode.UnscaledTime; menuAnimator.Play("MenuFadeIn"); } else { // 恢复游戏世界 Time.timeScale = 1f; // 播放菜单退场动画 menuAnimator.Play("MenuFadeOut"); // 动画播放完毕后,在动画事件中禁用面板,或使用协程等待 StartCoroutine(DisableMenuAfterAnimation(0.5f)); } } IEnumerator DisableMenuAfterAnimation(float delay) { // 使用WaitForSecondsRealtime,因为timeScale=0时WaitForSeconds会永远等待 yield return new WaitForSecondsRealtime(delay); pauseMenuPanel.SetActive(false); } }

3.3 场景三:平滑摄像机跟随与帧率无关的动画

结合deltaTimesmoothDeltaTime,实现高级效果。

public class SmoothCameraFollow : MonoBehaviour { public Transform target; public float followSpeed = 5f; public float rotationSpeed = 3f; public bool useSmoothDelta = false; private Vector3 _velocity = Vector3.zero; void LateUpdate() { if (target == null) return; // 位置跟随:使用SmoothDamp,它内部已经处理了时间。我们只需传入正确的deltaTime。 float delta = useSmoothDelta ? Time.smoothDeltaTime : Time.deltaTime; transform.position = Vector3.SmoothDamp(transform.position, target.position, ref _velocity, 0.3f, Mathf.Infinity, delta); // 旋转跟随:使用Lerp,需要手动乘以deltaTime Quaternion targetRotation = Quaternion.LookRotation(target.position - transform.position); transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * delta); } }

何时用smoothDeltaTime对于摄像机这种对瞬时卡顿敏感,追求丝滑而非即时响应的对象,使用smoothDeltaTime可以有效消除因单帧耗时波动带来的轻微抖动。但对于玩家角色控制,必须用deltaTime以保证操作的跟手性。

4. 性能优化、疑难杂症与面试题精讲

很多热搜词反映的问题,都与Time类的误用或深层机制有关。

4.1 性能优化:Time.maximumDeltaTime 与 Time.maximumParticleDeltaTime

  • Time.maximumDeltaTime: 如前所述,这是最后防线。但优化目标是不让它生效。你需要通过性能剖析器(Profiler)找到卡顿元凶:可能是复杂的AI计算、过多的DrawCall、同步加载大型资源、复杂的物理模拟(热搜词:simulation is not running in real time due to excessive cpu)等。针对性地优化,让每一帧的deltaTime都远低于这个阈值(比如保持在0.0167ms以下以实现60FPS)。

  • Time.maximumParticleDeltaTime: 这是粒子系统的专用“安全阀”。当一帧时间过长时,Unity会将粒子更新拆分成多个子步骤,避免因单次更新量过大导致粒子“爆炸”或位置异常。通常不需要手动修改,除非你遇到非常特殊的粒子表现问题。

4.2 疑难杂症排查清单

问题1:物体移动一卡一卡的,尤其在低帧率设备上。

  • 可能原因1:在Update中直接修改Rigidbody.positionTransform.position,与物理引擎的FixedUpdate更新冲突。解决方案:对物理对象,所有位置、力的改变都应在FixedUpdate中进行,并使用Rigidbody.MovePositionAddForce
  • 可能原因2:运动计算没有乘以Time.deltaTime,导致帧率直接影响速度。
  • 可能原因3:使用了Time.deltaTime,但运动计算本身放在了一个帧率不稳定的地方(例如,在协程中每帧yield return null但进行了复杂计算,导致等待时间不稳定)。确保运动逻辑在稳定的Update循环中。

问题2:游戏暂停后,某些特效或UI元素也停了。

  • 排查:检查这些动画或脚本是否使用了Time.deltaTime或受timeScale影响的Time.time。将其替换为Time.unscaledDeltaTimeTime.unscaledTime
  • 对于Animator:检查组件上的Update Mode,设置为Unscaled Time

问题3:网络同步或日志记录的时间戳对不上。

  • 排查:你是否错误地使用了Time.timeTime.time会受timeScale和游戏启动时机影响。对于需要和服务器或真实世界对齐的时间戳,应使用Time.realtimeSinceStartupSystem.DateTime.UtcNow

问题4:从AssetStore下载的插件在Time.timeScale = 0时行为异常。

  • 原因:插件内部可能硬编码了Time.deltaTime解决方案:查看插件源码(如果有),或联系作者。有时可以通过在暂停时禁用该插件组件来规避。

4.3 高频面试题深度剖析

面试题1:Time.deltaTimeTime.fixedDeltaTime有什么区别?分别在什么情况下使用?

  • 区别
    • Time.deltaTime:上一帧到当前帧的真实时间间隔,在Update中使用,值每帧变化。
    • Time.fixedDeltaTime:固定的物理更新时间间隔,在FixedUpdate中使用,值恒定(默认0.02s)。
  • 使用场景
    • Time.deltaTime:所有与渲染帧同步的逻辑,如玩家输入处理、非物理动画、游戏状态更新、UI动画(若需受timeScale影响)。
    • Time.fixedDeltaTime:所有与物理模拟相关的逻辑,如力的施加、速度的改变、物理关节的控制。在FixedUpdate方法内,Time.deltaTime的值就等于Time.fixedDeltaTime

面试题2:如何实现游戏的暂停功能?需要注意哪些坑?

  • 核心实现:设置Time.timeScale = 0
  • 需要注意的坑
    1. UI与菜单:暂停菜单的动画需使用Unscaled Time模式或Time.unscaledDeltaTime
    2. 协程:暂停期间启动的、基于WaitForSeconds的协程会卡住,需改用WaitForSecondsRealtime
    3. 音效:背景音乐可能也需要暂停。AudioSourcePause()方法,但注意有些UI音效可能不需要暂停。
    4. 粒子系统:检查粒子系统的Simulation Speed是否受timeScale影响,可能需要单独处理。
    5. 输入:暂停后,通常需要屏蔽或重定向玩家对游戏世界的输入,但保留对菜单的输入。

面试题3:Time.realtimeSinceStartupTime.unscaledTime有什么不同?

  • Time.unscaledTime:游戏启动后,不受timeScale影响的游戏时间。在编辑器暂停时会停止
  • Time.realtimeSinceStartup:应用启动后,绝对的、真实的挂钟时间。在编辑器暂停时也不会停止。它基于系统时间,更适用于性能测量和需要与外部真实时间对齐的场景。

面试题4:解释一下Time.captureFramerate的作用。

  • 作用:用于离线渲染录制固定帧率视频。设置后,Unity会尝试以固定帧率运行游戏,每帧的Time.deltaTime被锁定为1/captureFramerateTime.timeScale失效。
  • 原理:Unity通过在两帧之间插入精确的等待来实现固定帧间隔。
  • 注意:这会导致游戏运行速度变慢(因为要等待),仅用于录制目的,正常游戏运行时必须将其设为0。

5. 进阶话题:Time与Unity其他系统的协作

Time类不是孤立的,它与Unity的多个核心系统紧密耦合。

5.1 Time与物理系统(Physics)

这是最深度的耦合。FixedUpdate的调用频率由Time.fixedDeltaTime决定。Unity内部有一个“时间累积器”,记录自上一帧以来过去的时间。每一帧,累积器增加Time.deltaTime。只要累积器大于等于fixedDeltaTime,Unity就调用一次FixedUpdate,并从累积器中减去一个fixedDeltaTime。这个过程会重复,直到累积器小于fixedDeltaTime。这确保了物理模拟的稳定性,不受渲染帧率波动的影响。

这就是为什么在FixedUpdate中执行物理操作是至关重要的。如果你在Update中频繁修改刚体的速度或位置,你是在以不可预测的帧率干扰一个以固定频率运行的系统,必然导致抖动和不可靠的物理行为。

5.2 Time与动画系统(Animation/Animator)

  • Legacy Animation:动画播放速度默认受Time.timeScale影响。
  • Animator:通过Animator.updateMode控制:
    • Normal:受Time.timeScale影响。
    • AnimatePhysics:与物理更新同步(在FixedUpdate后更新),受Time.timeScale影响。
    • UnscaledTime:不受Time.timeScale影响,使用Time.unscaledDeltaTime。 制作暂停菜单时,将菜单Animator设为UnscaledTime是标准做法。

5.3 Time与粒子系统(Particle System)

粒子系统的Simulation Speed属性缩放的是粒子自身的模拟时间。它默认会受到Time.timeScale的乘数影响。这意味着当timeScale = 0时,粒子也会暂停。如果你希望游戏暂停时某些背景粒子(如星空、氛围尘埃)继续运动,需要单独设置这些粒子系统的Simulation Speed,或使用脚本控制。

5.4 Time与协程(Coroutines)

这是最容易踩坑的地方。

  • yield return new WaitForSeconds(2);: 等待2秒的游戏时间,受Time.timeScale影响。
  • yield return new WaitForSecondsRealtime(2);: 等待2秒的真实时间,不受Time.timeScale影响。
  • yield return null;/yield return 0;: 等待下一帧。恢复执行的时间点取决于timeScale。即使游戏暂停(timeScale=0),协程也会在下一帧被唤醒,只是Time.deltaTime为0。
  • yield return new WaitForFixedUpdate();: 等待下一次FixedUpdate调用。

关键点:在实现与时间相关的延迟、计时功能时,务必根据需求选择正确的等待指令。游戏逻辑计时用WaitForSeconds,真实世界计时(如广告倒计时、网络超时)用WaitForSecondsRealtime

6. 实战框架:构建一个高鲁棒性的时间管理模块

对于中型以上项目,我强烈建议抽象一个时间管理模块(如TimeManager),而不是在代码中到处直接调用Time.timeScale。这提供了更好的控制力和可维护性。

using System; using UnityEngine; public class TimeManager : MonoBehaviour { public static TimeManager Instance { get; private set; } // 全局时间缩放 public float GlobalTimeScale { get => Time.timeScale; set { Time.timeScale = value; OnGlobalTimeScaleChanged?.Invoke(value); } } // 局部时间缩放(例如,仅为某个角色或特效设置慢动作) private Dictionary<object, float> _localTimeScales = new Dictionary<object, float>(); private float _effectiveTimeScale = 1f; public event Action<float> OnGlobalTimeScaleChanged; void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); } // 设置局部时间缩放 public void SetLocalTimeScale(object key, float scale) { _localTimeScales[key] = scale; UpdateEffectiveTimeScale(); } public void RemoveLocalTimeScale(object key) { if (_localTimeScales.Remove(key)) { UpdateEffectiveTimeScale(); } } private void UpdateEffectiveTimeScale() { float product = 1f; foreach (var scale in _localTimeScales.Values) { product *= scale; } _effectiveTimeScale = product; // 这里可以驱动一个自定义的更新循环,用于受局部时间缩放影响的逻辑 // 例如:CustomUpdate(_effectiveTimeScale * Time.unscaledDeltaTime); } // 获取适用于特定上下文的时间增量(考虑了局部缩放) public float GetDeltaTime(object context = null) { float baseDelta = Time.inFixedTimeStep ? Time.fixedDeltaTime : Time.deltaTime; if (context != null && _localTimeScales.TryGetValue(context, out float localScale)) { return baseDelta * localScale; } return baseDelta; } // 暂停游戏(保留当前timeScale以便恢复) private float _pausedTimeScale; public void Pause() { if (GlobalTimeScale > 0) { _pausedTimeScale = GlobalTimeScale; GlobalTimeScale = 0f; } } public void Resume() { GlobalTimeScale = _pausedTimeScale; } }

这个模块提供了:

  1. 单例访问:全局可访问。
  2. 封装的时间缩放控制:通过属性安全地修改Time.timeScale,并触发事件。
  3. 局部时间缩放:实现“子弹时间”仅影响特定对象,而不暂停整个游戏世界。
  4. 安全的暂停/恢复:记住暂停前的速度。
  5. 智能的DeltaTime获取:根据上下文返回可能经过局部缩放的时间增量。

在你的游戏对象脚本中,可以这样使用:

void Update() { // 使用TimeManager获取可能被局部缩放的时间增量 float delta = TimeManager.Instance.GetDeltaTime(this); // this 作为上下文key transform.Translate(Vector3.forward * speed * delta); }

7. 总结与最终建议

Unity的Time类是你控制游戏节奏和确保跨平台一致性的瑞士军刀。记住以下核心要点,能帮你避开绝大多数陷阱:

  1. 运动与deltaTime是铁律:在Update中做任何与“每秒”相关的操作,都必须乘以Time.deltaTime
  2. 物理属于FixedUpdate:对刚体的操作务必放在FixedUpdate里,并使用Time.fixedDeltaTime(或Time.deltaTime)。
  3. 暂停时想想unscaled:当Time.timeScale = 0时,所有需要继续运行的东西(UI、菜单动画、真实计时器)都应切换到Time.unscaledDeltaTimeTime.unscaledTime
  4. 协程等待要选对:游戏内延迟用WaitForSeconds,真实世界延迟用WaitForSecondsRealtime
  5. 理解时间缩放的影响范围Time.timeScale影响Time.time/deltaTime、受缩放影响的动画、粒子系统速度、以及基于WaitForSeconds的协程。它不影响Update/FixedUpdate的调用频率,不影响使用unscaled时间属性的逻辑,也不影响WaitForSecondsRealtime
  6. 性能是底线:时刻关注Time.deltaTime的波动,利用性能剖析工具,避免其触及Time.maximumDeltaTime的红线,这是保证游戏流畅体验的根本。

最后,最好的学习方式是在项目中实践并遇到问题。当你下次遇到物体移动飘忽不定、暂停功能失灵或者慢动作效果怪异时,第一个就应该回来检查你对Time类的使用是否正确。把这套机制吃透,你的Unity代码质量会立刻上一个台阶。

http://www.jsqmd.com/news/1359731/

相关文章:

  • SimdAnyof.h
  • 阿里云Qwen营收分成政策下,开发者如何实现模型本地化部署与成本优化
  • Pelican基准:超越分数,用结构化评估透视AI代码生成模型真实能力
  • GitHub今日热榜 | 2026-08-08:新王登顶,五项目首日上榜
  • 新疆彩釉玻璃企业怎么选?本地采购商推荐的品质保证指南 - GrowthUME
  • ComfyUI-KJNodes:AI图像生成工作流效率革命与终极解决方案
  • 天津零基础学烘焙靠谱机构
  • 3步搭建你的私有AI聊天室:Open WebUI终极指南
  • 现场扩声实战指南:从系统搭建到混音调试与问题排查
  • 乌鲁木齐弯弧玻璃企业怎么选?本地采购商推荐的实用指南 - GrowthUME
  • 新疆食用油礼盒哪家值得信赖?本地消费者口碑推荐指南 - GrowthUME
  • Python机器学习全栈实践:从基础到工程部署
  • 分布式电源接入配电网的调度优化与Matlab实现
  • 数字货币交易所开发成本与架构优化全解析
  • Linux高负载低CPU使用率的排查与优化实践
  • Win11Debloat:给你的Windows系统来一次深度清理,重获纯净高效体验
  • GetQzonehistory:一键免费备份QQ空间完整青春记忆的终极方案
  • 少商新材料CRRM-4系列水泥基快速修补材料汇总对比
  • 5分钟搞定音乐库:ZonyLrcToolsX歌词批量下载完全指南
  • PHP高并发优化:减少系统调用的核心技术
  • 践行行业社会责任|成都仁民有害生物防治服务有限公司以专业力量助力蓉城公共卫生建设 - GrowthUME
  • 抖音无水印下载:3分钟掌握批量下载高清视频的实用工具
  • 风控系统如何从“管交易”延伸到“管IP”?IP风险画像落地指南
  • 如何轻松获取国家中小学智慧教育平台的电子课本PDF?这个工具让您3分钟搞定
  • DataSpace基准揭示:智能体框架如何提升AI应用15.36%准确率
  • GRETNA:MATLAB中的图论网络分析工具箱,三步掌握脑网络研究核心技术
  • Kigurumi项目实战:自动化发布验证契约在云原生DevOps中的应用
  • 2026年无锡健康管理公司服务解析:多维度能力与合规情况
  • MATLAB中使用LightGBM进行回归预测的实践指南
  • 后羿射日四段秒级写法:高燃场景叙事模板拆解与应用