- Home /
unitySteer + path for racing AI
Hello all,
I have a game that is using "canyon racer" scenario. Essentially vehicles are racing through valleys to get to checkpoints. After a player has reached all the checkpoints a lap will have been completed. I am using UnitySteer and AngryAnt's path to provide the AI. Everything is working great except for two issues:
the first is that once the vehicle has completed the first lap, he is "turned loose" from path and just flies in a straight line (eventually blowing up). What I need is an event that will tell me when my vehicle has completed the path given to it, so that it can cycle to the next destination and continue on through the race until all the laps have been completed.
The second issue is that have no idea how to setup the "pathfollower" vehicle in unitysteer, to better avoid obstacles or adhere to the path. I see the "weights" and have messed around with them (I don't know what the max/min values should be, I assume 0-1). I can tell a difference with the "pathFollowWeight" but not with the "obstacleAvoidanceWeight". I am using a slightly modified version of the script given here: http://answers.unity3d.com/questions/254/unitysteer-and-path only without the use of the "IRadarReceiver" (it was causing to many compile errors, and i don't know how to implement it correctly).
Any help would be greatly appreciated, even if it is just a link that will point me in the right direction. Thanks!
Answer by Ricardo · Mar 18, 2010 at 10:21 PM
the first is that once the vehicle has completed the first lap, he is "turned loose" from path and just flies in a straight line (eventually blowing up).
Is it possible that you have StopOnArrival set to false?
What I need is an event that will tell me when my vehicle has completed the path given to it, so that it can cycle to the next destination and continue on through the race until all the laps have been completed.
I'd recommend declaring your own event, adding a delegate on the MonoBehavior which registers to it, then invoking them here. You'll see the vehicle is really simple, it should be easy to extend.
The second issue is that have no idea how to setup the "pathfollower" vehicle in unitysteer, to better avoid obstacles or adhere to the path.
Regarding obstacle avoidance, the PathFollower does a very basic one. You may of course want to tweak it, as it got only rudimentary testing. It also bears saying that better avoidance will depend entirely on your scenario.
The three major parameters are:
- NeighborAvoidanceWeight: How much weight to give to the forces that would steer it away from neighbors.
- ObstacleAvoidanceWeight: How much weight to give to the forces that would steer it away from obstacles.
- PathFollowWeight: How much weight to give to the forces that aim to keep the vehicle on the path.
The values are multipliers clamped between 0 and float.MaxValue, so you could (for instance) set ObstacleAvoidance on 0.5f, NeighborAvoidance on 1 and PathFollow on 1.5f. As you can see on the lines preceding this one, the multipliers are then used before the steering force is applied.
Thank you for your help. the weight system makes much more sense now. I was able to get the event handler working and can now fire off code once the path has been completed. I tried setting the "StopOnArrival" both ways and it makes no difference. After the path is completed, i call "goto( destination )" (to try and restart the lap) and i just get hit with "onsearchFailed". So I have no clue how to keep the ship making laps around the track. thanks again for your help.
It'll be difficult to help with that without seeing your code. If you can post a sample project somewhere, I'd be glad to take a look at it.
Answer by n8 · Mar 21, 2010 at 09:04 PM
ok so i finally found the answer to my question about how to keep my ship making laps around the track. There is a line where you instantiate the "AngryAntPathway" object, and pass in the constructer's parmeters. the last parameter passed is for the "cyclic" option. This has been set to "false" (i didn't initially even know this existed), once I set it to "true" my ship will effortlessly make laps around the track! Here is the script that I have been using:
using UnityEngine; using System.Collections; using PathLibrary; using UnitySteer; using UnitySteer.Vehicles;
public class AiControl : MonoBehaviour, ISearchMonitor{ //, IRadarReceiver (I don't know how to implement the Radar yet)
protected ArrayList path; protected Seeker seeker; private PathFollower shipVehicle; //~ private bool Moving = false; private Vector3 destination; private int currWaypoint = 4; private bool canContinue = false; private int timesCalled = 0;
public Transform[] waypoints;
public float radius = 5f; public float maxSpeed = 5f; public float maxForce = 5f; public float pathFollowWeight = 1; public float obstacleAvoidanceWeight = 1; public float neighborAvoidanceWeight = 1; public float minCollisionTime = 1; public float maxFrameTime = 0.1f; public Vector3 StartDest;
// Use this for initialization void Start () { shipVehicle = new PathFollower(this.transform, 1, null, radius); //~ shipVehicle.MovesVertically = false; shipVehicle.MinCollisionTime = 2; shipVehicle.MaxSpeed = maxSpeed; shipVehicle.Mass = 0.1f; shipVehicle.MaxForce = maxForce; shipVehicle.AvoidDeg = 30; shipVehicle.PathFollowWeight = pathFollowWeight; shipVehicle.ObstacleAvoidanceWeight = obstacleAvoidanceWeight; shipVehicle.NeighborAvoidanceWeight = neighborAvoidanceWeight; shipVehicle.MinCollisionTime = minCollisionTime; shipVehicle.stopOnArrival = false;
//~ StartDest = GameObject.FindWithTag("FinishPoint").transform.position;
destination = waypoints[currWaypoint].transform.position;
destination.y = 20;
//~ GoTo(destination);
}
// Update is called once per frame void Update () { Vector3 target, lineOfSightTarget; ConnectionAsset currentConnection, connection, lastConnection;
if(seeker == null)
{
//~ Debug.Log("Seeker is null");
//~ Debug.Log("waypoints: " + waypoints.length());
}
if (path == null){
//~ Debug.Log("path is null");
GoTo(destination);
return;
}
if( path.Count > 0 )
// Miles and miles to go...
{
currentConnection = ( ConnectionAsset )path[ 0 ];
lastConnection = ( ConnectionAsset )path[ path.Count - 1 ];
// Draw the current path //
Debug.DrawLine( transform.position, currentConnection.To.AbsolutePosition, Color.white );
for( int i = 1; i < path.Count; i++ )
{
connection = ( ConnectionAsset )path[ i ];
Debug.DrawLine( connection.From.AbsolutePosition, connection.To.AbsolutePosition, Color.white );
}
Debug.DrawLine( lastConnection.To.AbsolutePosition, destination, Color.white );
if( Vector3.Angle( transform.forward, currentConnection.To.AbsolutePosition - transform.position ) < 90.0f )
// If the next node is in front of us...
{
target = transform.position + Vector3.Project( currentConnection.To.AbsolutePosition - transform.position, transform.forward * ( currentConnection.To.AbsolutePosition - transform.position ).magnitude );
// ... see if we can remain on course
if( !currentConnection.To.ContainsPoint( target ) )
// ... if we can't ...
{
target = currentConnection.To.GetNearestPoint( transform.position, radius );
// ... set target to the nearest point in the node we're approaching
}
}
else
// Otherwise, just find the nearest point
{
target = currentConnection.To.GetNearestPoint( transform.position, radius );
}
}
else
// Home stretch!
{
target = destination;
// Set the target to out destination
}
if(shipVehicle.arrived)
{
Debug.Log("Arrived");
if(canContinue)
{
Debug.Log("Getting next destination");
canContinue = false;
path = null;
}
}
shipVehicle.Update(Time.deltaTime);
}
public void GetNextDest(){ Debug.Log("Times Called: " + (++timesCalled )); if(currWaypoint < waypoints.Length - 1){ currWaypoint++;// = 0; Debug.Log("Next waypoint: " + currWaypoint); }else { currWaypoint = 0;//++; Debug.Log("Next waypoint (back to zero): " + currWaypoint); } Debug.Log("currWaypoint: " + currWaypoint); destination = waypoints[currWaypoint].transform.position; destination.y = 20; Debug.Log("GoToing: " + destination); seeker.Kill(); path = null; //~ GoTo( destination ); //~ Start(); }
public void GoTo(Vector3 destination) { seeker = new Seeker( transform.position, destination, maxFrameTime, radius, null ); if( seeker.Start != null && seeker.End != null ) { //~ Debug.Log("start & end != null"); shipVehicle.IsMoving = true; seeker.AddMonitor(this); Control.Instance.StartSeeker( seeker ); } }
public void OnSearchCompleted( Seeker seeker ) { //~ Debug.Log("Search Complete!"); if( seeker != this.seeker ) { Debug.LogError( "OnSearchCompleted: Not my seeker!" ); } //~ this.seeker = null; this.path = seeker.Solution; Debug.Log( path.Count ); this.destination = seeker.To;
if (this.path == null)
{
Debug.Log("path is still null");
return;
}
if (seeker.Start != seeker.End)
{
shipVehicle.Pathway = new AngryAntPathway(path, 0.25f, true);
}
else
{
shipVehicle.IsMoving = false;
}
canContinue = true;
}
public void OnSearchFailed( Seeker seeker ) { if( seeker != this.seeker ) { Debug.LogError( "OnSearchCompleted: Not my seeker!" ); return; }
Debug.LogError( "Search failed" );
this.seeker.Kill();
this.seeker = null;
}
public void OnSeekerInvalidated( Seeker seeker ) { if( seeker != this.seeker ) { Debug.LogError( "OnSearchCompleted: Not my seeker!" ); return; }
//Debug.Log( "Seeker invalidated" );
this.path = null;
GoTo( seeker.To );
// Narrow escape from whereever we are. We don't want to cache this.
}
}
Please keep in $$anonymous$$d that this script is in a "just get things working" state. There is a lot of code that could (and should) easily removed.
Getting this Error:
Assets/_AI/AiControl.cs(3,7): error CS0246: The type or namespace name `PathLibrary' could not be found. Are you missing a using directive or an assembly reference?
Is there something I'm missing? What are requirements to run this code? What should be imported into project and where? Also this code should be placed on object which should be controled? Any others scripts should be attached? Thank you.
I'm getting the same error and cupsster. Also, what does ISearch$$anonymous$$onitor look like?