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

Unity中A、B位置间Lerp插值时Y轴最大最小值限制问题

解决方案:保持插值点在AB连线同时钳制Y轴上限

核心问题是直接修改Y值会让点脱离A(当前物体)与B(centerGround)的连线,正确的做法是在AB连线的范围内找到Y值不超过listenerMaxY的点,而不是硬改坐标。

实现思路

  1. 先按原有逻辑计算出AB连线上的插值点(记为targetPoint)
  2. 如果targetPoint的Y值超过listenerMaxY,则重新计算在AB连线上、Y值恰好等于listenerMaxY的点
  3. 否则直接使用原插值点

修改后的核心代码

float percentageDistance = downwardDistance / listenerMinY;
float lerpRatio = Mathf.Clamp(percentageDistance, 0, .5f);
Vector3 targetPoint = Vector3.Lerp(transform.position, centerGround.position, lerpRatio);

// 处理Y轴上限钳制,同时保持在AB连线上
if (targetPoint.y > listenerMaxY)
{
    // 计算A到B的Y轴差值
    float yDiff = centerGround.position.y - transform.position.y;
    // 如果A和B的Y值相同(极端情况),直接钳制Y
    if (Mathf.Abs(yDiff) < 0.001f)
    {
        targetPoint.y = listenerMaxY;
    }
    else
    {
        // 计算需要调整的lerp比例,让Y刚好等于listenerMaxY
        float requiredRatio = (listenerMaxY - transform.position.y) / yDiff;
        // 确保比例不超过原插值的范围(0到0.5),避免点超出AB段的插值区间
        requiredRatio = Mathf.Clamp(requiredRatio, 0, lerpRatio);
        targetPoint = Vector3.Lerp(transform.position, centerGround.position, requiredRatio);
    }
}

listener.position = targetPoint;
listener.rotation = transform.rotation;

完整修改后的代码

public class CameraRay : MonoBehaviour
{
    [SerializeField] LayerMask groundLayer;
    [SerializeField] Transform centerGround;
    [SerializeField] Transform listener;

    [SerializeField] int listenerMinY = 20;
    [SerializeField] int listenerMaxY = 60;


    private void Update()
    {
        RaycastHit hit;

        float forwardDistance = 0;
        float downwardDistance = 0;

        // 控制地面目标点B的位置
        if (Physics.Raycast(transform.position, transform.forward, out hit, Mathf.Infinity, groundLayer))
        {
            forwardDistance = hit.distance;
            Debug.DrawRay(transform.position, transform.forward * forwardDistance, Color.blue);
            centerGround.position = hit.point;
        }

        // 检测到地面的垂直距离,用颜色区分状态
        if (Physics.Raycast(transform.position, Vector3.down, out hit, Mathf.Infinity, groundLayer))
        {
            downwardDistance = hit.distance;

            Color rayColor = Color.yellow;
            if (downwardDistance < listenerMinY)
                rayColor = Color.green;
            else if (downwardDistance > listenerMaxY)
                rayColor = Color.red;
            
            Debug.DrawRay(transform.position, Vector3.down * downwardDistance, rayColor);
        }

        float percentageDistance = downwardDistance / listenerMinY;
        float lerpRatio = Mathf.Clamp(percentageDistance, 0, .5f);
        Vector3 targetPoint = Vector3.Lerp(transform.position, centerGround.position, lerpRatio);

        // 处理Y轴上限钳制,同时保持在AB连线上
        if (targetPoint.y > listenerMaxY)
        {
            float yDiff = centerGround.position.y - transform.position.y;
            if (Mathf.Abs(yDiff) < 0.001f)
            {
                targetPoint.y = listenerMaxY;
            }
            else
            {
                float requiredRatio = (listenerMaxY - transform.position.y) / yDiff;
                requiredRatio = Mathf.Clamp(requiredRatio, 0, lerpRatio);
                targetPoint = Vector3.Lerp(transform.position, centerGround.position, requiredRatio);
            }
        }

        listener.position = targetPoint;
        listener.rotation = transform.rotation;
    }
}

逻辑解释

  • 当原插值点的Y超过上限时,通过线性计算找到AB连线上Y等于listenerMaxY的精确位置,确保点始终在A和B的连线上
  • 加入了Y差极小的判断,避免除以0的异常
  • 用Mathf.Clamp(requiredRatio, 0, lerpRatio)确保调整后的点不会超出原插值的范围(不会跑到原插值点的外侧)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 23:45:54