如何按比例计算数值替代固定步长,实现相机随移动速度平滑调整
根据玩家移动速度平滑调整相机参数的实现方案
问题背景
希望实现根据玩家移动速度调整相机与玩家距离的效果。当前代码采用固定步长判断,通过
InvokeRepeating每1秒执行一次,效果不够流畅。现希望通过按比例计算相关数值,将代码移至Update()中以获得更平滑的持续调整效果,原代码如下:
var movingSpeed = playerObject.GetComponent<Rigidbody>().velocity.magnitude; var cFollow = mainCamera.GetComponent<Follow>(); if(movingSpeed > 3.5){ cCameraController.DesiredDistance = 2.3f; cFollow.TiltVector = new Vector3(0f, -0.4f, 0f); }else if(axis == 2.8){ // 注:原代码疑似笔误,应为movingSpeed相关判断 cCameraController.DesiredDistance = 1.9f; cFollow.TiltVector = new Vector3(0f, -0.3f, 0f); }else if(axis == 1.8){ // 注:原代码疑似笔误,应为movingSpeed相关判断 cCameraController.DesiredDistance = 1.5f; cFollow.TiltVector = new Vector3(0f, -0.2f, 0f); }else{ cCameraController.DesiredDistance = 1.1f; cFollow.TiltVector = new Vector3(0f, 0f, 0f); }
核心思路
放弃固定阈值的硬切换逻辑,改用线性插值(Lerp)或速度区间比例计算,让相机参数随玩家速度变化平滑过渡;同时将逻辑移至Update()中每帧执行,彻底解决1秒间隔导致的卡顿感。
可行实现方案
方案一:匹配原阈值的区间平滑过渡
该方案严格对应你原有的速度阈值和参数值,同时实现区间内的平滑变化:
// 可序列化字段,方便在Unity Inspector中可视化调整参数 [SerializeField] private float[] speedThresholds = { 0f, 1.8f, 2.8f, 3.5f }; [SerializeField] private float[] distanceTargets = { 1.1f, 1.5f, 1.9f, 2.3f }; [SerializeField] private float[] tiltYTargets = { 0f, -0.2f, -0.3f, -0.4f }; [SerializeField] private float smoothSpeed = 5f; // 调整参数变化的平滑程度 private Rigidbody _playerRb; private Follow _cameraFollow; private CameraController _cameraController; void Awake() { // 提前获取组件,避免每帧调用GetComponent浪费性能 _playerRb = playerObject.GetComponent<Rigidbody>(); _cameraFollow = mainCamera.GetComponent<Follow>(); _cameraController = cCameraController; // 假设cCameraController已提前获取引用 } void Update() { float currentSpeed = _playerRb.velocity.magnitude; float targetDistance = distanceTargets[0]; float targetTiltY = tiltYTargets[0]; // 匹配当前速度所在的区间 for (int i = 0; i < speedThresholds.Length - 1; i++) { float lowerSpeed = speedThresholds[i]; float upperSpeed = speedThresholds[i + 1]; if (currentSpeed >= lowerSpeed && currentSpeed <= upperSpeed) { // 计算当前速度在区间内的占比 float ratio = (currentSpeed - lowerSpeed) / (upperSpeed - lowerSpeed); // 插值得到区间内的目标参数 targetDistance = Mathf.Lerp(distanceTargets[i], distanceTargets[i + 1], ratio); targetTiltY = Mathf.Lerp(tiltYTargets[i], tiltYTargets[i + 1], ratio); break; } } // 处理速度超过最大阈值的情况 if (currentSpeed > speedThresholds[^1]) { targetDistance = distanceTargets[^1]; targetTiltY = tiltYTargets[^1]; } // 平滑过渡到目标参数,Time.deltaTime保证帧率无关 _cameraController.DesiredDistance = Mathf.Lerp(_cameraController.DesiredDistance, targetDistance, Time.deltaTime * smoothSpeed); _cameraFollow.TiltVector = Vector3.Lerp(_cameraFollow.TiltVector, new Vector3(0f, targetTiltY, 0f), Time.deltaTime * smoothSpeed); }
方案二:简化版速度直接映射
如果不需要严格对应原阈值,可直接将速度范围映射为参数范围,实现更简洁的平滑效果:
[SerializeField] private float minSpeed = 0f; [SerializeField] private float maxSpeed = 3.5f; [SerializeField] private float minDistance = 1.1f; [SerializeField] private float maxDistance = 2.3f; [SerializeField] private float minTiltY = 0f; [SerializeField] private float maxTiltY = -0.4f; [SerializeField] private float smoothSpeed = 5f; private Rigidbody _playerRb; private Follow _cameraFollow; private CameraController _cameraController; void Awake() { _playerRb = playerObject.GetComponent<Rigidbody>(); _cameraFollow = mainCamera.GetComponent<Follow>(); _cameraController = cCameraController; } void Update() { float currentSpeed = _playerRb.velocity.magnitude; // 限制速度在设定范围内,避免参数超出预期值 float clampedSpeed = Mathf.Clamp(currentSpeed, minSpeed, maxSpeed); // 计算速度在范围内的占比 float speedRatio = (clampedSpeed - minSpeed) / (maxSpeed - minSpeed); // 平滑过渡到目标参数 float targetDistance = Mathf.Lerp(minDistance, maxDistance, speedRatio); float targetTiltY = Mathf.Lerp(minTiltY, maxTiltY, speedRatio); _cameraController.DesiredDistance = Mathf.Lerp(_cameraController.DesiredDistance, targetDistance, Time.deltaTime * smoothSpeed); _cameraFollow.TiltVector = Vector3.Lerp(_cameraFollow.TiltVector, new Vector3(0f, targetTiltY, 0f), Time.deltaTime * smoothSpeed); }
关键优化说明
- 提前缓存组件:在
Awake()中获取所需组件,避免每帧调用GetComponent带来的性能开销。 - 帧率无关平滑:使用
Time.deltaTime控制插值速度,确保在不同帧率的设备上表现一致。 - 双层平滑过渡:先根据速度计算目标参数,再从当前值向目标值插值,避免参数跳变,提升流畅感。
内容的提问来源于stack exchange,提问作者anderlaini
相关产品推荐
相关产品推荐

