Racing Game AI Problems - Desperate for help.

Using a waypoint system to create an AI racing agent. Calculating which way to drive towards works fine, but turns never work. The force being applied to the Z axis always seems to be off. The vehicle is unicycle, one wheel split into two pieces with a wheel collider on each side. Mesh collider on the track. Two things I need to figure out is:

  1. How can I calculate how much Z force is needed relative to the degree of a turn given the force forward is a constant speed.

  2. How can I create boundaries on my track? Tried tightening subdivisions of mesh on borders, as well as breaking borders up into separate pieces and adding separate mesh colliders to each. (Straight away works, curves do not, character flies right off the map!)

Any help is appreciated, kind of desperate at this point. (Sadly. it’s been two weeks non-stop working on this problem)

Thank You,

  • The Novice Scripter
var frontLeftWheel : Transform;
var frontRightWheel : Transform;
var wheelForce = .10;
private var hasWheelContact = false;
private var steerMaxAngle = 20;
private var activeWayPoint : WayPoint;

function Start () {
	// Initialize the waypoint we drive towards
	activeWayPoint = WayPoint.start;

	// Tweak the center of mass.
	// - Low center of mass a bit towards the front
	// - model a long long and not very high car
	rigidbody.centerOfMass = Vector3 (0, 0, 0);
	rigidbody.inertiaTensorRotation = Quaternion.identity;
	rigidbody.inertiaTensor = Vector3 (2, 1, 1) * rigidbody.mass;
}

function UpdateWithTargetPosition (target : Vector3) {
	
	// Calculate the target position relative to the this transforms coordinate system.
	relativeTarget = transform.InverseTransformPoint (target);
	
	// Calculate the target angle for the wheels, so they point towards the target
	targetAngle = Mathf.Atan2 (-relativeTarget.z, relativeTarget.x);

	// Atan returns the angle in radians, convert to degrees
	targetAngle *= Mathf.Rad2Deg;
	
	// The wheels have a maximum rotation angle
	targetAngle = Mathf.Clamp (steerMaxAngle, -steerMaxAngle, targetAngle);
	
	// Apply the rotation to the wheels
	// Wheels rotate around the y-axis
	// The rotation has to be relative to the car, which is the transform parent of the wheels
	frontLeftWheel.localEulerAngles = Vector3 (0, targetAngle, 0);
	frontRightWheel.localEulerAngles = Vector3 (0, targetAngle, 0);
	
	var slope = (Vector3.Distance (transform.position, target));
	//print("slope/distance " + slope + " targetAngle " + targetAngle);
	
	if (hasWheelContact)
	{
		// Accelerate
		transform.Translate (0, 0, wheelForce);
		
		var ZForce = ((slope/2)/500);
		print("ZForce " + ZForce);
		transform.Translate (ZForce , 0, 0);
		
		// We are too fast and need to turn too much. Slow down.
		if (Mathf.Abs (targetAngle) > 30  rigidbody.velocity.magnitude > .09) {
			// We are too fast
			rigidbody.drag = 10;
		}
	}
	
	// This is handy for debug visualizing where we actually want to drive
	 Debug.DrawLine (transform.position, target);
	
	// This is reset every frame. OnCosllisionStay enables it again.
	hasWheelContact = false;
}

function FixedUpdate () {
	// Calculate the position the ai car should drive towards
	targetPosition = activeWayPoint.CalculateTargetPosition (transform.position);
	// Apply forces, steer the wheels
	UpdateWithTargetPosition (targetPosition);
}

// Whenever we hit a waypoint, skip forward to the next way point
function OnTriggerEnter (triggerWaypoint : Collider) {
	if (activeWayPoint.collider == triggerWaypoint) {
		activeWayPoint = activeWayPoint.next;
	}
}

// Track if we the wheels are grounded
function OnCollisionStay (collision : Collision) {
	for (var p : ContactPoint in collision.contacts) {
		if (p.thisCollider.transform == frontLeftWheel)
     		Debug.DrawRay(p.point, p.normal, Color.red);
			hasWheelContact = true;
		if (p.thisCollider.transform == frontRightWheel)
            Debug.DrawRay(p.point, p.normal, Color.red);
			hasWheelContact = true;
	}
}

I apologize for not being more specific. Our track starts out as a
straight away, then curves to the right into a downward spiral leading
to another straight path. The agent travels along the straight path
fine as the forward force is a constant speed, the agent never tips
over, or leans to any direction but forward.

The agent approaches the turn, curves his wheels in the direction of
the turn (right) but does not travel to the right, instead traveling
straight. I realized this was because there was not any force acting
on the Z axis.

I basically need a formula to calculate the amount of force to apply
to the Z axis relevant to the degree of a turn. I figure the slope
will always be a straight line because his forward speed is constant,
and this slope would be the distance from the agent to the waypoint. I
am trying to find out the run needed and to apply it to the Z axis.

Plugging in a test number yielded promising result as he made the
right turn but ended up sliding to the outside of the turn; I think
this is due to centripetal acceleration. Therefore I also want to
implement a way to add the appropriate amount of drag to slow him down
enough to make a sharp turn.

All of this should update every frame. Hopefully, once a solution is
found, this will mean there will be little to no Z force applied to
the agent during straight paths, then as a turn approaches, the force
on the Z axis will be sufficient enough to bring him around the turn.
Drag would slow him down if needed.

You can apply numerous AI designs to your issue.

The old car tutorial assumed that the waypoint would trigger the change of variables on your agent and it used Drag to manage your max velocity with a max turn value.

Another more basic example (more level work):
For example, you could use three way points to manage a turn.

Waypoint 1 1- Set a lower velocity (will decrease velocity of current rate)
(start of turn) 2- Set the new waypoint target to Waypoint 2

Waypoint 2 1- Set a higher velocity (power out of turn)
2- Set the new waypoint target to Waypoint 3

Waypoint 3 … Set for next waypoint.

This is just a suggested design based on your question being about waypoints.

If you want a specific algorithm on handling turns, check out OpenSteer for ideas. It uses rays to determine distance and collision avoidance, based on the determined distance you can apply an average speed.

You can apply numerous AI designs to your issue.

The old car tutorial assumed that the waypoint would trigger the change of variables on your agent and it used Drag to manage your max velocity with a max turn value.

Another more basic example (more level work):
For example, you could use three way points to manage a turn.

Waypoint 1 1- Set a lower velocity (will decrease velocity of current rate)
(start of turn) 2- Set the new waypoint target to Waypoint 2

Waypoint 2 1- Set a higher velocity (power out of turn)
2- Set the new waypoint target to Waypoint 3

Waypoint 3 … Set for next waypoint.

This is just a suggested design based on your question being about waypoints.

If you want a specific algorithm on handling turns, check out OpenSteer for ideas. It uses rays to determine distance and collision avoidance, based on the determined distance you can apply an average speed.

You’ll need separate primitive colliders to do this right. A mesh collider won’t work except a slow speeds. For example, you attach a box collider that has a thickness greater than the distance your car can travel in one frame.

What happens with higher speeds and a mesh collider, is your car is going fast enough that it translates past your mesh in a single frame, so no collision is registered.

Hi Quietus,

I am not a scripting expert either, but I did notice that you are using " transform.Translate (0, 0, wheelForce); " to accelerate. Translate does not care about force–it will always make you go straight. You will want to add a force, like “rigidbody.AddForceAtPosition”, which is what I use. I want it “at a position” because it causes a torque, which you may not like. So, you should just add force. I hope this helps.

–JJ