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

Unity中如何找到主角附近的最近平坦区域?解决角色空中卡顿问题

Unity中寻找主角附近最近平坦区域的实现方案

一、空中卡住的检测逻辑

首先需要判断主角是否处于空中卡住状态,核心是通过地面检测+计时器触发后续逻辑:

  • 利用CharacterController.isGrounded(或射线检测)判断主角是否在地面
  • 维护计时器,当主角空中停留超过2-3秒时,触发平坦区域搜索
public class PlayerStuckHandler : MonoBehaviour
{
    public float stuckThreshold = 2.5f; // 空中停留触发阈值
    public float groundCheckDistance = 0.5f; // 地面检测射线长度
    private float stuckTimer = 0f;
    private CharacterController controller;

    void Awake()
    {
        controller = GetComponent<CharacterController>();
    }

    void Update()
    {
        bool isGrounded = controller.isGrounded;
        // 替代方案:用射线检测地面
        // isGrounded = Physics.Raycast(transform.position, Vector3.down, groundCheckDistance);

        if (!isGrounded)
        {
            stuckTimer += Time.deltaTime;
            if (stuckTimer >= stuckThreshold)
            {
                var nearestFlatPos = FindNearestFlatArea();
                if (nearestFlatPos.HasValue)
                {
                    transform.position = nearestFlatPos.Value;
                    stuckTimer = 0f;
                }
            }
        }
        else
        {
            stuckTimer = 0f;
        }
    }
}

二、核心:寻找符合条件的最近平坦区域

以下提供两种实用方案,根据项目场景选择:

方案1:球形范围检测地面(无NavMesh场景适用)

通过球形范围扫描地面碰撞体,筛选出满足角度要求且空间足够的区域:

private Vector3? FindNearestFlatArea()
{
    float searchRadius = 10f; // 搜索范围,可根据场景调整
    float requiredClearRadius = 0.5f; // 所需空区域半径
    int groundLayerMask = LayerMask.GetMask("Ground"); // 替换为你的地面层名称

    var groundColliders = Physics.OverlapSphere(transform.position, searchRadius, groundLayerMask);

    Vector3? nearestPos = null;
    float minDistance = float.MaxValue;

    foreach (var collider in groundColliders)
    {
        RaycastHit hit;
        if (Physics.Raycast(transform.position, collider.bounds.center - transform.position, out hit, searchRadius, groundLayerMask))
        {
            // 检测地面角度:法线与竖直方向夹角需在0-60度之间
            float angle = Vector3.Angle(hit.normal, Vector3.up);
            if (angle >= 0f && angle <= 60f)
            {
                // 检测目标点是否有足够空区域:排除地面外的其他碰撞体
                if (!Physics.CheckSphere(hit.point + Vector3.up * 0.1f, requiredClearRadius, ~groundLayerMask))
                {
                    float distance = Vector3.Distance(transform.position, hit.point);
                    if (distance < minDistance)
                    {
                        minDistance = distance;
                        nearestPos = hit.point + Vector3.up * 0.2f; // 轻微抬高避免卡地面
                    }
                }
            }
        }
    }

    return nearestPos;
}

方案2:NavMesh可行走区域搜索(有NavMesh场景适用)

如果场景已烘焙NavMesh,且NavMesh区域符合角度要求,可直接用NavMesh API快速定位:

using UnityEngine.AI;

private Vector3? FindNearestFlatArea()
{
    float searchRadius = 10f;
    float requiredClearRadius = 0.5f;
    NavMeshHit hit;

    if (NavMesh.SamplePosition(transform.position, out hit, searchRadius, NavMesh.AllAreas))
    {
        // 额外验证空区域,避免NavMesh上存在障碍物
        if (!Physics.CheckSphere(hit.position + Vector3.up * 0.1f, requiredClearRadius, ~LayerMask.GetMask("Ground", "NavMesh")))
        {
            return hit.position + Vector3.up * 0.2f;
        }
    }

    return null;
}

三、关键注意事项

  • 地面层设置:确保所有地面对象归为同一Layer,保证LayerMask筛选准确
  • 搜索范围优化:searchRadius不宜过大,避免性能损耗;过小可能找不到有效区域
  • 空区域检测高度:检测点轻微抬高(+0.1f),避免误判地面本身为障碍物
  • 性能优化:高频场景可降低检测频率(比如每0.5秒检测一次),减少计算开销

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 05:24:26