- Home /
How To Add AI To Car/Racer?
As a school project, my class is attempting to mod the unity car tutorial. For a week I have been struggling to get working AI cars which will simply drive around the track. I have found a few tutorials online that deal with this, but none seem to work in Unity3. All I want is for the car to follow the series of waypoints and still react to normal physics (like colliding with another car). I've borrowed the script from the old racer tutorial:
// Variables defined outside of functions appear in the inspector // and can be changed there without having to change the script var frontLeftWheel : Transform; var frontRightWheel : Transform; var backLeftWheel : Transform; var backRightWheel : Transform; var wheelForce = 120.0; //var lookAtPoint : Transform;
// All these variables are only used internally, thus we make them private private var hasWheelContact = false; private var steerMaxAngle = 40.0; 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 (1, 1, 2) * rigidbody.mass;
}
function UpdateWithTargetPosition (target : Vector3) {
// Calculate the target position relative to the this transforms coordinate system.
// eg. a positive x value means the target is to the right of the car,
// a positive z means the target is in front of the car
relativeTarget = transform.InverseTransformPoint (target);
// Calculate the target angle for the wheels, so they point towards the target
targetAngle = Mathf.Atan2 (relativeTarget.x, relativeTarget.z);
// Atan returns the angle in radians, convert to degrees
targetAngle *= Mathf.Rad2Deg;
// The wheels have a maximum rotation angle
targetAngle = Mathf.Clamp (targetAngle, -steerMaxAngle, steerMaxAngle);
// Apply the rotation to the wheels
// We want the wheels to 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);
rigidbody.drag = 0;
if (hasWheelContact)
{
// Accelerate
//force = maxSpeed * force;
rigidbody.AddRelativeForce (0, 0, wheelForce);
// We are too fast and need to turn too much. Slow down!
if (Mathf.Abs (targetAngle) > 15 && rigidbody.velocity.magnitude > 10) {
// We are too fast
rigidbody.drag = 4;
}
}
// This is handy for debug visualizing where we actually want to drive
Debug.DrawLine (transform.position, target);
// This is reset every frame. OnCollisionStay enables it again.
hasWheelContact = false;
}
function Update () {
//lookAtPoint = activeWayPoint.Transform;
//transform.LookAt(lookAtPoint);
//}
//function Update() { // 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) hasWheelContact = true; if (p.thisCollider.transform == frontRightWheel) hasWheelContact = true; if (p.thisCollider.transform == backLeftWheel) hasWheelContact = true; if (p.thisCollider.transform == backRightWheel) hasWheelContact = true; } }
This seems like it's trying to work, but as soon as the car reaches the first waypoint it starts flipping through the air wildly and acting unpredictably. My waypoints have box colliders set as triggers and this script:
// The start waypoint, this is initialized in Awake. // This variable is static thus all instances of the waypoint script share it. static var start : WayPoint;
// The next waypoint, this variable needs to be assigned in the inspector. // You can select all waypoints to see the full waypoint path. var next : WayPoint;
// This is used to determine where the start waypoint is. var isStart = false;
// Returns where the AI should drive towards. // position is the current position of the car. function CalculateTargetPosition (position : Vector3) {
// If we are getting close to the waypoint, we return the next waypoint.
// This gives us better car behaviour when cars don't exactly hit the waypoint
if (Vector3.Distance (transform.position, position) < 6) {
return next.transform.position;
}
// We are still far away from the next waypoint, just return the waypoints position
else {
return transform.position;
}
}
// This initializes the start and goal static variables. // We have to do this inside Awake because the waypoints need // to be initialized before the AI scripts use it. // All Awake function are always called before all Start functions. function Awake () { if (!next) Debug.Log ("This waypoint is not connected, you need to set the next waypoint!", this);
if (isStart)
start = this;
}
// Draw the waypoint pickable gizmo function OnDrawGizmos () { Gizmos.DrawIcon (transform.position, "Waypoint.tif"); }
// Draw the waypoint lines only when you select one of the waypoints function OnDrawGizmosSelected () { if (next) { Gizmos.color = Color.green; Gizmos.DrawLine (transform.position, next.transform.position); } }
I'm new at this, so the simplest bare-bones method to do this is all I'm after.. just a car or two to race around the track. Thanks for any help!
Answer by duck · Nov 20, 2010 at 11:35 AM
Perhaps the problem is that you have too few waypoints, resulting in a large sudden change in direction as you reach one and start heading to the next.
The way I tend to do this is to have lots of waypoints (more than it would be practical to manually assign each one's "next" reference).
I create them using a spacing tool along a spline so that you have 100 or more waypoints around the track, and each one is no more than a few metres apart.
If you make sure that these waypoint objects are named sequentially, eg: "trackmarker 0" - "trackmarker 100", rather than assigning the order manually in the editor, you can collect all the references in code, via a loop like this:
using UnityEngine; using System.Collections; using System.Collections.Generic;
public class Track : MonoBehaviour {
public static Vector3[] routePoints;
void Start() {
List<Vector3> collectPoints = new List<Vector3>();
bool found = true;
int num = 1;
while (found) {
GameObject obj = GameObject.Find("trackmarker "+num);
if (obj != null)
{
collectPoints.Add(obj.transform.position);
obj.active = false;
} else {
found = false;
}
}
routePoints = collectPoints.ToArray();
}
}
You then have a fairly detailed representation of the curve of the track, as an array of Vector3s.
The next trick is to not use the 'trigger' method of checking whether you reached a waypoint, but rather use vector maths. The reason for this is that you want your target waypoint to always be a certain distance ahead of your car - and that distance should be based on how fast it's travelling. I.E. if the car is travelling very fast, it should be looking much further along the waypoints than its current position.
I don't have time to add further detail to this question right now, but it's something I spend a lot of time on so I'll come back soon to flesh this out a bit!
Hey thanks a lot, it's been a long time since I asked this, and to be honest at the time the answer was beyond my means.. Now that I've got a little time to play, I've been looking back over some old stuff.. So now, finally, I'm ready to start attempting this method, to refine my old build, which looked ok but didn't really work great.. Thanks!
Answer by Seth-Bergman · Nov 20, 2010 at 10:57 AM
Update: By lowering the y value for centerOfMass to -1 or so the car no longer flies. It still jerks around a lot, but does make it around the track. I'm thinking now it's just a question of tweaking everything.. Still would appreciate any suggestions for refining this!
Answer by Mentalist4006 · Jan 11, 2011 at 03:27 AM
I had the same exact question as you did, and it was answered here. I believe it should work.