Racing game AI question

Hello,

I have looked around for Enemy racing AI examples but couldn’t really find a good solution to my problem.

Here is an example image of what I want to archive:

I have set up a simple waypoint system but I was wondering how you can detect when the car has to steer to the left/right for example above. The car should automaticly detect when it should steer to the right to the next waypoint.

I have both the location/rotation values from the car/waypoint but I just have no idea where to start. Does anyone have any tips for this?

I have simple steer mechanics for the wheel colliders like this:

if (axleInfo.steering) {
	axleInfo.leftWheel.steerAngle = steering;
	axleInfo.rightWheel.steerAngle = steering;
}
if (axleInfo.motor) {
	axleInfo.leftWheel.motorTorque = -motor;
	axleInfo.rightWheel.motorTorque = -motor;
}

I just have to detect whether it has to steer to the left or right to reach the waypoint.

I hope someone can help me further with this problem.

Hi there! You can get the signed angle between two 2D vectors from the following (untested, let me know if it works):

float Signed2DAngle(Vector2 fwd, Vector2 nFwd){
	float dot = fwd.x * nFwd.y – fwd.y * nFwd.x;
	return Mathf.Atan2(dot, Vector2.Dot(fwd, nFwd))*Mathf.Rad2Deg;
}

However, that will just steer you onto the straight line to the next waypoint, that might be absolutely fine, especially if your waypoints are close together and only have small differences for the turns each time.

Another method would be to look at the next 2 waypoints, and make your direction change based on some bezier curve that you pass through your current position, the immediately next waypoint, and the waypoint just after!

This is also untested, and basically all guesswork as I do not have the time to setup a proper system to test on, however I will provide some code in the hope it will help as a starting point!

Vector2 position; //the current position of the AI car
Vector2 waypoint; //the next waypoint (your destination)
Vector2 nextWaypoint; //the waypoint after next, to know in which direction we are hoping to leave the next waypoint
Vector2 velocity; //The current HORIZONTAL (dont include any vertical motion) velocity (speed and direction) of our AI car

void Update () {
	steering = GetSteeringAngle(position, waypoint, nextWaypoint, velocity);
}

float GetSteeringAngle(Vector2 p, Vector2 w1, Vector2 w2, Vector2 v){

	//Get the 'size' of the velocity vector to know how far we travel each second
	float speed = v.magnitude;

	//Get control point of the bezier curve
	Vector2 ctrlPoint = GetControlPoint(p, w1, w2);

	//get direction and distance from our current position to the control point
	Vector2 fwdDir1 = ctrlPoint-p;

	//find a point along fwdDir1
	//by 'travelling' along it by (speed*Time.deltaTime) amount
	Vector2 newPos1 = p + (fwdDir1*speed*Time.deltaTime);

	//find a point along the line from the control point to w2 (the waypoint after next)
	// we travel along this line by the same ratio (speed*Time.deltaTime)
	Vector2 newPos2 = ctrlPoint+((w2-ctrlPoint)*speed*Time.deltaTime);

	//Get the direction between these two new points
	Vector2 fwdDir2 = (newPos2-newPos1).normalized;

	//get the average forward direction
	//this was a complete guess to be honest, and I was not sure it would actually work
	//but I thought it might be sensible as our speed and other variables might change,
	//and so averaging between the actual direction and the next might help
	fwdDir1 = (fwdDir1.normalized + fwdDir2)/2f;

	//we have our desired forward direction and our current direction,
	//hence find the angle change to adjust!
	return Signed2DAngle(v, fwdDir1);
}

Vector2 GetControlPoint(Vector2 p, Vector2 w1, Vector2 w2){
	//get the average of our start and end points
	//
	//
	//    ______
	//  /		\
	// |	x	 |

	Vector2 midPoint = (p+w2)/2f;

	//w1 is a point on our curve,
	//however bezier curves are controlled by a control point which is not on the curve
	//fortunately given a control point 'p', we can work out from how bezier's are constructed,
	//that a point halfway between 'p' and our average 'midPoint', is a point on our curve

	//From that we can work the other way, hence if we want w1 to be on our curve
	//We find the direction from the mid point to it, and then times by 2 to get our control point


	//    _____(w1)
	//  /	  /\
	// |	x/   |

	//			(control point)
	//			/
	//    ______/
	//  /	  /\
	// |	x/   |

	//hence our equation is:
	//w1 + (w1-midPoint)
	//which is 2*w1 - midPoint

	return (2*w1-midPoint);
}

//Gets the signed (-/+) angle off from our fwd direction
float Signed2DAngle(Vector2 fwd, Vector2 nFwd){
	//(nFwd.y, -nFwd.x) is a vector perpedicular to nFwd
	//hence this is the dot product between fwd and a vector at right angles to nFwd
	float dot = (fwd.x * nFwd.y) - (fwd.y * nFwd.x);

	//the two dot products act as the relative length of sides of a right-angled triangle, and a little trigonometry therefore gives the angle of their incidence.
	//Mathf.Atan2 returns the answer in radians, hence we must multiply by 180/PI (Mathf.Rad2Deg) to get degrees
	return Mathf.Atan2(dot, Vector2.Dot(fwd, nFwd))*Mathf.Rad2Deg;
}

Maybe that will help :slight_smile:

Scribe