Search Unity

UnitySteer - steering library for Unity

Discussion in 'Scripting' started by Arges, Jul 9, 2009.

  1. Payback

    Payback

    Joined:
    Oct 7, 2012
    Posts:
    2
    I was using steer for a while, but decided that I didn't need the flocking and instead just went with the basic seeker provided by a* path. This code is a bridge between the two assets. It takes an A* Path and generates a steer that will follow the path


    Convert an AStar path to a steer Pathway
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using Pathfinding;
    5.  
    6. namespace UnitySteer
    7. {
    8.  
    9.     public class AStarPathway : Pathway {
    10.        
    11.         int pointCount;
    12.         Vector3[] points;
    13.         float radius;
    14.  
    15.         float segmentLength;
    16.         float segmentProjection;
    17.         Vector3 chosen;
    18.         Vector3 segmentNormal;
    19.  
    20.         float[] lengths;
    21.         Vector3[] normals;
    22.         float totalPathLength;
    23.        
    24.    
    25.         public AStarPathway () {
    26.            
    27.         }
    28.        
    29.         // construct an AStarPathway given the number of points (vertices),
    30.         // an array of points, and a path radius.
    31.         public AStarPathway (Path inPath, float _radius, bool _cyclic) {
    32.             initialize (inPath, _radius, _cyclic);
    33.         }
    34.        
    35.         // construct a AStarPathway given a path (vertices),
    36.         // an array of points, and a path radius.
    37.         void initialize (Path inPath, float _radius, bool _cyclic)
    38.         {
    39.             Vector3[] _points = inPath.vectorPath;
    40.            
    41.             // set data members, allocate arrays
    42.             radius = _radius;
    43.             isCyclic = _cyclic;
    44.             pointCount = _points.Length;
    45.             totalPathLength = 0;
    46.             if (isCyclic)
    47.                 pointCount++;
    48.            
    49.             lengths = new float [pointCount];
    50.             points  = new Vector3 [pointCount];
    51.             normals = new Vector3 [pointCount];
    52.  
    53.             // loop over all points
    54.             for (int i = 0; i < _points.Length; i++)
    55.             {
    56.                 // copy in point locations, closing cycle when appropriate
    57.                 bool closeCycle = isCyclic  (i == pointCount-1);
    58.                 int j = closeCycle ? 0 : i;
    59.                 points[i] = _points[j];
    60.  
    61.                 // for the end of each segment
    62.                 if (i > 0)
    63.                 {
    64.                     // compute the segment length
    65.                     normals[i] = points[i] - points[i-1];
    66.                     lengths[i] = normals[i].magnitude;
    67.  
    68.                     // find the normalized vector parallel to the segment
    69.                     normals[i] *= 1 / lengths[i];
    70.  
    71.                     // keep running total of segment lengths
    72.                     totalPathLength += lengths[i];
    73.                 }
    74.             }
    75.         }
    76.        
    77.        
    78.         // utility methods
    79.  
    80.  
    81.  
    82.         // assessor for total path length;
    83.         protected override float GetTotalPathLength()
    84.         {
    85.             return totalPathLength;
    86.         }
    87.  
    88.         /// <summary>
    89.         /// Returns the first point along the path
    90.         /// </summary>
    91.         /// <remarks>
    92.         /// Descendents are expected to override this method
    93.         /// </remarks>
    94.         protected override Vector3 GetFirstPoint()
    95.         {
    96.             return points[0];
    97.         }
    98.                
    99.         /// <summary>
    100.         /// Returns the last point along the path
    101.         /// </summary>
    102.         /// <remarks>
    103.         /// Descendents are expected to override this method
    104.         /// </remarks>
    105.         protected override Vector3 GetLastPoint()
    106.         {
    107.             return points[points.Length -1];
    108.         }
    109.  
    110.  
    111.         public override Vector3 mapPointToPath(Vector3 point, ref mapReturnStruct tStruct)
    112.         {
    113.             float d;
    114.             float minDistance = float.MaxValue;// FLT_MAX;
    115.             Vector3 onPath=Vector3.zero;
    116.  
    117.             // loop over all segments, find the one nearest to the given point
    118.             for (int i = 1; i < pointCount; i++)
    119.             {
    120.                 segmentLength = lengths[i];
    121.                 segmentNormal = normals[i];
    122.                 d = pointToSegmentDistance (point, points[i-1], points[i]);
    123.                 if (d < minDistance)
    124.                 {
    125.                     minDistance = d;
    126.                     onPath = chosen;
    127.                     tStruct.tangent = segmentNormal;
    128.                 }
    129.             }
    130.  
    131.             // measure how far original point is outside the Pathway's "tube"
    132.             tStruct.outside = (onPath - point).magnitude - radius;
    133.  
    134.             // return point on path
    135.             return onPath;
    136.         }
    137.  
    138.         public override float mapPointToPathDistance(Vector3 point)
    139.         {
    140.             float d;
    141.             float minDistance = float.MaxValue;
    142.             float segmentLengthTotal = 0;
    143.             float pathDistance = 0;
    144.  
    145.             for (int i = 1; i < pointCount; i++)
    146.             {
    147.                 segmentLength = lengths[i];
    148.                 segmentNormal = normals[i];
    149.                 d = pointToSegmentDistance (point, points[i-1], points[i]);
    150.                 if (d < minDistance)
    151.                 {
    152.                     minDistance = d;
    153.                     pathDistance = segmentLengthTotal + segmentProjection;
    154.                 }
    155.                 segmentLengthTotal += segmentLength;
    156.             }
    157.  
    158.             // return distance along path of onPath point
    159.             return pathDistance;
    160.         }
    161.  
    162.         public override Vector3 mapPathDistanceToPoint(float pathDistance)
    163.         {
    164.             // clip or wrap given path distance according to cyclic flag
    165.             float remaining = pathDistance;
    166.             if (isCyclic)
    167.             {
    168.                 remaining = (float)System.Math.IEEERemainder(pathDistance, totalPathLength);
    169.             }
    170.             else
    171.             {
    172.                 if (pathDistance < 0) return points[0];
    173.                 if (pathDistance >= totalPathLength) return points [pointCount-1];
    174.             }
    175.  
    176.             // step through segments, subtracting off segment lengths until
    177.             // locating the segment that contains the original pathDistance.
    178.             // Interpolate along that segment to find 3d point value to return.
    179.             Vector3 result=Vector3.zero;
    180.             for (int i = 1; i < pointCount; i++)
    181.             {
    182.                 segmentLength = lengths[i];
    183.                 if (segmentLength < remaining)
    184.                 {
    185.                     remaining -= segmentLength;
    186.                 }
    187.                 else
    188.                 {
    189.                     float ratio = remaining / segmentLength;
    190.                     result = Vector3.Lerp(points[i - 1], points[i], ratio);
    191.                     break;
    192.                 }
    193.             }
    194.             return result;
    195.         }
    196.        
    197.         private int MapPointToPathNode(Vector3 currentPosition) {
    198.            
    199.             float d;
    200.             float minDistance = float.MaxValue;// FLT_MAX;
    201.             int bestPoint = -1;
    202.            
    203.             // loop over all segments, find the one nearest to the given point
    204.             for (int i = 1; i < pointCount; i++)
    205.             {
    206.                 segmentLength = lengths[i];
    207.                 segmentNormal = normals[i];
    208.                 d = pointToSegmentDistance (currentPosition, points[i-1], points[i]);
    209.                 if (d < minDistance) {
    210.                     minDistance = d;
    211.                     bestPoint = i;
    212.                 }
    213.             }
    214.            
    215.             // return point on path
    216.             return bestPoint;
    217.         }
    218.        
    219.         public Vector3 NextPathPoint(Vehicle vehicle) {
    220.            
    221.             Vector3 currentPosition = vehicle.Position;
    222.             int pointNode = MapPointToPathNode(currentPosition);
    223.            
    224.             if (pointNode == -1)
    225.                 return Vector3.zero;
    226.            
    227.             int bestNode = -1;
    228.             float bestDist = Mathf.Infinity;
    229.             for (int i = pointNode; i < pointCount; i++) {
    230.                 float dist = (points[i] - currentPosition).magnitude;
    231.                
    232.                 if (dist < bestDist  dist > vehicle.Radius) {
    233.                     bestDist = dist;
    234.                     bestNode = i;
    235.                 }
    236.             }
    237.            
    238.             if (bestNode != -1)
    239.                 return points[bestNode];
    240.             else
    241.                 return Vector3.zero;
    242.         }
    243.  
    244.  
    245.         // ----------------------------------------------------------------------------
    246.         // computes distance from a point to a line segment
    247.         //
    248.         // cwr: (I considered moving this to the vector library, but its too
    249.         // tangled up with the internal state of the AStarPathway instance)
    250.         //
    251.         // RJM: Look into OpenSteerUtility for a version  of this method without
    252.         // the entangled local state
    253.         //
    254.         float pointToSegmentDistance (Vector3 point, Vector3 ep0, Vector3 ep1)
    255.         {
    256.             // convert the test point to be "local" to ep0
    257.             Vector3 local = point - ep0;
    258.  
    259.             // find the projection of "local" onto "segmentNormal"
    260.             segmentProjection = Vector3.Dot(segmentNormal, local);
    261.  
    262.             // handle boundary cases: when projection is not on segment, the
    263.             // nearest point is one of the endpoints of the segment
    264.             if (segmentProjection < 0)
    265.             {
    266.                 chosen = ep0;
    267.                 segmentProjection = 0;
    268.                 return (point- ep0).magnitude;
    269.             }
    270.             if (segmentProjection > segmentLength)
    271.             {
    272.                 chosen = ep1;
    273.                 segmentProjection = segmentLength;
    274.                 return (point-ep1).magnitude;
    275.             }
    276.  
    277.             // otherwise nearest point is projection point on segment
    278.             chosen = segmentNormal * segmentProjection;
    279.             chosen +=  ep0;
    280.             return Vector3.Distance(point, chosen);
    281.         }
    282.  
    283.        
    284.     }
    285.    
    286. }
    287.  
    A steer that follows a steer path
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using UnitySteer;
    5.  
    6. /// <summary>
    7. /// Steers a vehicle to follow a path
    8. /// </summary>
    9. public class SteerForWalkPath : Steering
    10. {
    11.    
    12.     #region Private fields
    13.     FollowDirection _direction = FollowDirection.Forward;
    14.     float _predictionTime = 2f;
    15.     Pathway _path;
    16.     #endregion
    17.    
    18.    
    19.     #region Public properties
    20.     /// <summary>
    21.     /// Direction to follow the path on
    22.     /// </summary>
    23.     public FollowDirection Direction {
    24.         get {
    25.             return this._direction;
    26.         }
    27.         set {
    28.             _direction = value;
    29.         }
    30.     }
    31.    
    32.     /// <summary>
    33.     /// How far ahead to estimate our position
    34.     /// </summary>
    35.     public float PredictionTime {
    36.         get {
    37.             return this._predictionTime;
    38.         }
    39.         set {
    40.             _predictionTime = value;
    41.         }
    42.     }
    43.    
    44.     /// <summary>
    45.     /// Path to follow
    46.     /// </summary>
    47.     public Pathway Path {
    48.         get {
    49.             return this._path;
    50.         }
    51.         set {
    52.             _path = value;
    53.         }
    54.     }
    55.  
    56.     #endregion
    57.  
    58.     /// <summary>
    59.     /// Should the force be calculated?
    60.     /// </summary>
    61.     /// <returns>
    62.     /// A <see cref="Vector3"/>
    63.     /// </returns>
    64.     protected override Vector3 CalculateForce ()
    65.     {
    66.         if (_path == null || _path.TotalPathLength == 0)
    67.             return Vector3.zero;
    68.        
    69.         AStarPathway path = (AStarPathway)_path;
    70.         if (path != null) {
    71.             Vector3 dest = path.NextPathPoint(Vehicle);
    72.            
    73.             if (dest == Vector3.zero)
    74.                 Debug.DrawLine(Vehicle.Position, dest, Color.red);
    75.             else
    76.                 Debug.DrawLine(Vehicle.Position, dest, Color.blue);
    77.            
    78.             Vector3 difference = dest - Vehicle.Position;
    79.             return (difference * Vehicle.MaxForce) - Vehicle.Velocity;
    80.            
    81. //          Vector3 res = Vector3.zero;
    82. //          Color debugcolor = Color.blue;
    83. //          Vector3 diff = dest - Vehicle.Position;
    84. //          float dotProduct = Vector3.Dot(diff.normalized,Vehicle.gameObject.transform.forward);
    85. //          Debug.DrawRay(Vehicle.Position, Vehicle.gameObject.transform.forward * 3, Color.yellow);
    86. //          Debug.Log("DP = " + dotProduct.ToString());
    87. //          if (dotProduct > -0.3  dotProduct < 0.3) { // slow down for corners
    88. //              res = Vehicle.GetSeekVector(dest);
    89. //              debugcolor = Color.red;
    90. //          }
    91. //          else {
    92. //              res = diff;
    93. //          }
    94. //         
    95. //          Debug.DrawLine(Vehicle.Position, dest, debugcolor);
    96. //          return res;
    97.                
    98.         }
    99.        
    100.         return Vector3.zero;
    101.     }
    102. }
    103.  
    In your code somewhere put the following, and add the steer above to the gameobject (as well as all the other bits like vehicles, wanderers, etc)

    Code (csharp):
    1.  
    2. public void OnPathComplete (Path p) {
    3.   if (!p.error) {
    4.     // Assign the path to the steering algorithm, this will make the bot follow it
    5.     AStarPathway steerpath = new AStarPathway(p, 0.5f, false);
    6.    }
    7.    else
    8.     Debug.Log ("pathing error - " + p.errorLog);
    9. }
    10.  
    Is this useful to anyone? As I said I'm not using it anymore, but I dont know where better to put it? Is there a wiki or anywhere else anyone can suggest?
     
  2. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    Thanks for the code!

    This is a good place...

    There is also a wiki:
    http://wiki.unity3d.com/index.php/Main_Page

    You can also put up packages on the asset store as a form of distribution, but this will more likely have users that expect support from the author.

    Some people also make public versionable repositories and make them available.
     
  3. im

    im

    Joined:
    Jan 17, 2013
    Posts:
    1,408
    what would otherwise be a most excellent and useful asset looks abandoned
    https://www.assetstore.unity3d.com/#/content/36

    hopefully the author will get back in the game and update this asset and add some documentation and examples

    here is an old 2.2 with working unity steer and working examples

    http://arges-systems.com/files/UnitySteer.2.2.zip

    here author continues to work on it it, latest release 2.6

    https://github.com/ricardojmendez/UnitySteer

    here is slightly older perhaps more stable version

    https://github.com/ricardojmendez/UnitySteer/tree/v2.5.1

    here are some examples, not all are working with release 2.5.1 and not all are working with with 2.6 and not all are working with dev.

    https://github.com/ricardojmendez/UnitySteerExamples

    so you have to sort it out yourself if you want newer than 2.2 and have the examples working, bit a of a mess!

    here are older releases

    https://github.com/ricardojmendez/UnitySteer/tree/v2.2
    https://github.com/ricardojmendez/UnitySteer/tree/v2.1

    have fun! but not too much fun ;)
     
  4. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    I believe the state of this project is: The author is writing Unity Steer to work for their own commercial project and are making the code freely available for those who want to use it. I do think it's worth noting that this is not a paid commercial package, so you should expect to get as much support as any other open source bit of code. You will need to get onto Git-Hub to download and get a feeling for the code, read what posts there are on the Author's site and experiment for yourself. This is not a terribly difficult package, per se, but it is also non-trivial and not for a new user.

    You can also read up on steering and flocking in general (google) and get an idea of theory behind this package, so you can start to understand the ideas behind this code.

    [CLARIFICATION]

    This means their own commercial GAME which uses Unity Steer, and they are publishing Unity Steer for the community.

    Sorry for the confusion.
     
  5. im

    im

    Joined:
    Jan 17, 2013
    Posts:
    1,408
    hi thanks!

    im all for an affordably priced commercially supported version for indies! put me on the list.

    but for now i have couple questions

    which version do you suggest would be best to use? im presently using 2.2 cause it works with the examples, but i read a number of pages back that it has issues like turning on a dime, which i dont want so the suggestion was to use 2.5.1, but that one doesnt work too good with a number of the with the cat examples and i also tried 2.6 and dev master version with those examples but it didnt work. basically bunch of missing scripts, i guess i could try to convert the import the missing scripts from 2.2. and adapt it for 2.5.1 or 2.6 or even dev, but im not sure which is the stablest.

    also im dealing with mostly cube shaped objects like buildings and vehicles in the stuff im working on

    and i saw the posts on spheres a while back so what was the final solution? any value in doing cube version of just make the sphere slightly larger to fit the whole cube, i guess using override?

    thanks in advance!
     
  6. im

    im

    Joined:
    Jan 17, 2013
    Posts:
    1,408
    here is my first attempts at cubes, no cute kittens were harmed ;)

    bunch of different sized cubes and 100 kittens getting > 200 fps, with just 10 kittens getting > 1700 fps, with 500 kittens getting like 3 fps :( amd 1090t/ati 6870

    $ALKUhaI.png

    just know that you cant have a kitten with both SteerForCubicalObstacleAvoidance and SteerForSphericalObstacleAvoidance

    also keep in mind that for the most part this is not my code, it was written by the man, i just kind of borrowed it.

    here are some notes
    // [me] cubes on ground i only care about x/z, not y
    // [me] and it works best when x=z or very close to it.
    // [me] i should really use only the side facing the vehicle,
    // [me] but that is for another day
    // [me] and for now i leave vehicles as spherical

    also the math may not be correct cause i kind of took the formula for sphere and put the round peg in the square hole literly.

    so if someone could take a look at where i've made changes and check out the math and intersection for correctness and post back feedback i would appreciate it.

    Code (csharp):
    1.  
    2. #define ANNOTATE_AVOIDOBSTACLES
    3. using UnityEngine;
    4. using UnitySteer;
    5. using UnitySteer.Helpers;
    6.  
    7. /// <summary>
    8. /// Steers a vehicle to avoid stationary obstacles
    9. /// </summary>
    10. public class SteerForCubicalObstacleAvoidance : Steering
    11. {
    12.     #region Structs
    13.     public struct PathIntersection
    14.     {
    15.         public bool intersect;
    16.         public float distance;
    17.  
    18.         public CubicalObstacle obstacle;
    19.        
    20.         public PathIntersection(CubicalObstacle obstacle)
    21.         {
    22.             this.obstacle = obstacle;
    23.             intersect = false;
    24.             distance = float.MaxValue;
    25.         }
    26.     }; 
    27.     #endregion
    28.    
    29.     #region Private fields
    30.     [SerializeField]
    31.     float _avoidanceForceFactor = 0.75f;
    32.  
    33.     [SerializeField]
    34.     float _minTimeToCollision = 2;
    35.     #endregion
    36.    
    37.     #region Public properties
    38.     /// <summary>
    39.     /// Multiplier for the force applied on avoidance
    40.     /// </summary>
    41.     /// <remarks>If his value is set to 1, the behavior will return an
    42.     /// avoidance force that uses the full brunt of the vehicle's maximum
    43.     /// force.</remarks>
    44.     public float AvoidanceForceFactor {
    45.         get {
    46.             return this._avoidanceForceFactor;
    47.         }
    48.         set {
    49.             _avoidanceForceFactor = value;
    50.         }
    51.     }
    52.  
    53.     /// <summary>
    54.     /// Minimum time to collision to consider
    55.     /// </summary>
    56.     public float MinTimeToCollision {
    57.         get {
    58.             return this._minTimeToCollision;
    59.         }
    60.         set {
    61.             _minTimeToCollision = value;
    62.         }
    63.     }
    64.     #endregion
    65.    
    66.     protected new void Start()
    67.     {
    68.         base.Start();
    69.         Vehicle.Radar.ObstacleFactory = new ObstacleFactory(CubicalObstacle.GetObstacle);
    70.     }
    71.    
    72.     /// <summary>
    73.     /// Calculates the force necessary to avoid the closest spherical obstacle
    74.     /// </summary>
    75.     /// <returns>
    76.     /// Force necessary to avoid an obstacle, or Vector3.zero
    77.     /// </returns>
    78.     /// <remarks>
    79.     /// This method will iterate through all detected spherical obstacles that
    80.     /// are within MinTimeToCollision, and steer to avoid the closest one to the
    81.     /// vehicle.  It's not ideal, as that means the vehicle might crash into
    82.     /// another obstacle while avoiding the closest one, but it'll do.
    83.     /// </remarks>
    84.     protected override Vector3 CalculateForce()
    85.     {
    86.         Vector3 avoidance = Vector3.zero;
    87.         if (Vehicle.Radar.Obstacles == null || Vehicle.Radar.Obstacles.Count == 0)
    88.         {
    89.             return avoidance;
    90.         }
    91.  
    92.         PathIntersection nearest = new PathIntersection(null);
    93.         /*
    94.          * While we could just calculate line as (Velocity * predictionTime)
    95.          * and save ourselves the substraction, this allows other vehicles to
    96.          * override PredictFuturePosition for their own ends.
    97.          */
    98.         Vector3 futurePosition = Vehicle.PredictFuturePosition(_minTimeToCollision);
    99.         Vector3 line = (futurePosition - Vehicle.Position);
    100.  
    101.         // test all obstacles for intersection with my forward axis,
    102.         // select the one whose point of intersection is nearest
    103.         Profiler.BeginSample("Find nearest intersection");
    104.         foreach (var o in Vehicle.Radar.Obstacles)
    105.         {
    106.             CubicalObstacle cube = o as CubicalObstacle;
    107.             PathIntersection next = FindNextIntersectionWithCube (cube, line);
    108.             if (!nearest.intersect ||
    109.                 (next.intersect
    110.                  next.distance < nearest.distance))
    111.             {
    112.                 nearest = next;
    113.             }
    114.         }
    115.         Profiler.EndSample();
    116.  
    117.  
    118.         // when a nearest intersection was found
    119.         Profiler.BeginSample("Calculate avoidance");
    120.         if (nearest.intersect
    121.             nearest.distance < line.magnitude)
    122.         {
    123.             #if ANNOTATE_AVOIDOBSTACLES
    124.             Debug.DrawLine(Vehicle.Position, nearest.obstacle.center, Color.red);
    125.             #endif
    126.  
    127.             // compute avoidance steering force: take offset from obstacle to me,
    128.             // take the component of that which is lateral (perpendicular to my
    129.             // forward direction), set length to maxForce, add a bit of forward
    130.             // component (in capture the flag, we never want to slow down)
    131.             Vector3 offset = Vehicle.Position - nearest.obstacle.center;
    132.             avoidance =  OpenSteerUtility.perpendicularComponent(offset, transform.forward);
    133.  
    134.             avoidance.Normalize();
    135.             avoidance *= Vehicle.MaxForce;
    136.             avoidance += transform.forward * Vehicle.MaxForce * _avoidanceForceFactor;
    137.         }
    138.         Profiler.EndSample();
    139.  
    140.         return avoidance;
    141.     }
    142.    
    143.     /// <summary>
    144.     /// Finds the vehicle's next intersection with a cubical obstacle
    145.     /// </summary>
    146.     /// <param name="obs">
    147.     /// A cubical obstacle to check against <see cref="CubicalObstacle"/>
    148.     /// </param>
    149.     /// <param name="line">
    150.     /// Line that we expect we'll follow to our future destination
    151.     /// </param>
    152.     /// <returns>
    153.     /// A PathIntersection with the intersection details <see cref="PathIntersection"/>
    154.     /// </returns>
    155.     public PathIntersection FindNextIntersectionWithCube (CubicalObstacle obs, Vector3 line)
    156.     {
    157.         /*
    158.          * This routine is based on the Paul Bourke's derivation in:
    159.          *   Intersection of a Line and a Sphere (or circle)
    160.          *   http://www.swin.edu.au/astronomy/pbourke/geometry/sphereline/
    161.          *
    162.          * Retaining the same variable values used in that description.
    163.          *
    164.          */
    165.         float a, b, c, bb4ac;
    166.         var toCenter = Vehicle.Position - obs.center;
    167.  
    168.         // initialize pathIntersection object
    169.         var intersection = new PathIntersection(obs);
    170.        
    171.         #if ANNOTATE_AVOIDOBSTACLES
    172.         obs.annotatePosition();
    173.         Debug.DrawLine(Vehicle.Position, Vehicle.Position + line, Color.cyan);
    174.         #endif
    175.        
    176.         // computer line-cube intersection parameters
    177.         a = line.sqrMagnitude;
    178.         b = 2 * Vector3.Dot(line, toCenter);
    179.         c = obs.center.sqrMagnitude;
    180.         c += Vehicle.Position.sqrMagnitude;
    181.         c -= 2 * Vector3.Dot(obs.center, Vehicle.Position);
    182.         // [me] cubes on ground i only care about x/z, not y
    183.         // [me] and it works best when x=z or very close to it.
    184.         // [me] i should really use only the side facing the vehicle,
    185.         // [me] but that is for another day
    186.         // [me] and for now i leave vehicles as spherical
    187.         c -= Mathf.Pow(Mathf.Max (obs.shape.x,obs.shape.z) + Vehicle.ScaledRadius, 2);
    188.         bb4ac = b * b - 4 * a * c;
    189.  
    190.         if (bb4ac >= 0)  {
    191.             intersection.intersect = true;
    192.             Vector3 closest = Vector3.zero;
    193.             if (bb4ac == 0) {
    194.                 // Only one intersection
    195.                 var mu = -b / (2*a);
    196.                 closest = mu * line;
    197.             }
    198.             else {
    199.                 // More than one intersection
    200.                 var mu1 = (-b + Mathf.Sqrt(bb4ac)) / (2*a);
    201.                 var mu2 = (-b - Mathf.Sqrt(bb4ac)) / (2*a);
    202.                 /*
    203.                  * If the results are negative, the obstacle is behind us.
    204.                  *
    205.                  * If one result is negative and the other one positive,
    206.                  * that would indicate that one intersection is behind us while
    207.                  * the other one ahead of us, which would mean that we're
    208.                  * just overlapping the obstacle, so we should still avoid.  
    209.                  */
    210.                 if (mu1 < 0  mu2 < 0)
    211.                     intersection.intersect = false;
    212.                 else
    213.                     closest = (Mathf.Abs(mu1) < Mathf.Abs (mu2)) ? mu1 * line : mu2 * line;
    214.             }
    215.             #if ANNOTATE_AVOIDOBSTACLES
    216.             Debug.DrawRay(Vehicle.Position, closest, Color.red);
    217.             #endif
    218.  
    219.             intersection.distance =  closest.magnitude;
    220.         }
    221.         return intersection;
    222.     }
    223.    
    224.     #if ANNOTATE_AVOIDOBSTACLES
    225.     void OnDrawGizmos()
    226.     {
    227.         if (Vehicle != null)
    228.         {
    229.             foreach (var o in Vehicle.Radar.Obstacles)
    230.             {
    231.                 var cube = o as CubicalObstacle;
    232.                 Gizmos.color = Color.red;
    233.                 Gizmos.DrawWireCube(cube.center, cube.shape);
    234.             }
    235.         }
    236.     }
    237.     #endif
    238. }
    239.  
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. /// <summary>
    6. /// Defines the data for a cubical obstacle that should be used to override
    7. /// the object's bounds calculation.
    8. /// </summary>
    9. /// <remarks>
    10. /// While the automatic bounds calculation performed by CubicalObstacle.GetObstacle
    11. /// is very useful, it aims to encompass the whole object in a cube. As
    12. /// such, objects that are very tall or very wide will end up returning
    13. /// unnecessarily large bounding cubes.
    14. /// Adding a CubicalObstacleData behavior to any object allows the developer
    15. /// to override these bounds calculations with an obstacle that's precisely
    16. /// positioned for the area we wish to block off. For example, if our
    17. /// agents are gravity-bound we care only about considering the trunks of
    18. /// trees as obstacles, not the whole figure.
    19. ///
    20. /// The vast majority of the functionality is now implemented as part of
    21. /// DetectableObject. Retaining this class for backwards compatibility.
    22. /// </remarks>
    23. public class CubicalObstacleData : CubicalDetectableObject {
    24.    
    25.    
    26.     void OnDrawGizmosSelected() {
    27.         Gizmos.DrawWireCube(transform.position + Center, Shape);
    28.     }
    29. }
    30.  
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnitySteer;
    4.  
    5. /// <summary>
    6. /// Parent class for objects that vehicles can aim for, be it other vehicles or
    7. /// static objects.
    8. /// </summary>
    9. public class CubicalDetectableObject: MonoBehaviour
    10. {
    11.     Vector3 _squaredShape;
    12.  
    13.     /// <summary>
    14.     /// Cached transform for this behaviour
    15.     /// </summary>
    16.     protected Transform _transform;
    17.    
    18.     [SerializeField]
    19.     protected bool _drawGizmos = false;
    20.    
    21.     /// <summary>
    22.     /// The vehicle's center in the transform
    23.     /// </summary>
    24.     [SerializeField]
    25.     [HideInInspector]
    26.     Vector3 _center;
    27.    
    28.     /// <summary>
    29.     /// The vehicle's center in the transform, scaled to by the transform's lossyScale
    30.     /// </summary>
    31.     [SerializeField]
    32.     [HideInInspector]
    33.     Vector3 _scaledCenter;
    34.    
    35.     /// <summary>
    36.     /// The vehicle's radius, scaled by the maximum of the transform's lossyScale values
    37.     /// </summary>
    38.     [SerializeField]
    39.     [HideInInspector]
    40.     Vector3 _scaledShape = Vector3.one;
    41.  
    42.     /// <summary>
    43.     /// The vehicle's radius.
    44.     /// </summary>
    45.     [SerializeField]
    46.     [HideInInspector]
    47.     Vector3 _shape = Vector3.one;
    48.    
    49.  
    50.    
    51.     /// <summary>
    52.     /// Vehicle's position
    53.     /// </summary>
    54.     /// <remarks>The vehicle's position is the transform's position displaced
    55.     /// by the vehicle center</remarks>
    56.     public Vector3 Position {
    57.         get {
    58.             if (_transform == null)
    59.             {
    60.                 _transform = GetComponent<Transform>();
    61.             }
    62.             return _transform.position + _scaledCenter;
    63.         }
    64.     }
    65.    
    66.     /// <summary>
    67.     /// Vehicle center on the transform
    68.     /// </summary>
    69.     /// <remarks>
    70.     /// This property's setter recalculates a temporary value, so it's
    71.     /// advised you don't re-scale the vehicle's transform after it has been set
    72.     /// </remarks>
    73.     public Vector3 Center {
    74.         get {
    75.             return this._center;
    76.         }
    77.         set {
    78.             _center = value;
    79.             RecalculateScaledValues();
    80.         }
    81.     }
    82.    
    83.     /// <summary>
    84.     /// Vehicle shape
    85.     /// </summary>
    86.     /// <remarks>
    87.     /// This property's setter recalculates a temporary value, so it's
    88.     /// advised you don't re-scale the vehicle's transform after it has been set
    89.     /// </remarks>
    90.     public Vector3 Shape {
    91.         get {
    92.             return _shape;
    93.         }
    94.         set {
    95.             float x = Mathf.Clamp(value.x, 0.01f, float.MaxValue);
    96.             float y = Mathf.Clamp(value.y, 0.01f, float.MaxValue);
    97.             float z = Mathf.Clamp(value.z, 0.01f, float.MaxValue);
    98.            
    99.             _shape = new Vector3(x,y,z);
    100.            
    101.             RecalculateScaledValues();         
    102.         }
    103.     }
    104.  
    105.     /// <summary>
    106.     /// The vehicle's center in the transform, scaled to by the transform's lossyScale
    107.     /// </summary>
    108.     public Vector3 ScaledCenter {
    109.         get {
    110.             return this._scaledCenter;
    111.         }
    112.     }
    113.    
    114.     /// <summary>
    115.     /// The vehicle's shape, scaled by the maximum of the transform's lossyScale values
    116.     /// </summary>
    117.     public Vector3 ScaledShape {
    118.         get {
    119.             return this._scaledShape;
    120.         }
    121.     }
    122.  
    123.     public Vector3 SquaredShape {
    124.         get {
    125.             return this._squaredShape;
    126.         }
    127.     }
    128.    
    129.    
    130.     #region Methods
    131.     protected virtual void Awake()
    132.     {
    133.         _transform = GetComponent<Transform>();
    134.         RecalculateScaledValues();
    135.     }
    136.    
    137.     /// <summary>
    138.     /// Predicts where the vehicle will be at a point in the future
    139.     /// </summary>
    140.     /// <param name="predictionTime">
    141.     /// A time in seconds for the prediction <see cref="System.Single"/>. Disregarded on the base function.
    142.     /// </param>
    143.     /// <returns>
    144.     /// Vehicle position<see cref="Vector3"/>
    145.     /// </returns>
    146.     public virtual Vector3 PredictFuturePosition(float predictionTime)
    147.     {
    148.         return _transform.position;
    149.     }
    150.    
    151.    
    152.     /// <summary>
    153.     /// Recalculates the vehicle's scaled shape and center
    154.     /// </summary>
    155.     protected virtual void RecalculateScaledValues() {
    156.         if (_transform == null)
    157.         {
    158.             // Since this value gets assigned on Awake, we need to assign it when on the editor
    159.             _transform = GetComponent<Transform>();
    160.         }
    161.         var scale  = _transform.lossyScale;
    162.        
    163.         float x = _shape.x * scale.x;
    164.         float y = _shape.y * scale.y;
    165.         float z = _shape.z * scale.z;
    166.        
    167.         _scaledShape = new Vector3(x,y,z);
    168.         _scaledCenter = Vector3.Scale(_center, scale);
    169.  
    170.         _squaredShape = new Vector3(x*x,y*y,z*z);
    171.     }  
    172.    
    173.     protected virtual void OnDrawGizmos()
    174.     {
    175.        
    176.         if (_drawGizmos)
    177.         {
    178.             if (_transform == null)
    179.             {
    180.                 // Since this value gets assigned on Awake, we need to assign it when on the editor
    181.                 _transform = GetComponent<Transform>();
    182.             }
    183.             Gizmos.color = Color.blue;
    184.             Gizmos.DrawWireCube(Position, ScaledShape);
    185.         }
    186.     }
    187.     #endregion
    188.    
    189.    
    190.    
    191.  
    192. }
    193.  
    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Text;
    5. using UnityEngine;
    6.  
    7. namespace UnitySteer
    8. {
    9.     public class CubicalObstacle : Obstacle
    10.     {
    11.         public Vector3 shape;
    12.         public Vector3 center;
    13.  
    14.  
    15.         // constructors
    16.         public CubicalObstacle(Vector3 shape, Vector3 center)
    17.         {
    18.             this.shape = shape;
    19.             this.center = center;
    20.         }
    21.  
    22.         public CubicalObstacle()
    23.         {
    24.             shape = Vector3.one;
    25.             center = Vector3.zero;
    26.         }
    27.        
    28.         public override string ToString ()
    29.         {
    30.             return string.Format ("[CubicalObstacle {0} {1,2,3}]", center, shape.x, shape.y, shape.z);
    31.         }
    32.        
    33.         /// <summary>
    34.         ///Returns a CubicalObstacle from the current gameObject
    35.         /// </summary>
    36.         /// <param name="gameObject">
    37.         /// A game object to create the obstacle from<see cref="GameObject"/>
    38.         /// </param>
    39.         /// <returns>
    40.         /// A CubicalObstacle encompassing the game object<see cref="Obstacle"/>
    41.         /// </returns>
    42.         public static Obstacle GetObstacle( GameObject gameObject )
    43.         {
    44.             CubicalObstacle obstacle;
    45.             int id = gameObject.GetInstanceID();
    46.             Component[] colliders;
    47.             Vector3 shape = Vector3.zero;
    48.             Vector3 currentShape;
    49.  
    50.             if(!ObstacleCache.ContainsKey( id ))
    51.             {
    52.                 var obstacleData = gameObject.GetComponent<CubicalObstacleData>();
    53.                 // If the object provides his own cubical obstacle information,
    54.                 // use it instead of calculating a cube that encompasses the
    55.                 // whole collider.
    56.                 if (obstacleData != null)
    57.                 {
    58.                     ObstacleCache[id] = new CubicalObstacle(obstacleData.Shape, gameObject.transform.position + obstacleData.Center);
    59.                 }
    60.                 else
    61.                 {
    62.                     colliders = gameObject.GetComponentsInChildren<Collider>();
    63.    
    64.                     if( colliders == null )
    65.                     {
    66.                         Debug.LogError( "Obstacle '" + gameObject.name + "' has no colliders" );
    67.                         return null;
    68.                     }
    69.    
    70.                     foreach( Collider collider in colliders )
    71.                     {
    72.                         if( collider.isTrigger )
    73.                         {
    74.                             continue;
    75.                         }
    76.                         // Get the maximum extent to create a cube that encompasses the whole obstacle
    77.                         //float maxExtents = Mathf.Max(Mathf.Max(collider.bounds.extents.x, collider.bounds.extents.y), collider.bounds.extents.z);
    78.                        
    79.                         /*
    80.                          * Calculate the displacement from the object center to the
    81.                          * collider, and add in the maximum extents of the bounds.
    82.                          * Notice that we don't need to multiply by the object's
    83.                          * local scale, since that is already considered in the
    84.                          * bounding rectangle.
    85.                          */
    86.                         //float distanceToCollider = Vector3.Distance(gameObject.transform.position, collider.bounds.center);
    87.                         //currentShape = new Vector3(collider.bounds.size.x + collider.bounds.extents.x, collider.bounds.size.y + collider.bounds.extents.y, collider.bounds.size.z + collider.bounds.extents.z);
    88.                         currentShape = collider.bounds.size;
    89.                         // [me] cubes on ground i only care about x/z, not y
    90.                         // [me] and it works best when x=z or very close to it.
    91.                         // [me] i should really use only the side facing the vehicle,
    92.                         // [me] but that is for another day
    93.                         // [me] and for now i leave vehicles as spherical
    94.                         if( Mathf.Max(currentShape.x,currentShape.z) > Mathf.Max(shape.x,shape.z))
    95.                         {
    96.                             shape = currentShape;
    97.                         }
    98.                     }
    99.                     ObstacleCache[id] = new CubicalObstacle( shape, gameObject.transform.position );
    100.                 }
    101.             }
    102.             obstacle = ObstacleCache[ id ] as CubicalObstacle;
    103.  
    104.             return obstacle;
    105.         }
    106.  
    107.         public void annotatePosition()
    108.         {
    109.             annotatePosition(Color.grey);
    110.         }
    111.        
    112.         public void annotatePosition(Color color)
    113.         {
    114.             // Primitive cube position indicator, since Unity lacks a
    115.             // Debug.DrawCube
    116.             Debug.DrawRay(center, Vector3.up * shape.y, color);
    117.             Debug.DrawRay(center, Vector3.forward * shape.z, color);
    118.             Debug.DrawRay(center, Vector3.right * shape.x, color);
    119.         }
    120.     }
    121. }
    122.  
     
    Last edited: Sep 6, 2013
  7. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    I am using* the most recent version published on the main trunk of the repository.

    *using being a loose term, as it's one of my hobby projects that sees the light of day once every month or so...
     
  8. Ahnuld

    Ahnuld

    Joined:
    Oct 20, 2012
    Posts:
    15
    I discovered UnitySteer (2.7) a few days ago and have been playing with it since. It really helped me understand more of AI and steering algorithms.
    Now I'm testing it for a small concept, the idea is quite simple: a gameobject moves from one point to another point in 3d space, while avoiding obstacles. I thought this would be a piece of cake with the existing UnitySteer code, but I ran into a problem with the included SphericalObstacleRepulsion behaviour.

    When using objects with a size larger than 1 unit, the vehicle just flies into them, and is only repulsed by the middle of the Detectable Object. No matter how I change the radius, it won't work on larger objects. It's as if the radius settings of the objects has no significant effect on the repulsion algorithm. The avoidance/repulsion force seems to be calculated based only on the distance between the centers of the two objects.

    is this behaviour still a work in progress or am I missing something here?
     
    Last edited: Nov 18, 2013
  9. Alan47

    Alan47

    Joined:
    Mar 5, 2011
    Posts:
    163

    Hi,

    sorry to "hijack" this thread, but I've discovered UnitySteer just yesterday and while the rest of the functionality is amazing, I'm running into the exact same problem with obstacle avoidance as Ahnuld. Is there any solution to this? It would be really nice to have this library for my project, but without obstacle avoidance, it's more or less useless...


    Thanks,


    Alan
     
  10. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    I've had some luck with obstacle avoidance... the issue I find is that since it's *repulsion* it often pushes back the object (hey, it's being repulsed!) when I want it steer around it. This is great for organic behaviour (flies avoiding a spider - eek) but failed in the context of a space ship avoiding an asteroid...

    Remember this library is being written to solve issues with one particular game... so this may not work for yours. The answer may be to dig through the code and find where and how obstacle avoidance is working and modify or write your own avoidance code.
     
  11. Meltdown

    Meltdown

    Joined:
    Oct 13, 2010
    Posts:
    5,822
    I'm upgrading to UnitySteer 2.7. I previously had an AutonomousVehicle, RadarPing and SteerForPathSimplified, which all worked
    But after the update and changing RadarPing to Radar, SteerForPathSimplified always throws an error saying the Vehicle is null!

    What am I missing? I've tried adding a Biped Vehicle as well, so no idea why this would be null.

    EDIT : Never mind, setting it in Steering.cs never seemed to work, so I had to set it in SteerForPathSimplified.cs

    Code (csharp):
    1. void Awake()
    2.     {
    3.         Vehicle = GetComponent<AutonomousVehicle>();
    4.     }
     
    Last edited: Dec 21, 2013
  12. zupabxl2

    zupabxl2

    Joined:
    Nov 5, 2013
    Posts:
    5
    Hi,
    i'm working on a scene in which a player position determined by top down kinect is chased by cars trying to crash on him.
    I've used the autonomous vehicle and the steer for pursuit scripts on the cars, they follow my player (mouse controlled for now) but i have a few questions :

    - Is there a documentation on the autonomous vehicle script attributes ? there are some i don't understand. how do i adjust speed of the cars and what's the difference between max force/speed ?
    - How do i get accurate collisions between the cars ? i tried mesh colliders but that was not working, so i use box colliders for now.
    - How do i avoid the cars to get on top of each other ? I've tried to do higher box colliders but that seems like a bad solution.
    - Is there a way to add particles/explosions on any collision event ? tutorials to do that maybe?

    I also still have to work on a way to deform/crush the cars when they collide. Any help on this subject is welcome too.
    Sorry i'm not a programmer, so that's hard work for me, and otherwise, thanks to the developpers to let us use these really usefull scripts.
    And thanks in advance for your help !
     
  13. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    zupabxl2:

    There are a few different issues here all working at the same time.

    Is there a documentation on the autonomous vehicle script attributes?
    The first is to recognise that UnitySteer is an open source work in progress that the folks at Arges systems are very nice to make available, even if it's relatively under-documented. The flip side to this, is you get to use a very powerful library for free.

    With this in mind, there probably is not the documentation you are looking for. You are encouraged to read through the code, and to research flocking and steering and see who UnitySteer is an implementation of these concepts. You will have to decipher the code and experiment to find the difference between max force and speed. Is there any other users of UnitySteer that can pitch in with a suggestion?

    How do I get accurate collisions between the cars?
    Before you dive into the deep end, getting a quick grip on some of the basics of Unity would be good. You should take a look at the Learn section of the Unity site for more information on Colliders and the best ones to use in what circumstances. I cover this in my tutorial project: Space Shooter. Another good resource is the Documentation. Look at the Physics sections, specifically colliders, in both the User Manual and the Component Reference. Using primitive or compound colliders are preferable over mesh colliders.

    How do i avoid the cars to get on top of each other?
    This is hard to say until you tell us how you've implemented your steering and avoidance code. Are the cars constrained to a ground plane? If not, what keeps them from flying around rather than driving? How are your cars trying to avoid each other? What have you done to simulate gravity?

    Is there a way to add particles/explosions on any collision event? Tutorials to do that maybe?
    Yes, definitely! Several! Check out the Learn section of the Unity site for more information. We cover this specifically in the Space Shooter Project.
     
  14. zupabxl2

    zupabxl2

    Joined:
    Nov 5, 2013
    Posts:
    5
    Hi,
    i'm now reading threw the code to get how the scripts works, but it will take some time for me to understand everything. I'll look at your tutorials, many thanks for your support. I uploaded a video of my WIP so you can see what i've done and maybe where i want to get : http://www.youtube.com/watch?v=yH2kbYUohwE

    the cars are not constrained to the ground, as i like the fact that they can be pushed to the air by the player or by physics based explosions. The cars avoid each other by the use of box collider. The gravity is controlled by the project's physics.

    Also, i would like the vehicles to be able to switch between being attracted or repulsed by the player, but i can't get the fear or evasion scripts working for now.

    Again, many thanks for the amazing support you provide, and sorry for my english since it's not my main language, i do my best to be fully understandable.
     
    Last edited: Jan 15, 2014
  15. 99thmonkey

    99thmonkey

    Joined:
    Aug 10, 2012
    Posts:
    525
    First, I'm sorry if this has been answer as I did not see anywhere anyone else asked this.

    I have my AI set up (it's a Wasp). I've got tether set to 0,0,0 with radius of 5 AND I'm using Wander. I also have Steer for Obstacle Repulsion (the newest one to use which states other Obstacle one is obsolete). I have all my non-movable objects set with the DetectObject piece too.

    Issue - My wasp goes through the ground. Anyone know how to fix this?
     
  16. Meltdown

    Meltdown

    Joined:
    Oct 13, 2010
    Posts:
    5,822
    If anybody has a problem running UnitySteer on an iPad or iPhone, and you get the dreaded...

    Rethrow as TypeInitializationException in Vector3Pathway.AddPoint

    You need to replace any instance of the .First() and .Last() methods in Vector3Pathway.cs
    with an array reference.

    So change

    _points.First() to _points[0]
    _points.Last() to _points[_points.Count - 1]
     
  17. ScriptGeek

    ScriptGeek

    Joined:
    Mar 4, 2011
    Posts:
    45
    I'm working with UnitySteer 3.0 and my project requires NPCs to walk around randomly and avoid colliding with one another while also avoiding collisions with static obstacles. I was thinking I could use Unity's Navmesh with the SteerForNavmesh behavior and a NavMeshObstacle component on each NPC and check the Carve checkbox, but this results in the NPCs performing jittery behavior. Is there any way to get this to work or is there a better way to accomplish this?
     
  18. longvk1989

    longvk1989

    Joined:
    Apr 10, 2015
    Posts:
    17
    Can anyone show me how to create a SteerForMoveAroundPoint which can steer object around (circle) a certain point ?
     
  19. varunvp

    varunvp

    Joined:
    Jul 11, 2014
    Posts:
    57
    Has anyone been able to loop the SteerForPathSimplified? It is a beautiful script, but without looping, it's a bit lacking.