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

Unity 3D足球游戏路径绘制问题:无法识别深度轴

解决3D足球游戏路径绘制无法识别深度轴的问题

问题根源

你当前的实现直接使用Raycast命中Box Collider的hit.point,但如果Collider是薄平面(比如仅覆盖地面Y轴高度),命中点的Z轴(深度)值会被固定在Collider的Z位置,无法反映玩家想要的3D深度方向输入,导致路径只能在X/Y平面延伸。

修复方案

以下两种方案都能让路径支持3D空间的X/Y/Z三轴,根据你的游戏需求选择:

方案1:基于目标深度平面的坐标映射

直接将屏幕输入映射到3D空间中指定深度的平面,替代Raycast的命中点,适合固定深度范围的场景(比如预设球门区域的深度):

private List<Vector3> points = new List<Vector3>();
public Transform ball;
public LineRenderer lineRenderer;
// 可根据场景调整的目标深度(比如球门所在的Z坐标)
public float targetDepth = 10f;
// 绘制区域的X/Y范围限制
public float minX = -5f;
public float maxX = 5f;
public float minY = 0f;
public float maxY = 3f;

private void Update()
{
    if (Input.GetMouseButton(0))
    {
        Camera mainCam = Camera.main;
        // 将屏幕点转换为目标深度平面上的世界坐标
        Vector3 screenPos = Input.mousePosition;
        screenPos.z = mainCam.WorldToScreenPoint(new Vector3(0, 0, targetDepth)).z;
        Vector3 worldPoint = mainCam.ScreenToWorldPoint(screenPos);

        // 限制在设定的绘制区域内
        if (worldPoint.x >= minX && worldPoint.x <= maxX && worldPoint.y >= minY && worldPoint.y <= maxY)
        {
            if (points.Count == 0 || Vector3.Distance(points[points.Count - 1], worldPoint) > 0.1f)
            {
                points.Add(worldPoint);
                lineRenderer.positionCount = points.Count;
                lineRenderer.SetPositions(points.ToArray());
            }
        }
    }

    if (Input.GetMouseButtonUp(0))
    {
        if (points.Count == 0) return;
        ball.position = points[0];
        StartCoroutine(MoveBallAlongPath());
        points.Clear();
        lineRenderer.positionCount = 0;
    }
}

private IEnumerator MoveBallAlongPath()
{
    int resolution = 100;
    // 改用基于时间的移动,避免帧率影响速度
    float moveSpeed = 8f;

    for (int i = 0; i < points.Count - 1; i++)
    {
        Vector3 pointA = points[i];
        Vector3 pointB = points[i + 1];
        Vector3 controlPoint = (pointA + pointB) / 2;

        for (int j = 0; j < resolution; j++)
        {
            float t = j / (float)resolution;
            Vector3 targetPoint = CalculateBezierPoint(t, pointA, controlPoint, pointB);
            float distance = Vector3.Distance(ball.position, targetPoint);
            float moveTime = distance / moveSpeed;
            float startTime = Time.time;

            while (Time.time - startTime < moveTime)
            {
                float progress = (Time.time - startTime) / moveTime;
                ball.position = Vector3.Lerp(ball.position, targetPoint, progress);
                yield return null;
            }
        }
    }
}

private Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
{
    float u = 1 - t;
    float tt = t * t;
    float uu = u * u;

    Vector3 p = uu * p0;
    p += 2 * u * t * p1;
    p += tt * p2;

    return p;
}

方案2:支持动态调整深度的Raycast方案

保留Box Collider作为绘制区域,但让玩家可以通过鼠标滚轮/触摸滑动调整深度,适合需要自由控制3D路径的场景:

private List<Vector3> points = new List<Vector3>();
public Transform ball;
public LineRenderer lineRenderer;
public Collider drawArea;
// 初始深度及范围限制
private float currentDepth = 5f;
private float minDepth = 2f;
private float maxDepth = 15f;

private void Update()
{
    // 鼠标滚轮调整深度
    if (Input.mouseScrollDelta.y != 0)
    {
        currentDepth += Input.mouseScrollDelta.y * 0.5f;
        currentDepth = Mathf.Clamp(currentDepth, minDepth, maxDepth);
    }

    if (Input.GetMouseButton(0))
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        // 获取射线在当前深度的点
        Vector3 targetPoint = ray.GetPoint(currentDepth);

        // 检查是否在绘制区域内
        if (drawArea.bounds.Contains(targetPoint))
        {
            if (points.Count == 0 || Vector3.Distance(points[points.Count - 1], targetPoint) > 0.1f)
            {
                points.Add(targetPoint);
                lineRenderer.positionCount = points.Count;
                lineRenderer.SetPositions(points.ToArray());
            }
        }
    }

    if (Input.GetMouseButtonUp(0))
    {
        if (points.Count == 0) return;
        ball.position = points[0];
        StartCoroutine(MoveBallAlongPath());
        points.Clear();
        lineRenderer.positionCount = 0;
    }
}

// MoveBallAlongPath和CalculateBezierPoint方法同方案1

额外优化点

  • 路径平滑度:当前二阶贝塞尔曲线可以改为三阶(添加两个控制点),让转弯更自然。
  • 触摸适配:针对移动端,可添加双指滑动调整深度的逻辑,替代鼠标滚轮。
  • 路径预览:绘制时可以实时预览球的移动轨迹,提升玩家体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 17:34:50