2

I've been struggling to create a game with a competent (but fair) racing AI, and I have several constraints that I'm trying to meet. Here's the conditions in order:

1.) The AI logic and player controls BOTH share the same car controller to drive and turn, etc. The AI vehicle simply passes a clamped (-1 to 1) value depending on how far the car is trying to turn and the throttle is engaged. Both the AI and player share the boolean to brake, and drift turning with the SHIFT key

2.) The AI needs to make smart, informed decisions that the player would in applying the right amount of turn, when to coast, etc.

I originally used a plain waypoint increment system to have the AI continue lapping around the track depending on the waypoints. With the right wheel friction curve values (which I happened to luckily find online) this actually works OK. However, while I was struggling more with the wheel friction values especially, and still want to have a smoother path following and turning logic for the AI, it was suggested to me to use cubic Bezier splines. I found the Catlike Coding tutorial fascinating (I'm sure many of you know this, but here's the link below for anyone interested):

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

I then got the idea of using the Spline Walker from this tutorial to be the ONE waypoint for the AI, and effectively "tease" the car to follow the spline walker, like this video:

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

However, here's my big problem - if I want to have my car FOLLOW the spline walker, while keeping the spline walker always in front, I have to ensure the "progress" that the spline walker follows is relative to the position of the following car, with the spline walker remaining a LITTLE in front so that the car doesn't decide to slow down and stop.

I've been looking for examples to do this, and I can't say I've been too successful.

Here's my current progress calculating code snippets - I'm trying to store distances between positions on the spline right now, via the old waypoint objects which happen to share the same transform coordinates:

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);
    }

This is in the update function for the spline walker when the chasing car and it's old waypoint path have been provided to the spline:

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);

Finally, here's the functions I've tried to use to calculate the spline walker progress in the past:

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;
}

I might just be making it harder on myself than I need to, but I'm just going to say, I'm legit stumped. I got close with having the spline waypoint jump ahead of the car MORE when there is more distance between the current start and end waypoint for the AI car, but that's still not what I'm trying to achieve.

Anyone have any particular suggestions here? Tips, nudges in the direction, and code would be fantastic. Thanks in advance!

UPDATE

Yes, I'm still working on this! There was some logic that I regarded as faulty in the previous spline calculation code - for instance, this:

float currCarSplineProgress = currCarDistance / overallDistance;

I've changed to this:

float currCarSplineProgress = (currCarDistance) / currSplineLength;

The idea of that part is to check the car's progress on the current curve it is traveling close to in the overall spline, and position the spline walker accordingly so that it jumps ahead to the next spline when needed. Here's the full updated code:

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;

Yet, unexpected things STILL happen when the car makes enough progress - the spline walker will just suddenly jump to one of the previous sections!

enter image description here

Any insights?

Kieran Ojakangas
  • 495
  • 4
  • 18

1 Answers1

0

You have the right idea for making the AI chase a target moving along the spline. I believe that's how most AI in racing games work.

To ensure the target is always ahead of the AI, set the target's position to the AI's position on the spline added by some value related to how fast the AI is moving.

I would suggest checking out the Unity Standard Assets package:

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

There is a car AI system in there that works by following a target that is moving along a spline.

Ian094
  • 21
  • 3
  • Hey, thanks for the reply. I get that the waypoint AI would work since it follows the target. I guess the trick is that the spline walker risks getting BEHIND the AI car or at the same position due to the current way I'm trying to calculate/translate the spline walker's position. – Kieran Ojakangas Dec 09 '19 at 16:45