首页 > 解决方案 > Unity3D BezierCurve - 跟随 AI 的 Spline Walker?

问题描述

我一直在努力创建一款具有称职(但公平)的赛车 AI 的游戏,并且我有几个我试图满足的限制。以下是按顺序排列的条件:

1.) AI 逻辑和玩家控制 BOTH 共享同一个汽车控制器来驱动和转弯等。AI 车辆简单地传递一个钳位(-1 到 1)值,具体取决于汽车试图转弯的距离和油门已订婚的。AI 和玩家共享布尔值来刹车,并用 SHIFT 键漂移转向

2.) AI 需要做出明智的、明智的决定,玩家将在应用适当的转弯量、何时滑行等方面做出明智的决定。

我最初使用简单的航路点增量系统让 AI 根据航路点继续绕轨道运行。使用正确的车轮摩擦曲线值(我碰巧在网上找到了),这实际上可以正常工作。然而,当我特别在车轮摩擦值上苦苦挣扎时,并且仍然希望为 AI 提供更平滑的路径跟随和转向逻辑时,有人建议我使用三次贝塞尔样条曲线。我发现 Catlike Coding 教程很吸引人(我相信你们中的很多人都知道这一点,但是对于任何感兴趣的人来说,这里是下面的链接):

https://catlikecoding.com/unity/tutorials/curves-and-splines/

然后,我想到了使用本教程中的 Spline Walker 作为 AI 的唯一航路点,并有效地“逗弄”汽车跟随 spline walker,就像这个视频:

https://www.youtube.com/watch?v=UcA4K2rmX-U#action=share

然而,这是我的大问题——如果我想让我的车跟随样条助行器,同时保持样条助行器始终在前面,我必须确保样条助行器跟随的“进度”相对于后车的位置,样条助行器在前面保持一点点,这样汽车就不会决定减速和停止。

我一直在寻找这样做的例子,我不能说我太成功了。

这是我当前的进度计算代码片段 - 我现在正试图通过碰巧共享相同变换坐标的旧航点对象存储样条曲线上位置之间的距离:

private void Start()
    {
        Rigidbody rb = chasingCar.GetComponent<Rigidbody>();
        if(path)
        {
            nodes = path.GetComponentsInChildren<Transform>();
        }
        Array.Resize(ref distances, nodes.Length-1);
        for (int i = 0; i < nodes.Length-1; i++)
        {
            //start storing the distances between two successive waypoints on the spline
            distances[i] = Vector3.Distance(nodes[i].position, nodes[i + 1].position);
            totalDistance += distances[i];
        }
        Debug.Log("First distance value is " + distances[0] + " and overall distance est is " + totalDistance);
        Debug.Log("Second distance value is " + distances[1] + " and overall distance est is " + totalDistance);
        Debug.Log("Fifth distance value is " + distances[4] + " and overall distance est is " + totalDistance);
    }

当追逐车及其旧的航路点路径已提供给样条时,这是样条步行器的更新功能:

