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

射线检测障碍物时禁止向射线方向移动的实现方案

解决方案:第三人称控制器障碍物阻挡输入与跳跃优化

看起来你遇到的核心问题有两个:一是当前的射线检测只会完全禁止移动,没法区分方向过滤输入;二是跳跃时残留的移动输入会让角色带着方向起跳。我帮你调整代码,完美解决这两个问题:

1. 重构射线检测:支持检测任意方向

先把原来的IsNotBlocked改成更灵活的方向检测函数,这样能分别检查前后左右是否有障碍物:

// 检测指定的角色局部方向是否被障碍物阻挡
bool IsDirectionBlocked(Vector3 localDirection)
{
    // 从角色胸部位置发射射线,避免地面/脚下物体干扰检测
    Vector3 rayOrigin = transform.position + Vector3.up * 0.5f;
    // 把角色局部方向转换成世界空间方向
    Vector3 worldDirection = transform.TransformDirection(localDirection);
    
    // 执行射线检测,返回是否命中目标层
    if (Physics.Raycast(rayOrigin, worldDirection, out RaycastHit hit, _CheckDistance, _RaycastCollidableLayers))
    {
        // 调试用:命中时画红色射线,方便你看检测范围
        Debug.DrawLine(rayOrigin, hit.point, Color.red);
        return true;
    }
    // 未命中时画绿色射线
    Debug.DrawLine(rayOrigin, rayOrigin + worldDirection * _CheckDistance, Color.green);
    return false;
}

2. 过滤输入方向:只保留无障碍物的移动分量

在获取用户输入后,我们把输入分解到角色的前后左右方向,逐个检查是否被阻挡,过滤掉无效的输入分量:

void Update()
{
    PushStates();

    // --- 修改输入处理逻辑 ---
    Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
    Vector2 inputDir = input.normalized;

    // 把输入拆解为角色局部的前后/左右分量
    float forwardInput = inputDir.y; // W/S对应的前后输入
    float rightInput = inputDir.x;   // A/D对应的左右输入

    // 逐个方向检查,被阻挡则清零对应输入
    if (IsDirectionBlocked(Vector3.forward) && forwardInput > 0) forwardInput = 0;
    if (IsDirectionBlocked(Vector3.back) && forwardInput < 0) forwardInput = 0;
    if (IsDirectionBlocked(Vector3.right) && rightInput > 0) rightInput = 0;
    if (IsDirectionBlocked(Vector3.left) && rightInput < 0) rightInput = 0;

    // 重新构建过滤后的有效输入方向
    Vector2 filteredInputDir = new Vector2(rightInput, forwardInput);
    // 如果过滤后输入几乎为0,直接设为Vector2.zero,避免微小移动触发动画
    if (filteredInputDir.magnitude < 0.1f)
    {
        filteredInputDir = Vector2.zero;
    }
    else
    {
        filteredInputDir = filteredInputDir.normalized;
    }
    // --- 输入处理结束 ---

    bool running = Input.GetButton("Run");

    // 使用过滤后的有效输入执行移动
    if (filteredInputDir != Vector2.zero)
    {
        Move(filteredInputDir, running);
    }
    else
    {
        // 没有有效输入时,平滑重置移动速度,避免动画异常
        currentSpeed = Mathf.SmoothDamp(currentSpeed, 0, ref speedSmoothVelocity, speedSmoothTime);
    }

    // 原有动画与跳跃检测逻辑保持不变
    float animationSpeedPercent = ((running) ? currentSpeed / runSpeed : currentSpeed / walkSpeed * .5f);
    anim.SetFloat("speedPercent", animationSpeedPercent, speedSmoothTime, Time.deltaTime);
    anim.SetFloat("speedPercentPush", animationSpeedPercent, speedSmoothTime, Time.deltaTime);

    // ... 原有跳跃检测代码 ...
    JumpCheck();
}

3. 跳跃时重置水平状态:确保只向上跳

在你的三个跳跃函数里,添加重置水平速度的代码,彻底清除输入残留,让跳跃只有垂直方向的力:

void ShortJump()
{
    // 重置水平移动状态,确保跳跃时没有水平速度
    currentSpeed = 0f;
    speedSmoothVelocity = 0f;
    
    // 原有跳跃逻辑
    velocityY = shortJumpForce; // 替换成你实际的跳跃力变量
    anim.SetTrigger("shortJump");
    canJump = Time.time + jumpCooldown;
}

void JumpWalk()
{
    currentSpeed = 0f;
    speedSmoothVelocity = 0f;
    
    velocityY = walkJumpForce;
    anim.SetTrigger("jumpWalk");
    canJump = Time.time + jumpCooldown;
}

void JumpRun()
{
    currentSpeed = 0f;
    speedSmoothVelocity = 0f;
    
    velocityY = runJumpForce;
    anim.SetTrigger("jumpRun");
    canJump = Time.time + jumpCooldown;
}

4. 优化Move函数:空中彻底禁止水平移动

如果你已经有跳跃期间禁止移动的逻辑,可以在Move函数开头加个判断,确保空中时不会处理任何水平移动:

void Move(Vector2 inputDir, bool running)
{
    // 不在地面时,直接返回不处理水平移动
    if (!GroundCheck())
    {
        currentSpeed = 0f;
        speedSmoothVelocity = 0f;
        return;
    }

    // 原有移动逻辑保持不变
    if (inputDir != Vector2.zero)
    {
        float targetRotation = Mathf.Atan2(inputDir.x, inputDir.y) * Mathf.Rad2Deg + cameraT.eulerAngles.y;
        transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime));
    }

    float targetSpeed = ((running) ? runSpeed : walkSpeed) * inputDir.magnitude;
    currentSpeed = Mathf.SmoothDamp(currentSpeed, targetSpeed, ref speedSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime));

    velocityY += Time.deltaTime * gravity;

    Vector3 velocity = transform.forward * currentSpeed + Vector3.up * velocityY;

    controller.Move(velocity * Time.deltaTime);
    currentSpeed = new Vector2(controller.velocity.x, controller.velocity.z).magnitude;

    if (GroundCheck())
    {
        velocityY = 0;
        anim.SetBool("onAir", false);
    }
    else
    {
        anim.SetBool("onAir", true);
    }
}

这些修改的核心作用:

  • 方向过滤:玩家可以向无障碍物的方向自由移动,不会被完全锁死
  • 跳跃重置:彻底清除移动输入残留,顶着障碍物跳时只会垂直向上
  • 空中锁死:跳跃过程中不会产生任何水平速度,完全符合你的需求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:25:43