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

如何实现Unity角色蹲伏/站起的平滑慢速过渡?

解决Unity角色蹲伏/站起过渡速度过快的问题

我想让角色蹲伏和站起的过程更平滑缓慢,但不管是直接修改transform.localScale还是后来用Mathf.Lerp调整CharacterController的height和center,过渡速度都太快,求解决办法。

初始核心代码

private void Start()
{
    startYScale = transform.localScale.y;
    mainCamera = Camera.main;
}
private void PlayerMovement() 
{
    currentSpeed = moveSpeed;
    move = controls.Player.Movement.ReadValue<Vector2>();
    movement = (move.y * transform.forward) + (move.x * transform.right);
    if (isRunning)
    {
        currentSpeed = runSpeed; 
        Vector3 running = (move.y * transform.forward) + (move.x * transform.right);
        controller.Move(running * currentSpeed * Time.deltaTime);
    }
    if (crouching)
    {
        currentSpeed = crouchSpeed;
        isRunning = false; 
        transform.localScale = new Vector3(transform.localScale.x, crouchYScale, transform.localScale.z);
    }
    else
    {
        currentSpeed = moveSpeed;
        transform.localScale = new Vector3(transform.localScale.x, startYScale, transform.localScale.z);
    }
   
    controller.Move(movement * currentSpeed * Time.deltaTime);
}

尝试优化后的代码

private void FixedUpdate()
{
    var desierdHeight = crouching ? crouchYScale : startYScale;
    if (controller.height != desierdHeight)
    {
        AdjustHeight(desierdHeight);
        playerCamera.transform.localPosition = new Vector3(0,controller.height, 0); 
    }
}
private void AdjustHeight(float height)
{
    float center = height / 2; 
    controller.height = Mathf.Lerp(controller.height, height, crouchSpeed);
    controller.center = Vector3.Lerp(controller.center, new Vector3(0, center,0), crouchSpeed);
}

问题根源分析

  1. 复用移动速度作为插值比例:你用了crouchSpeed(移动速度参数,通常远大于1)作为Mathf.Lerp的第三个参数,而这个参数要求是0-1之间的插值比例,大于1会直接跳到目标值,根本没有过渡效果。
  2. 未加入时间增量:Lerp计算没乘以Time.fixedDeltaTime,导致过渡速度随帧率波动,且每帧变化幅度过大。
  3. 浮点精度判断问题:直接用controller.height != desierdHeight判断,会因为浮点精度误差一直触发调整,还可能导致抖动。

具体解决方案

1. 新增独立过渡参数

不要复用移动速度,单独定义控制过渡快慢的变量,可在Inspector面板可视化调整:

[SerializeField] private float crouchTransitionSpeed = 2f; // 值越小过渡越慢
[SerializeField] private float heightThreshold = 0.01f; // 浮点精度阈值
private float startHeight;
private float crouchHeight;

2. 修正Lerp逻辑,加入时间增量

保证过渡速度帧率无关,同时用阈值判断结束过渡:

private void Start()
{
    controller = GetComponent<CharacterController>();
    playerCamera = Camera.main;
    // 记录CharacterController初始高度,不再依赖localScale
    startHeight = controller.height;
    crouchHeight = startHeight * 0.5f; // 自定义蹲伏高度比例
}

private void FixedUpdate()
{
    float targetHeight = crouching ? crouchHeight : startHeight;
    
    // 用阈值判断是否接近目标,避免浮点精度问题
    if (Mathf.Abs(controller.height - targetHeight) > heightThreshold)
    {
        AdjustHeight(targetHeight);
        // 同步平滑调整相机位置
        Vector3 targetCamPos = new Vector3(0, targetHeight, 0);
        playerCamera.transform.localPosition = Vector3.Lerp(
            playerCamera.transform.localPosition, 
            targetCamPos, 
            crouchTransitionSpeed * Time.fixedDeltaTime
        );
    }
    else
    {
        // 直接设置为目标值,结束过渡
        controller.height = targetHeight;
        controller.center = new Vector3(0, targetHeight / 2, 0);
        playerCamera.transform.localPosition = new Vector3(0, targetHeight, 0);
    }
}

private void AdjustHeight(float targetHeight)
{
    float targetCenter = targetHeight / 2;
    float lerpFactor = crouchTransitionSpeed * Time.fixedDeltaTime;
    controller.height = Mathf.Lerp(controller.height, targetHeight, lerpFactor);
    controller.center = Vector3.Lerp(controller.center, new Vector3(0, targetCenter, 0), lerpFactor);
}

3. 移除localScale修改逻辑

直接修改transform.localScale会导致碰撞体缩放异常,容易出现穿墙、碰撞检测失效等问题,用CharacterController的height和center控制高度是更规范的做法。

4. 进阶:用SmoothDamp实现更自然的过渡

如果需要带加速度/减速度的顺滑效果,可替换Mathf.Lerp为Mathf.SmoothDamp:

private float heightVelocity;
private float centerVelocity;

private void AdjustHeight(float targetHeight)
{
    float targetCenter = targetHeight / 2;
    // 0.2f为平滑时间,值越大过渡越慢
    controller.height = Mathf.SmoothDamp(controller.height, targetHeight, ref heightVelocity, 0.2f);
    controller.center = Vector3.SmoothDamp(controller.center, new Vector3(0, targetCenter, 0), ref centerVelocity, 0.2f);
}

内容的提问来源于stack exchange,提问作者Okashi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 14:54:14