别再乱用Update了!用FixedUpdate和LateUpdate搞定Unity物理与相机跟随(附Time.deltaTime详解)

别再乱用Update了!用FixedUpdate和LateUpdate搞定Unity物理与相机跟随(附Time.deltaTime详解) 别再乱用Update了用FixedUpdate和LateUpdate搞定Unity物理与相机跟随附Time.deltaTime详解在Unity开发中帧更新函数的正确使用是区分新手与资深开发者的关键分水岭。许多开发者习惯将所有逻辑塞进Update函数却不知这会导致物理模拟不稳定、相机抖动等隐蔽问题。本文将结合3D平台跳跃游戏的实战场景拆解FixedUpdate、Update和LateUpdate的核心差异并给出可直接复用的代码模板。1. 三大更新函数的本质区别1.1 FixedUpdate物理世界的节拍器FixedUpdate是Unity物理引擎的心跳默认每0.02秒50Hz调用一次。其核心特征包括固定时间步长不受渲染帧率影响通过Time.fixedDeltaTime获取固定间隔物理计算专用Rigidbody移动、碰撞检测等物理相关操作必须在此执行累积补偿机制当游戏帧率低于物理更新频率时Unity会自动补足调用次数void FixedUpdate() { // 正确的物理移动方式 rigidbody.MovePosition(transform.position moveDirection * 10f * Time.fixedDeltaTime); }1.2 Update游戏逻辑的主战场作为最常用的更新函数Update每渲染帧调用一次但开发者常犯以下错误错误用法正确替代方案问题原因transform.Translate(0, 0, 1)transform.Translate(0, 0, 30*Time.deltaTime)忽略帧率波动直接修改Rigidbody属性使用AddForce或MovePosition绕过物理引擎提示Time.deltaTime表示上一帧的实际耗时是解决帧率依赖问题的关键1.3 LateUpdate相机与渲染的终局处理在所有Update执行完毕后调用其典型应用场景包括相机跟随确保角色移动完成后再更新相机位置UI元素定位避免与其他物体的位置更新产生竞争后期处理效果依赖场景最终状态的特效void LateUpdate() { cameraTransform.position Vector3.Lerp( cameraTransform.position, target.position offset, 5f * Time.deltaTime ); }2. 平台跳跃游戏中的实战分配方案2.1 角色移动系统设计在3D平台跳跃游戏中不同组件的更新策略应如下划分物理移动FixedUpdate重力应用跳跃力施加碰撞检测响应输入检测Update按键状态轮询动画参数计算非物理位移如过场动画状态同步LateUpdate角色模型朝向同步特效位置修正影子投射器更新2.2 典型错误案例对比以下是将物理计算错误放入Update的后果// 错误示范帧率依赖型移动 void Update() { rigidbody.velocity new Vector3(Input.GetAxis(Horizontal) * 10f, 0, 0); } // 正确示范帧率无关型移动 void FixedUpdate() { rigidbody.AddForce( new Vector3(Input.GetAxis(Horizontal) * 100f, 0, 0), ForceMode.Acceleration ); }当游戏帧率从60FPS降到30FPS时错误代码会导致角色移动速度减半而正确方案能保持稳定表现。3. Time时间系统的深度解析3.1 deltaTime与fixedDeltaTime的数学本质这两个参数的本质区别可通过以下公式理解移动距离 速度 × 时间间隔 // Update版本动态时间步长 distance speed * Time.deltaTime; // FixedUpdate版本固定时间步长 distance speed * Time.fixedDeltaTime;3.2 时间缩放的影响与处理当修改Time.timeScale时各更新函数的行为变化函数调用频率时间参数典型影响Update不变deltaTime缩放动画播放速度变化FixedUpdate可能改变fixedDeltaTime不变物理模拟失准LateUpdate不变deltaTime缩放相机跟随延迟注意物理模拟相关游戏如平台跳跃应尽量避免修改timeScale4. 完整实现角色控制相机跟随系统4.1 角色控制器代码public class PlayerController : MonoBehaviour { [SerializeField] float moveSpeed 5f; [SerializeField] float jumpForce 8f; Rigidbody rb; bool isGrounded; void Awake() { rb GetComponentRigidbody(); } void Update() { // 输入检测非物理操作 if (Input.GetButtonDown(Jump) isGrounded) { rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse); } } void FixedUpdate() { // 物理移动 float moveInput Input.GetAxis(Horizontal); rb.velocity new Vector3(moveInput * moveSpeed, rb.velocity.y, 0); } void OnCollisionStay(Collision collision) { // 接地检测 if (collision.contacts[0].normal.y 0.7f) { isGrounded true; } } void OnCollisionExit(Collision collision) { isGrounded false; } }4.2 智能相机跟随方案public class CameraFollow : MonoBehaviour { [SerializeField] Transform target; [SerializeField] Vector3 offset new Vector3(0, 2f, -5f); [SerializeField] float smoothTime 0.3f; Vector3 velocity Vector3.zero; void LateUpdate() { Vector3 targetPosition target.position offset; transform.position Vector3.SmoothDamp( transform.position, targetPosition, ref velocity, smoothTime ); transform.LookAt(target); } }在实际项目中这种架构可使角色移动帧率稳定在0.02秒间隔而相机平滑度维持在60FPS的视觉效果。当遇到复杂场景时建议将相机的碰撞检测也放在LateUpdate中处理避免穿墙问题。