Vector3 position;
        if (chasingCar && path)
        {
            float distFromCar = Vector3.Distance(transform.position, chasingCar.transform.position);
            Debug.Log("Distance from car " + distFromCar);
            if(distFromCar < 35)
            {
                //get current spline waypoint
                //int splineIdx = GetSplineIndex(progress);
                int splineIdx = chasingCar.GetComponent<CarEngine>().GetCurrentNodeTarget();
                //declare next spline waypoint
                int splineIdxNext = splineIdx + 1;
                if (path && splineIdxNext == (nodes.Length))
                    splineIdxNext = 0;
                Debug.Log("Current splineIdx " + splineIdx);
                //float currCarDistance = Vector3.Distance(chasingCar.transform.position, nodes[splineIdx].position);
                float currCarDistance = SumSplineProgress(splineIdx);
                float overallDistance = Vector3.Distance(nodes[splineIdx].position, nodes[splineIdxNext].position);
                float currCarSplineProgress = currCarDistance / overallDistance;
                float overallProgress = (currCarDistance) / (totalDistance);
                progress = overallProgress;

            }
            else
            {
                progress += Time.deltaTime / duration;
            }
            Debug.Log("Chasing, current progress: " + progress);
            position = spline.GetPoint(progress);

最后,这是我过去尝试用来计算样条步行器进度的函数:

int GetSplineIndex(float progress)
{
    float curProgress = progress * (totalDistance);
    Debug.Log("Current calculated progress " + curProgress);
    return System.Convert.ToInt32(Mathf.Floor(curProgress));
}

float SumSplineProgress(int index)
{
    float currTotalDistance = 0f;
    for(int i = index; i > -1; i--)
    {
        currTotalDistance += distances[i];
    }
    return currTotalDistance;
}

我可能只是让自己变得比我需要的更难,但我只想说,我真的很难过。当 AI 汽车的当前起点和终点航路点之间的距离更大时,我已经接近让样条航路点在汽车前面跳跃更多,但这仍然不是我想要实现的目标。

有人在这里有什么特别的建议吗?提示,方向的轻推和代码会很棒。提前致谢!

更新

是的,我仍在努力!在之前的样条计算代码中有一些我认为是错误的逻辑 - 例如,这个:

float currCarSplineProgress = currCarDistance / overallDistance;

我已经改成这样:

float currCarSplineProgress = (currCarDistance) / currSplineLength;

该部分的想法是检查汽车在整个样条曲线中接近的当前曲线上的进展,并相应地定位样条步行器,以便它在需要时向前跳到下一个样条曲线。这是完整的更新代码:

Vector3 position;
        if (chasingCar && path)
        {
            float distFromCar = Vector3.Distance(transform.position, chasingCar.transform.position);
            Debug.Log("Distance from car " + distFromCar);
            if(distFromCar < 50)
            {
                //get current spline waypoint
                //int splineIdx = GetSplineIndex(progress);
                int splineIdx = chasingCar.GetComponent<CarEngine>().GetCurrentNodeTarget()-1;
                //declare next spline waypoint
                int splineIdxNext = splineIdx + 1;
                if(splineIdx == -1)
                {
                    splineIdx = nodes.Length - 2;
                    splineIdxNext = 0;
                }
                if (path && splineIdxNext == (nodes.Length))
                    splineIdxNext = 0;
                Debug.Log("Current splineIdx " + splineIdx);
                //float currCarDistance = GetConvertedDistance(chasingCar.transform.position, nodes[splineIdx].position);
                float currCarDistance = Vector3.Distance(chasingCar.transform.position, nodes[splineIdx].position);
                float restDistance = Vector3.Distance(chasingCar.transform.position, nodes[splineIdxNext].position);
                //float currCarDistance = SumSplineProgress(splineIdx);
                Debug.Log("currCarDistance " + currCarDistance);
                //float currSplineLength = Vector3.Distance(nodes[splineIdx].position, nodes[splineIdxNext].position);
                float currSplineLength = currCarDistance + restDistance;
                float overallDistance = 0;
                float nextOverallDist = 0f;
                if(splineIdx != 0)
                    overallDistance = SumSplineProgress(splineIdx-1);
                Debug.Log("overallDistance " + overallDistance);
                float currCarSplineProgNext = 0f;
                if (splineIdxNext != 1 && splineIdxNext != 0)
                {
                    nextOverallDist = SumSplineProgress(splineIdxNext - 1);
                    currCarSplineProgNext = (currCarDistance) / nextOverallDist;
                }
                Debug.Log("currSplineLength " + currSplineLength);
                float currCarSplineProgress = (currCarDistance) / currSplineLength;
                float leading = 10f;
                if (distFromCar < 20)
                    leading += 15f;
                float overallProgress;
                Debug.Log("currCarSplineProgress " + currCarSplineProgress);
                if (currCarSplineProgress < .7f)
                {
                    overallProgress = (currSplineLength + (currCarDistance * .3f)) / (totalDistance);
                }
                else
                {
                    Debug.Log("Jumping to next waypoint...");
                    overallProgress = (nextOverallDist + (currCarDistance * .3f)) / (totalDistance);
                }
                Debug.Log("Overall progress " + overallProgress);
                //if (overallProgress >= 1f)
                //    overallProgress = 0f;
                progress = overallProgress;

            }
            else
            {
                progress += Time.deltaTime / duration;
            }
            Debug.Log("Chasing, current progress: " + progress);
            position = spline.GetPoint(progress);
        }
        else
        {
            position = spline.GetPoint(progress);
        }
        transform.localPosition = position;

然而,当汽车取得足够的进展时,意外的事情仍然会发生 - 花键步行器会突然跳到前面的部分之一!

在此处输入图像描述

有什么见解吗?

标签: unity3dartificial-intelligenceracing

解决方案


你有正确的想法让 AI 追逐一个沿着样条线移动的目标。我相信这就是赛车游戏中大多数 AI 的工作方式。

为确保目标始终领先于 AI,请将目标的位置设置为样条曲线上 AI 的位置加上与 AI 移动速度相关的某个值。

我建议查看 Unity 标准资产包:

https://assetstore.unity.com/packages/essentials/asset-packs/standard-assets-for-unity-2017-3-32351

那里有一个汽车人工智能系统,它通过跟踪沿样条线移动的目标来工作。


推荐阅读