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

如何按比例计算数值替代固定步长,实现相机随移动速度平滑调整

根据玩家移动速度平滑调整相机参数的实现方案

问题背景

希望实现根据玩家移动速度调整相机与玩家距离的效果。当前代码采用固定步长判断,通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 17:11:01