Search Unity

Poly|Nav: Pathfinding for Unity2D

Discussion in 'Assets and Asset Store' started by nuverian, Jan 27, 2014.

  1. GordonWedge

    GordonWedge

    Joined:
    Jul 6, 2013
    Posts:
    23
    Thanks Nuverian, i did this, but this only move the problem a step after, if i enable the script later (from a button for example), the sprite rotate before the first straight path, i think the rotation must start on the first change of direction.
    There is a way to avoid this? Or, can i put the sprite in a way that it will be on the right direction on the first rotation, but I don't understand what is the rotation amount and why.

    thanks
     
  2. iso250

    iso250

    Joined:
    Apr 19, 2015
    Posts:
    28
    Hello.

    Is there a way to increase or decrease an agent's turning speed? I'm looking to have some agents perform 90 degree turns around corners and other agents having to take wide corners.

    Thank you!!

    EDIT

    Just discovered how. I made a public int called TurningSpeed. On line 355 I changed...
    steer = Truncate(steer, maxForce);

    in to this.
    steer = Truncate(steer, maxForce) * TurningSpeed;

    TurningSpeed must be greater than 0 to work. 1 makes it work as standard, higher numbers make the turns sharper and sharper.
     
    Last edited: Jun 23, 2015
  3. josessito

    josessito

    Joined:
    Feb 14, 2014
    Posts:
    57
    Hi, I have a question. I just want to see if a path is possible between two points; is CheckLOS what I'm looking for?
    I don`t want the path at this point, just to check if there is one... I've been looking at the code but it is a little intense for me.

    By the way I have a suggestion (it is probably for a second asset) It would be great to have a 2D platformer path system (jumping ledges and taking gravity and jumping into account), I think I could be a good seller (I'm not good enough programming to make something of the sort, else I would, at least for my game)

    Thanks!

    I forgot, Is there a way to find a second path? I mean haivng the same start pointA and end pointB it could be usefull to generate mutiple different paths (maybe select one of n possibles at random) Sometimes the optimal path is.. well is boring ;)
     
    Last edited: Jun 25, 2015
    Nixaan likes this.
  4. Nixaan

    Nixaan

    Joined:
    May 30, 2013
    Posts:
    118
    That would be great.
     
    nuverian likes this.
  5. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hello,
    I kind of fail to see the problem here. :/
    I just tried doing this and the sprite stays at the rotation set in the editor. As soon as I enable MoveBetween component and when the first destination is set for the agent, the sprite rotates towards it, but not earlier.

    Hey,
    Glad to know you have found a solution. I will actualy go ahead and add a turning speed variable in the next version. Honestly it's something sliped away.

    Thanks!

    Hey,

    To check if a path exists beetween A and B, a path has to be calculated.
    Here is the way to calculate and check if paths exist regardless of agent, but before posting the code, please note that a path may not be ready in the same frame as the one requested. As such this works with callbacks.
    Code (CSharp):
    1. void TestPath(Vector2 from, Vector2 to){
    2.     PolyNav2D.current.FindPath(from, to, OnPathReady);
    3. }
    4.  
    5. void OnPathReady(Vector2[] path, bool pathFound){
    6.     //do something...
    7. }
    With delegates:
    Code (CSharp):
    1. void TestPathAndDo(Vector2 from, Vector2 to, System.Action CallMethod){
    2.     PolyNav2D.current.FindPath(from, to, (path, pathFound)=>
    3.         {
    4.             if (pathFound){
    5.                 CallMethod();
    6.             }
    7.         });
    8. }
    Having platformer (jump & gravity) pathfinding would indeed be great, but I I think is a bit outside of the scope of this asset, yeah. I think I saw something similar on the asset store one day, but I'm not sure though. If not, I could take a look at it :)

    There is no way to get the 2nd, or an alternative path unfortunately. I will see if it's easy to add some randomness though as an option :)

    Cheers and thanks!
     
  6. Nixaan

    Nixaan

    Joined:
    May 30, 2013
    Posts:
    118
    It could be an addon pack so it doesn't clutter the rest of the asset for those that won't need it.
     
  7. johanneskopf

    johanneskopf

    Joined:
    Feb 16, 2015
    Posts:
    81
    Great asset! Really simple to setup :)
    I use your asset in conjunction with Ferr2D, to generate my 2D world map with it and let my ships move with your PolyNav asset.
    Ferr2D is able to generate 2D polygon colliders, but the polygon collider's points seem to be generated in the wrong direction for PolyNav.
    My bugfix is not working every time. Sometimes it works, sometimes the walkable is calculated wrong and my ships are driving to the wrong point (to the edge of the polygon collider of the nearest obstacle) if I press the outside area.
    Can you help me?

    Code (CSharp):
    1. void CreatePolyMap(bool generateMaster)
    2.     {
    3.         ...
    4.             if (masterCollider is PolygonCollider2D)
    5.             {
    6.                 var polyCollider = (PolygonCollider2D)masterCollider;
    7.  
    8.                 // Edit: needed when path of polygoncollider is set up in the wrong direction (e.g. from Ferr2D auto generated)
    9.                 if (reversePolygonPointsForFerr2D)
    10.                     polyCollider.SetPath(0, polyCollider.GetPath(0).Reverse().ToArray());
    11. ...
    12.  
     
  8. iso250

    iso250

    Joined:
    Apr 19, 2015
    Posts:
    28
    Oh, in addition to changing the steer line to

    steer = Truncate(steer, maxForce) * TurningSpeed;

    in the seeking a target section, I found that it also helped if you added it to the slowing at target's arrival section, too. Also, I have found that if the TurningSpeed is too low the agents can sometimes do a little too much circling around the destination before hitting it. In those cases I put this line in to slowing at target's arrival.

    steer = Truncate(steer, maxForce) * Mathf.Max (TurningSpeed, 2.5f);
     
  9. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Thanks for the suggestion :)

    Hi and thanks.

    If you use Fer2D for Obstacles Polygons, there is an option on the PolyNavObstacle inspector to "invert polygon". Have you tried that one?
    Are you using Fer2D for the master walkable polygon? (judging by the code you posted)

    Thanks.


    Thanks for posting information for everyone :)
     
  10. plasticYoda

    plasticYoda

    Joined:
    Aug 20, 2013
    Posts:
    62
    I finally started to experiment with this today - noticed a minor bug. In PolyNavAgent you check to see if the new destination is the same as primeGoal, and if they are close, you don't setup the pathing - which is a good thing. However, you default primeGoal to (0,0), so if I'm trying to move the PolyNavAgent to (0,0) initially, it won't start pathing. I simply added a primeGoal = transform.position; in the Awake() function, which fixed the problem.

    I'll let you know if I see any other problems or have any questions.
     
  11. TarlSS

    TarlSS

    Joined:
    Apr 26, 2014
    Posts:
    21
    Hello,

    I'd like to do some pathfinding given that an object has a minimum thrust, like an aircraft. I'd like it to hit points WITHOUT slowing down, or slowing down to a minimum speed.

    How can I account for that? It'd be really cool to see a ship weave through asteroids at high speed without slowing down.

    Anyway you can help me with that?
     
  12. josessito

    josessito

    Joined:
    Feb 14, 2014
    Posts:
    57
    Hi nuverian, I was wondering, are you still working on the performance of the pathfinding? I'm seen some nasty spikes in the profiler when the TryNextPath() (or something like that) method is called from polyNav. Maybe I can do some optimization? I'm not using the agents yet, only the pathfinding and I have regenerate on change turned off.

    Thanks!

    I have spikes of up to 51ms. I really love this asset and I understand that pathfinding is an intensive task, but I'm not sure if I'll be able to use it like these.
     
    Last edited: Jun 28, 2015
  13. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Ah, thanks :)
    I will fix this in the next version.

    Hello,
    If you want the agent to be as rigid as possible, try these settings:
    Mass = 1
    Slowing Distance = 0
    Look Ahead Distance = 0

    Let me know if that's what you are after.

    Hello,
    Do you have a lot of obstacles in the scene that change position, rotation or scale? If so, regenerating the map is quite heavy. I don't think it's the pathfinder's problem. If you can share your project please send me a PM and I will take a look.

    Thanks
     
  14. josessito

    josessito

    Joined:
    Feb 14, 2014
    Posts:
    57
    Hi nuverian, I tried to PM you but I can't upload the images. Here is an image of the profiler. The three spikes you see are almost 80% from polynav.

    Profiler.png

    Here is my setup, I have regenerate map of and invert polygon checked in all the obstacles. (polygon colliders).

    Setup.png

    I tried the asset in my laptop and it reach up to 51ms (i3 processor 8gb of ram). This screenshot was captured from my desktop pc ( i7 processor 16gb of ram) and you can see it is also over 21ms, almost exclusively caused by polynav.

    The obstacles are fixed, they don't change size, nor rotate, and no new obstacles are introduced in the scene on runtime.

    EDIT: I could try to optimize the level design for polyNav to have an easier time if needed, but I'm not sure what is best and what to avoid for this. I'm also planing to add Agents to the scene (flying units) and in that case the units will need to readjust their path and it would kill the performance of my game if there is nothing I can do about it.
     
    Last edited: Jun 30, 2015
  15. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    984
    Hey! Great asset! Nice and easy to understand and setup. However, I'm having some trouble with something that seems very basic. I'm tempted to think it might be a bug but maybe I'm just being dumb. I found a particular shape and position for a single triangle that polynav can't seem to work around. I've narrowed it down to the very basics to show what I mean. In all cases I'm trying to find a path from the square to the mouse and then place the circle at the final destination. I've tried fiddling with PolyNav2D options and flipping the 'Invert Polygon' switch. Nothing seems to work.

    In this first example the path cuts right through the obstacle. You'll notice that in the configuration the 'skin' region of the obstacle is all wrong. Flipping the 'Invert Polygon' option seems to do nothing here.
    Bad_01.PNG

    In this case it finds the path to the other side of the triangle where I clicked but it's taking the longest possible path rather than the shortest.
    Bad_02.PNG

    Here it is failing to find a path at all! The yellow line runs from the square to the point I clicked on the screen. You'll also note that I changed the shape of the polygon so that it displays proper collision regions but it seems to make no difference.
    Bad_03.PNG


    As a side note I think there should be a flag that determines if the final location can exist within the invalid regions defined at the vertices of the obstacles. In my case I'm using PolyNav2D for a 2D platformer and the skin size has to be very high to let the agent walk far enough past cliff edges to let it fall down. But the if the user clicks in any of these dead zones then nothing happens. My own nav agent can deal with getting too close to walls at the end of the calculation so I just want to be able to set the destination anywhere without restriction.
     
  16. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    984
    Ah nevermind. I think I figured it out. I was using Ferr2D to setup my terrain. And the colliders it generated at startup were conflicting with the colliders that PolyNav2D required at edit-time. I removed the code that makes the PolyNavObstacle generate colliders and it seems to work ok now. There are still some degenerate skin issues but they don't seem to have much (if any) impact on the path-finding now.

    That being said I also see that the way Ferr2D generates its collision polygons causes an anomaly with the inflated polygon skins used by PolyNav2D. I didn't realize this at first because it was actually helping me. I still need the ending positions to be allowed to land within these regions so I'll just have to muck around with PolyNav2D's code until I can find a way to make it do this.
     
  17. iso250

    iso250

    Joined:
    Apr 19, 2015
    Posts:
    28
    Look at the posts I put up regarding TurningSpeed. That will fix it. The game I'm making needed some agents to take sweeping paths around a course and others needed to make sharp, precise turns. TurningSpeed gave me a variable that allowed me to do that independent of all existing variables.
     
  18. josessito

    josessito

    Joined:
    Feb 14, 2014
    Posts:
    57
    I'm also using ferr2d. Does anyone else has check the performance of the asset? Is it just me that have those spikes?
     
    Last edited: Jul 1, 2015
  19. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    984
    I'm working on a system that randomly generates levels using prefab chunks. I haven't really gotten far enough to test the overall performance but your earlier post has me a little concerned. I was already thinking of ways I might mitigate it if it turns out to be the case for me as well. Here are two possible solutions:

    1) The first thing I notice is that the biggest chunk of time is spent calculating the line-of-sight between each node. A quick look seems to indicate that it is checking for collisions against every line segment of every polygon found within the PolyNav2D region. You might try replacing this function with one of your own that uses Physics2D.Linecast(). This will likely use Unity's 2D broad-phase filter (hopefully, I'm not actually sure of that) and may significantly cut down on the number of line segment comparisons. However, it will also mean that line-of-sight will ignore the extra 'skin' that gets tacked on to every polygon.

    2) Another way to cut down on line segment checks might involve making a smaller PolyNav2D region that is moved to center on the entity that is performing the path calculation. However, I suspect the performance hit from constantly rebuilding might be just as bad.

    It's gonna be a few days before I really get a chance to investigate myself so in the meantime feel free to try either of these methods out and let me know ;)

    EDIT: Also, how frequently are you checking for paths? With a single entity I was checking for a path about 30 times per second (around 100 per second I noticed a good bit of lag). Maybe your just pathing too frequently?

    ~Sluggy
     
    Last edited: Jul 1, 2015
  20. josessito

    josessito

    Joined:
    Feb 14, 2014
    Posts:
    57
    Hi Sluggy, I did what you suggested and indeed it is much much faster, the problem is I'm using it for a projectile, which should explode if it hits an obstacle and without the inflated radius any time the projectile needs to go around a curve, the path goes too close to the obstacle, and the projectile collides with the obstacle. Can you think a way to take the radius into consideration, I've been thinking about it but I guess I'm not experienced or clever enough to achieve this.

    This is how the method looks now.

    Code (CSharp):
    1.  
    2. ///Determine if 2 points see each other.
    3.     public bool CheckLOS (Vector2 posA, Vector2 posB){
    4.  
    5.         if ( (posA - posB).sqrMagnitude < Mathf.Epsilon )
    6.             return true;
    7.         if (Physics2D.Linecast(posA, posB, 1 << obstacleLayer))
    8.             return false;
    9.        
    10.         //Edit: Commnet out
    11.         //Polygon poly = null;
    12.         //for (int i = 0; i < map.allPolygons.Length; i++){
    13.         //    poly = map.allPolygons[i];
    14.         //    for (int j = 0; j < poly.points.Length; j++){
    15.                
    16.         //        if (SegmentsCross(posA, posB, poly.points[j], poly.points[(j + 1) % poly.points.Length]))
    17.         //            return false;
    18.         //    }
    19.         //}
    20.         return true;
    21.     }
    22.  
     
  21. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    984
    Ok so this is all by memory since I don't have a computer in front of me right now.

    What you could do is make a secondary structure in PolyNav2D that maps the unity colliders to the inflated polygons. It might even do this for you already since I see an object called 'map' in that code. If it doesn't do it I'd recommend doing it in the InflatePolygon method (can't recall exactly the name). Once you detect a collision with the lincast you could find the inflated polygon that is associated with the collider returned from the linecast. Then you can simply use the inner loop of that LOS method to get exact results. Essentially you'd be using the lincast as a brosd phase filter and only have to re-test against all the lines of a single polygon rathet than every poly in the scene.
     
  22. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    984
    Alright. So first I want to say that solving your little problem here has actually helped solve my own since I can now have the terminating node land within the inflated colliders! Woot!

    Now for the bad news, it became pretty apparent to me that this won't always be that great of a solution. 1) It adds a little extra overhead (though not too much I think) to the regeneration of the maps. 2) Worse, it still requires a comparison against every poly in the nav scene if any LOS broadphase (read: Physics2D.Linecast) fails to hit a non-inflated polygon. 3) Also, with this method you'll probably want to user simpler polygons rather than big complex ones so that you can cut down on the number of line segments that have to be checked when a raycast does actually hit a polygon.

    So first thing we'll do is change the PolyNav2D object in the following ways:
    Add these values to the top of the PolyNav2D class.
    Code (CSharp):
    1.  
    2.     //SLUGGY
    3.     //NOTE: I chose to go with pre-buffer capacity of four just in case
    4.     //obstacles are added at runtime. This may speed things up a bit in such a case.
    5.     //You can make it zero if you wish to conserve memory and aren't planning to add
    6.     //new obstacles on the fly very often.
    7.     Dictionary<Collider2D, Polygon> BroadphaseMap = new Dictionary<Collider2D, Polygon>(4);
    8.  
    9.     //This is used to define our broadphase raycasting layer. Kind yucky but it's the best I can think of.
    10.     //You'll need to change the editor for this object for it to show up in the inspector.
    11.     public LayerMask ObstacleLayer;
    12.     //END SLUGGY
    You'll want to change the PolyNav2D->CreatePolyMap() method to look like the following
    Code (CSharp):
    1.  
    2.     //takes all colliders points and convert them to usable stuff
    3.     void CreatePolyMap(bool generateMaster){
    4.  
    5.         var masterPolys = new List<Polygon>();
    6.         var obstaclePolys = new List<Polygon>();
    7.  
    8.         //create a polygon object for each obstacle
    9.         for (int i = 0; i < navObstacles.Count; i++){
    10.             var obstacle = navObstacles[i];
    11.             var transformedPoints = TransformPoints(obstacle.points, obstacle.transform);
    12.             var inflatedPoints = InflatePolygon(transformedPoints, Mathf.Max(0.01f, inflateRadius + obstacle.extraOffset) );
    13.      
    14.             //SLUGGY - I wanted to do this in 'AddObstacle' but the polygon
    15.             //isn't inflated until here. Also, we can't always be sure that
    16.             //Ferr2D will have been initialized first so that there even *is*
    17.             //a collider present.
    18.  
    19.             //SLUGGY: removed this --> obstaclePolys.Add(new Polygon(inflatedPoints));
    20.             //And replaced it with the two lines below.
    21.             var poly = new Polygon(inflatedPoints);
    22.             obstaclePolys.Add(poly);
    23.  
    24.             var collider = obstacle.gameObject.GetComponent<Collider2D>();
    25.             if (collider != null) BroadphaseMap[collider] = poly;
    26.             //END SLUGGY
    27.         }
    28.  
    29.         if (generateMaster){
    30.  
    31.             if (masterCollider is PolygonCollider2D){
    32.  
    33.                 var polyCollider = (PolygonCollider2D)masterCollider;
    34.                 //invert the main polygon points so that we save checking for inward/outward later (for Inflate)
    35.                 var reversed = new List<Vector2>();
    36.          
    37.                 for (int i = 0; i < polyCollider.pathCount; ++i){
    38.  
    39.                     for (int p = 0; p < polyCollider.GetPath(i).Length; ++p)
    40.                         reversed.Add( polyCollider.GetPath(i)[p] );
    41.              
    42.                     reversed.Reverse();
    43.  
    44.                     var transformed = TransformPoints(reversed.ToArray(), polyCollider.transform);
    45.                     var inflated = InflatePolygon(transformed, Mathf.Max(0.01f, inflateRadius) );
    46.              
    47.                     //SLUGGY: removed this --> masterPolys.Add(new Polygon(inflated));
    48.                     //And replaced it with the two lines below.
    49.                     var poly = new Polygon(inflated);
    50.                     masterPolys.Add(poly);
    51.                     BroadphaseMap[masterCollider] = poly;
    52.                     //END SLUGGY
    53.                     reversed.Clear();
    54.                 }
    55.  
    56.             } else if (masterCollider is BoxCollider2D){
    57.                 var box = (BoxCollider2D)masterCollider;
    58.                 var tl = box.offset + new Vector2(-box.size.x, box.size.y)/2;
    59.                 var tr = box.offset + new Vector2(box.size.x, box.size.y)/2;
    60.                 var br = box.offset + new Vector2(box.size.x, -box.size.y)/2;
    61.                 var bl = box.offset + new Vector2(-box.size.x, -box.size.y)/2;
    62.                 var transformed = TransformPoints(new Vector2[]{tl, bl, br, tr}, masterCollider.transform);
    63.                 var inflated = InflatePolygon(transformed, Mathf.Max(0.01f, inflateRadius));
    64.  
    65.                 //SLUGGY: removed this --> masterPolys.Add(new Polygon(inflated) );
    66.                 //And replaced it with the two lines below.
    67.                 var poly = new Polygon(inflated);
    68.                 masterPolys.Add(poly);
    69.                 BroadphaseMap[masterCollider] = poly;
    70.                 //END SLUGGY
    71.          
    72.             }
    73.  
    74.      
    75.         } else {
    76.  
    77.             masterPolys = map.masterPolygons.ToList();
    78.         }
    79.  
    80.         //create the main polygon map (based on inverted) also containing the obstacle polygons
    81.         map = new PolyMap(masterPolys.ToArray(), obstaclePolys.ToArray());
    82.  
    83.         //
    84.         //The colliders are never used again after this point. They are simply a drawing method.
    85.         //
    86.     }
    The next change is the PolyNav2D->CheckLOS() method. You'll also need to add the CheckLOSOnEveryPoly() method.
    Code (CSharp):
    1.  
    2.     ///Determine if 2 points see each other.
    3.     public bool CheckLOS (Vector2 posA, Vector2 posB){
    4.  
    5.         if ( (posA - posB).sqrMagnitude < Mathf.Epsilon )
    6.             return true;
    7.  
    8.         Polygon poly = null;
    9.         //SLUGGY - replaced the section below
    10.         /*
    11.         for (int i = 0; i < map.allPolygons.Length; i++){
    12.             poly = map.allPolygons[i];
    13.             for (int j = 0; j < poly.points.Length; j++){
    14.                 if (SegmentsCross(posA, posB, poly.points[j], poly.points[(j + 1) % poly.points.Length]))
    15.                     return false;
    16.             }
    17.         }
    18.         return true;
    19.         */
    20.  
    21.         //This provides a kind of broadphase filter. However, a large hit still has
    22.         //to be taken when the broadphase fails to find a hit and we need to compare
    23.         //against all polys in the nav world.
    24.         RaycastHit2D[] broadphaseTest = new RaycastHit2D[1];
    25.         if (Physics2D.LinecastNonAlloc(posA, posB, broadphaseTest, ObstacleLayer.value) > 0)
    26.         {
    27.             //ok, we know what collider we hit, now it's
    28.             //time to check the BroadPhase list for the inflated poly.
    29.             if(BroadphaseMap.TryGetValue(broadphaseTest[0].collider, out poly))
    30.             {
    31.                 //check against obstacle polygons
    32.                 for (int j = 0; j < poly.points.Length; j++)
    33.                 {
    34.                     if (SegmentsCross(posA, posB, poly.points[j], poly.points[(j + 1) % poly.points.Length]))
    35.                         return false;
    36.                 }
    37.             }
    38.             else
    39.             {
    40.                 //there was no collider in the broadphase map. We''l have to do it the hard way.
    41.                 return CheckLOSOnEveryPoly(posA, posB);
    42.             }
    43.         }
    44.         //broadphase failed to test positive. We *still* have to do it the hard way!
    45.         return CheckLOSOnEveryPoly(posA, posB);
    46.         //END SLUGGY
    47.     }
    48.  
    49. bool CheckLOSOnEveryPoly(Vector2 posA, Vector2 posB)
    50.     {
    51.         Polygon poly = null;
    52.         for (int i = 0; i < map.allPolygons.Length; i++)
    53.         {
    54.             poly = map.allPolygons[i];
    55.             for (int j = 0; j < poly.points.Length; j++)
    56.             {
    57.                 if (SegmentsCross(posA, posB, poly.points[j], poly.points[(j + 1) % poly.points.Length]))
    58.                     return false;
    59.             }
    60.         }
    61.         return true;
    62.     }
    And finally, for good measure you'll want to expose the collision layers for the Linecasts in the inspector by change the PolyNav2DInspector->OnInspectorGUI() method. just add the following to the top of the function.
    Code (CSharp):
    1.  
    2.         //SLUGGY
    3.         serializedObject.Update();
    4.         EditorGUI.BeginChangeCheck();
    5.         var prop = serializedObject.FindProperty("ObstacleLayer");
    6.         EditorGUILayout.PropertyField(prop, true);
    7.         if(EditorGUI.EndChangeCheck())
    8.                 serializedObject.ApplyModifiedProperties();
    9.         //END SLUGGY

    Like I said - this definitely has some drawbacks but it might just be enough of a band-aid to boost performance for you. A better general solution would probably be to either to have PolyNav2D generate new unity Collider2D objects for the inflated polys and place them in a unique layer and raycast against those or just flat-out provide a built-in spacial partitioning system so that it can perform its own broadphase filtering.

    On a side note, I plan to make another quick fix that will help avoid degenerate poly inflation due to the way Ferr2D makes its colliders. It often places multiple points at the vertices. Event when 'SplitCorners' is off it still has issues where the first and last vertices meet. Check back ina couple of days if you're interested.
     
    Last edited: Jul 2, 2015
  23. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hello guys and sorry for the late replies.

    I will have the get Fer2D to see what's going on since many people seem to use it with PolyNav.

    Regarding using Physics, there is a simpler solution for taking the inflate radius into account in my opinion. Here they are:
    Code (CSharp):
    1.     public bool CheckLOS (Vector2 posA, Vector2 posB){
    2.  
    3.         if ( (posA - posB).sqrMagnitude < Mathf.Epsilon )
    4.             return true;
    5.  
    6.         if (Physics2D.CircleCast(posA, inflateRadius/2, (posB - posA).normalized, (posA - posB).magnitude, obstaclesLayer.value))
    7.             return false;
    8.  
    9.         return true;
    10.     }
    Code (CSharp):
    1.     public bool PointIsValid (Vector2 point){
    2.  
    3.         if (Physics2D.OverlapCircle(point, inflateRadius/2, obstaclesLayer.value) != null)
    4.             return false;
    5.  
    6.         return true;
    7.     }
    Yes, that's quite faster than the manual methods.
    I might go this way for the next update. Thanks everyone for bringing this into the discussion and sharing information with others!

    Cheers!
     
  24. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    984
    Haha! I feel silly going to all that extra effort now.

    As for Ferr2D. The issue is that it generates colliders by connecting line segments so you get a lot of duplicate verts. It should be simple enough to fix this when you build the poly map. For each vert in the collider check to see if the next one is approximately in the same place and if so, just skip it.
     
  25. josessito

    josessito

    Joined:
    Feb 14, 2014
    Posts:
    57
    @Sluggy @nuverian Thanks for your help! It is much appreciated. I found, however, that using circlecast is not that much faster (for me at least). So I will stick with linecast(there is a very noticeable difference) and I will try to make it work somehow ( I will probably inflate the returned path manually or something, because I don't need much precision). I did however use 1 << obstaclesLayer instead of obstaclesLayer.value. The second option for some reason didn't worked for me (I'm not sure what is going on there). At any rate, thanks for your help!!
     
  26. neoneper

    neoneper

    Joined:
    Nov 14, 2013
    Posts:
    48
    How could I possibly use this asset with Edge Colíder?
    I'm working with sprites that can be destroyed at runtime, and whenever this occurs, the edge colliders of the sprite are automatically updated.

    I intend to acquire it asset, if there is any way for me to use the edge colliders, or any other way to dynamically update the possible points of searches for valid node!

    Tansk (^.~). Wating!
     
  27. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    984
    I finally got my pathfinder working and integrated with the level generator and it is as you said - The performance hits spikes on very regular intervals and the CircleCast does little to alleviate it. Surprisingly the code I posted above performs much better. However, there are some bugs in it that currently make it non-serviceable and it still hits some spikes that are GC related.
     
  28. bojjenclon

    bojjenclon

    Joined:
    Feb 18, 2015
    Posts:
    2
    Two Feature Suggestions:
    1. Would it be possible to have a kind of fudge factor with destination success? As in, if the agent is within x units from the destination, it considers that a success.
    2. A "stuck" check. Perhaps a certain amount of time an agent can remain stationary (i.e. trying to get around another agent or obstacle) before it considers the attempt a failure.

    Both of these are fairly easy to implement in the game object's update loop, but I figured it could be something built into the asset itself as it would make for a good option that would only take a couple extra lines in the editor. Plus I'd like to be able to benefit directly from the callbacks. If either of these are already possible, let me know.

    Edit: After messing around with Poly|Nav some more, I realized I should probably specify that both of these suggestions came from messing around with groups of enemies with avoidance radii. Since they don't factor other agents into their paths, they can get really close to their destination but not consider it a success. And with groups of them, they just bunch up. In addition, if the destination itself is an agent with an avoidance radius, the traveling agent will never "reach" its path. Perhaps I'm approaching it wrong, which is very possible as I only just bought the asset the other night. If so please let me know. :)
     
    Last edited: Jul 7, 2015
  29. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Sorry for late replies everyone.

    Don't be :)
    Yeah this can be fixed by removing very close by verts. Thanks.

    Hello,
    PolyNav currently works only with Polygon and Square colliders, which are used to create the valid and non valid polygonal areas that agents can move on. A line collider does not have much information to create a closed polygon shape required. Sorry :)


    I will take a further look at performance after I return from the summer trip I'm current at :)
    Thanks.

    Hey,
    The first is possible by increasing the "Stopping Distance". Is that what you mean?
    I definetely need to implement your 2nd "stuck" request to avoid this behaviour when they try to avoid each other while trying to reach the same destination too. Makes sense. I will take a look at this :)

    Thanks!
     
  30. Silverlode

    Silverlode

    Joined:
    Apr 9, 2013
    Posts:
    41
    I have a question about the recent performance improvements. Would it affect the ability to add/update agents collision to the map on a semi-frequent basis? Currently I am doing this only when agents are staying still for moderate length of time (because as you've said, updating the map for every agent for every frame is too much work). Im just wondering if there was scope now to update the map every 1 frame X, for up to Yagents, or whether this is still unrealistic.
     
  31. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hello,
    The performance improvenets in 1.5 are relevant to the pathfinding itself rather than the map generation. Is that what you are asking?

    Thanks
     
  32. fighder

    fighder

    Joined:
    Feb 1, 2015
    Posts:
    17
    Dear nuverian,
    I am trying to use your polynav 2d but when I try to pass an action with a parameter through agent.OnDestinationReached, it does not work. Was wondering how can I pass an action with one parameter with your pathfinder?
     
  33. usfuntoolz

    usfuntoolz

    Joined:
    Jul 16, 2015
    Posts:
    1
    Hi,

    I am having an error while having multiple PolyNav2D objects in my game. The scenario is as follows:

    - There are 9 parent objects, each containing a single PolyNav2D object with obstacles inside.
    - All these 9 objects are setActive false on game start.
    - When user presses play button, 1 of these 9 objects is selected randomly and is setActive true.
    - Likewise when game finishes, the randomly selected object is setActive to false again.
    - There is a manager object with play button which chooses from 9 objects and changes their active state.

    The issue that I am facing is that while this active, inactive system works just fine, the PolyNav2D objects inside their parents objects sometime give log error that there is no PolyNav2D object active, while Hierarchy shows them as active. My player object does not move because of this.
    Furthermore while switching is done between these 9 objects, sometimes the PolyNav2D object from previous active object displays as well. Hence making obstacles in path which are not part of the current map.

    Please help as this scenario is causing me huge issues.
     
  34. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hello,
    The OnDestinationReached event is of type System.Action and as such you can't subscribe a method with parameters. This is just how events work :)
    Can you describe your use case?

    Thanks.

    Hello,
    I have replied to your PM regarding this and a way to fix it.

    Thanks
     
  35. Sh4D

    Sh4D

    Joined:
    Dec 8, 2014
    Posts:
    17
    Hi,
    I have been reading anything I could find about Polynav and it might be exactly what I am searching.
    I am making an isometric real time tactical rpg in a post apo world, and i really wouldn't care purchasing polynav if I was sure I am going to use it. This is the kind of thing I would only know after making it work properly in my project and testing it for performance issues, etc,etc... It is really too bad you can't have some kind of trial version!
    I really would love sparing me the time to make my own pathfinder but will definitly do it if I have to.
    The tricky part about my game is that if features both keybased movement and mouse click (where the pathfinder will be necessary).
    All my characters are cut in 5, in child gameobjects (Head, legs, torso, weapon, holster). I also have a socle circle2Dcollider for reacting with mines and with each other, or not entering walls. Can I define for avoidance the socle collider child gameobject pivot of the characters?
    Also most object and walls are destroyable, and from what i have read the path would update accordingly if this kind of things happen while someone walks outside a building for instance?
    And the last question for now, when the path is defined can it return to me the angle of the chosen direction so I can display the proper animations accordingly?
    I added a little picture so you can have a better picture of what is my game. You can see more on my site in the Games/Almost Alive part. www.emircerimovic.com

     
  36. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hello there,

    Thanks for your interest in PolyNav.
    Let me answer your questions :)

    Agent avoidance is already done with a circle which resides at the agent's transform position. You can't, dont need to, define a specific circle collider for it. Currently the avoidance circle center can't be changed, but you can easily change the source code to add an offset (I will probably add this in the next version too).

    Now, the map can be set to regenerate whenever an obstacle polygon is enabled/disabled or changed, BUT (and thats a big but), regenerating the map is very costly in performance even with a dozen of obstacles.
    Regnerating the navigation map in runtime, is to be done very seldom and is not really ment to be used for creating dynamic obstacle avoidance.

    So with that said, probably (and unfortunately :) ) PolyNav will not meet your needs due to the very dynamic (and frequency of it) nature of your game.

    A lot of optimizations and features are planned for version 2 that cover these things, but version 2 is still far ahead for now.

    Thanks!
     
  37. Sh4D

    Sh4D

    Joined:
    Dec 8, 2014
    Posts:
    17
    Thanks for the quick answer. I could use polynav as the base of my pathfinding code if you dont mind me changing the source code? I could regenerate the navigation map only when an obstacle gameobject is destroyed, and upadte current paths at that moment or use check points or don't update path until a new one is defined? Also I don't want to create a gameobject for avoidance but I already have one for other purposes, but as you said I can change the source code if you don't mind.
     
  38. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hello,
    The source code is included and you are of course completely free to change it to fit your needs :)

    Cheers!
     
  39. bojjenclon

    bojjenclon

    Joined:
    Feb 18, 2015
    Posts:
    2
    Is there a way to keep the map from automatically generating whenever an obstacle is added/removed? I'd prefer to manually tell PolyNav when to generate its map, rather than having it do it on its own. I know PolyNav isn't exactly made for dynamic content, but I feel it would be fine with the system I'm using (level chunks) if I could just tell it to wait until all objects in the chunk are loaded before generating its map.

    Also, I suppose "stopping distance" would work. I think what I really wanted is something PolyNav probably shouldn't worry about out of the box, but rather is a user-specific problem that needs to be handled case-by-case.
     
  40. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hello,

    Ah, I've missed checking the "Regenerate On Update" flag for the enabling/disabling the obstacles.
    Please open up PolyNav2D.cs and in LateUpdate at line #93 simply add:
    Code (CSharp):
    1.         if (!generateOnUpdate){
    2.             return;
    3.         }
    Then, call GenerateMap(false) whenever you like to regenerate/update it.

    Regarding stopping distance, can you please rephrase the question?

    Thanks
     
  41. pmbigham

    pmbigham

    Joined:
    Feb 18, 2015
    Posts:
    1
    Can I put multiple Polygon Collider 2D components on a single PolyNav2D and if they overlap it will move an agent between them?
     
  42. johanneskopf

    johanneskopf

    Joined:
    Feb 16, 2015
    Posts:
    81
    Fixed the issues with Ferr2D, thanks!

    Is it possible to click on an obstacle and get the to the closest point on the direct path?
    So not to the closest path on invalid, but the closest point on the front side of something.
    (I want to move a ship to an island and stop right in front of the island, although i clicked on some point in the island)
    Thanks in advance, John
     
    Last edited: Jul 27, 2015
  43. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hello,
    There can only be one master "walkable" polygon.
    Is that what you mean?

    Hello,
    Yes. There is a check on the PolyNavAgent to Move to closer point if the destination requested to move is invalid.
    The option is called "Closer Point On Invalid" :)
     
  44. DoctorShinobi

    DoctorShinobi

    Joined:
    Oct 5, 2012
    Posts:
    219
    To anyone having trouble with Ferr2D integration, i've manged to come up with a 1 line hack that makes it work with Poly navmesh.

    go to Ferr2DT_PathTerrain.CS. Inside it look for RecreateCollider2D function. Look for this line :
    Code (csharp):
    1.  
    2. if (useEdgeCollider)
    3.  
    Now add the next line to the end of this condition's else code
    Code (csharp):
    1.  
    2. //fix collider to work with poly nav mesh
    3. poly.points = poly.points.Distinct().ToArray();
    4.  
    All it does is remove duplicate vertices and that seems to make it work. I haven't tested it with too many different configurations, so I can't gurantee it won't break anything. But so far it seems to work well.
     
    johanneskopf and nuverian like this.
  45. Sh4D

    Sh4D

    Joined:
    Dec 8, 2014
    Posts:
    17
    Hi, I worked around a good beginning of my pathfinder using polynav. I mostly removed a lot of things I wasn't needing and things are working quite well for now.
    Althought I spent some ammount of time trying to figure out why the InflatePolygon function doesn't work properly. Here is an image that will explain it better than thousand words.
    http://i.imgur.com/8Z5g1CL.jpg
    As you can see the polygons offset is larger up and down and thinner on left and right, which results into non existing obstacles when an agent walks/slides by a wall constucted with several sprites. I tried to figure out what was wrong in the function but I might be too tired... or maybe I missed something and you could point me in the right direction??
     
  46. cjacobwade

    cjacobwade

    Joined:
    Jun 8, 2013
    Posts:
    10
    I'm having an issue where polynav agents are sometimes teleported to origin on awake and then refuse to move afterwards. Have any similar issues popped up in the past?
     
  47. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hello,

    If you can PM me the changed code I could take a look at this and see what might potentialy be wrong here, but it's realy hard to tell without knowing what you have chagned :)

    Thanks.

    Hello,

    The only reason that agents would forcefully be teleported in awake, would be for restricting them to within the valid walkable areas of the map. So if in awake their current position is for example within an obsacle or outside of the PolyNav walkable polygon, they would be restricted within the closer possible valid position.
    Can you please double check if this is the case?

    Thanks
     
  48. Sh4D

    Sh4D

    Joined:
    Dec 8, 2014
    Posts:
    17
    Hey, I just removed things from the agent code for now. I did no change on the polynav script where the inflate function and other functions used for it are. I am sure this offset on obstacles problem doesn't come from my changes because it was already displaying like that before I changed anything. EDIT: I did further testing and I think the offset shape only works properly if all the polygon angles are the same... I wouldn't bother you if I didn't think this might be a problem for other people too. I found this out, it might help http://stackoverflow.com/questions/...ating-deflating-offsetting-buffering-polygons I will be looking more into it too.
     
    Last edited: Aug 7, 2015
  49. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    Hey,
    Im really sorry for the late reply. Being weekend I was away.
    So i just realized. I think you have your polygon rotated in some axis other than Z. Is that the case?
    (meaning that either x or y are not 0) :)
     
  50. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    @Sh4D Oh..Wrong call :) I just saw what you mean.
    Will have to take a further look into this. Thanks for the link too.