News Forums RAIN General Discussion and Troubleshooting Custom Navigator for A* Pathfinding Project

Tagged: 

This topic contains 47 replies, has 13 voices, and was last updated by  mandi 1 month, 2 weeks ago.

Viewing 15 posts - 16 through 30 (of 48 total)
  • Author
    Posts
  • #26034

    lordofduct
    Participant

    So, I played with Yuewah’s solution, the first version I downloaded did not work, just like CodersExpo had an issue.

    I read that Yuewah updated it, so I downloaded again, and it still didn’t work.

    I still brought the code for the AStarNavigator into my own project, as well as the AStarAIPath, and got it rigged up to work. It “sort of” worked, in that the Mob moved. But it didn’t move around the grid, but instead just tried to walk to <0,0,0>.

    Furthermore, I was not interested in using a solution that used the AIPath class from Aron Granber’s AStar project. As the documentation even says on it:
    “This script is not written for high performance, so I do not recommend using it for large groups of units.”
    http://arongranberg.com/astar/docs/class_a_i_path.php

    I went and I implemented my own navigator that avoids the use of AIPath, this is my first pass run at it, and it works. I’ve tested it on a 3d plane, with CharacterControllerMotor (as well as my custom motor), and attached SimpleSmoother to it as well, and it all seemed to work fine.

    Seeker needs to be attached to your rig, but you can attach it wherever, and just attach the reference to the appropriate field in the navigator tab of the Rig AI component.

    using UnityEngine;
    using RAIN.Core;
    using RAIN.Serialization;
    using RAIN.Navigation;
    using RAIN.Navigation.Graph;
    using RAIN.Navigation.Pathfinding;
    using RAIN.Motion;
    namespace com.apoc.AI
    {
        [RAINSerializableClass]
        public class AGAStarNavigator : RAINNavigator
        {
            #region Fields
            [RAINSerializableField()]
            private Seeker _seeker;
            [RAINSerializableField()]
            private float _waypointCloseEnoughDistance = 0.5f;
            private bool _initialized = false;
            private bool _isSeeking = false;
            private Vector3 _targetSeeking = VectorUtil.PosInfVector3;
            private Pathfinding.Path _path;
            private int _currentPathIndex;
            #endregion
            #region Properties
            public Seeker Seeker { get { return _seeker; } }
            public float WaypointCloseEnoughDistance
            {
                get { return _waypointCloseEnoughDistance; }
                set { _waypointCloseEnoughDistance = value; }
            }
            #endregion
            #region Methods
            public override void ReInit()
            {
                base.ReInit();
                _initialized = false;
                this.Initialize();
            }
            private void Initialize()
            {
                if (_initialized) return;
                _seeker.pathCallback -= OnPathComplete;
                _seeker.pathCallback += OnPathComplete;
                _isSeeking = false;
                _initialized = true;
            }
            private void OnPathComplete(Pathfinding.Path p)
            {
                _path = p;
                _currentPathIndex = 0;
                _isSeeking = false;
            }
            private void UpdateTargetSeeking(Vector3 fromPos, Vector3 toPos)
            {
                _isSeeking = true;
                _targetSeeking = toPos;
                _path = null;
                _seeker.StartPath(fromPos, toPos);
            }
            #endregion
            #region RAINNavigator Overrides
            public override RAINPath CurrentPath
            {
                get;
                set;
            }
            public override MoveLookTarget GetNextPathWaypoint(bool allow3DMovement, global::RAIN.Motion.MoveLookTarget cachedMoveLookTarget = null)
            {
                this.Initialize();
                var moveTarget = cachedMoveLookTarget ?? new MoveLookTarget();
                if (this.pathTarget == null || !this.pathTarget.IsValid)
                {
                    moveTarget.TargetType = MoveLookTarget.MoveLookTargetType.None;
                    return moveTarget;
                }
                var pos = this.pathTarget.Position;
                if (!Mathf.Approximately((pos - _targetSeeking).sqrMagnitude, 0.0f))
                {
                    this.UpdateTargetSeeking(this.AI.Kinematic.Position, pos);
                }
                if (_isSeeking || _path == null || !InBounds(_currentPathIndex, _path.vectorPath.Count))
                {
                    moveTarget.TargetType = MoveLookTarget.MoveLookTargetType.None;
                    return moveTarget;
                }
                if ((_path.vectorPath[_currentPathIndex] - this.AI.Kinematic.Position).magnitude <= _waypointCloseEnoughDistance)
                {
                    _currentPathIndex++;
                    if (!InBounds(_currentPathIndex, _path.vectorPath.Count))
                    {
                        //moveTarget.TargetType = MoveLookTarget.MoveLookTargetType.Vector;
                        //moveTarget.VectorTarget = _path.vectorPath[_currentPathIndex];
                        //return moveTarget;
                        moveTarget.TargetType = MoveLookTarget.MoveLookTargetType.None;
                        return moveTarget;
                    }
                }
                moveTarget.VectorTarget = _path.vectorPath[_currentPathIndex];
                return moveTarget;
            }
            public override bool IsAt(global::RAIN.Motion.MoveLookTarget aPosition)
            {
                float d = Mathf.Max(aPosition.CloseEnoughDistance, this.AI.Motor.CloseEnoughDistance);
                if (this.AI.Motor.Allow3DMovement)
                {
                    return (this.AI.Kinematic.Position - aPosition.Position).magnitude <= d;
                }
                else
                {
                    Vector3 position = aPosition.Position;
                    position.y = this.AI.Kinematic.Position.y;
                    var v = this.AI.Kinematic.Position - position;
                    return v.magnitude <= d;
                }
            }
            public override RAINNavigationGraph CurrentGraph
            {
                get;
                set;
            }
            public override Vector3 ClosestPointOnGraph(Vector3 aPosition, float aMaxYOffset)
            {
                if (this.CurrentGraph != null)
                    return this.CurrentGraph.ClosestPointOnGraph(aPosition, aMaxYOffset);
                else
                    return aPosition;
            }
            public override MoveLookTarget GetNextPathPosition(bool allow3DMovement)
            {
                return null;
            }
            public override bool GetPathTo(Vector3 position, int maxPathfindSteps, out global::RAIN.Navigation.Pathfinding.RAINPath path)
            {
                path = null;
                return true;
            }
            public override bool GetPathToMoveTarget(out global::RAIN.Navigation.Pathfinding.RAINPath path)
            {
                path = null;
                return true;
            }
            public override bool IsPathfinding
            {
                get { return true; }
            }
            public override bool OnGraph(Vector3 aPosition, float aMaxYOffset)
            {
                return true;
            }
            public override void RestartPathfindingSearch()
            {
            }
            #endregion
    		#region Utils
    		//this is usually in my own utils class, put here for sharing on forum
    		private static bool InBounds(int a, int max)
    		{
    			return a >= 0 && a < max;
    		}
    		#endregion
        }
    }
    • This reply was modified 1 year, 2 months ago by  lordofduct.
    • This reply was modified 1 year, 2 months ago by  lordofduct.
    • This reply was modified 1 year, 2 months ago by  lordofduct.
    #26037

    lordofduct
    Participant

    I’m not done yet though, this was just my basic proof of concept for it.

    I verified what Yuewah said about most of the methods not being referenced by RAIN much actually.

    CurrentPath
    GetNextPathWaypoint
    IsAt

    are the only ones that seem to be referenced for use by the motor. The rest seem to be used by BasicNavigator, but are defined as part of the RAINNavigator contract for whatever reason.

    Thing is, I don’t use CurrentPath at all for anything, I never set it or anything, and have no idea what the motor uses it for. Everything seems to work without setting it. I plan to look further into it further, but don’t have the time right now to do so.

    The rest of the methods… I don’t know why RivalTheory but all these methods in the RAINNavigator abstract contract. For that matter I don’t know why so many contracts are defined as abstract classes, yet are 100% abstract… why not an interface??? If I wanted to say implement a RAINPath that integrated with AStar, I’d have to write a wrapper class, as opposed to just implement an interface. Maybe they just did it for consistency sake or something (many contracts are partially implemented). Whichever…

    #26040

    Yuewah Chan
    Participant

    @lordofduct, it is glad to hear that you did it in your own implementation. Just to verify, do you try to open my project scene to test ?

    #26044

    lordofduct
    Participant

    Yes I did.

    The bear just stands there playing the look around animation.

    #26083

    Scrambler
    Participant

    lordofduct,

    I’ve implemented AStarPro in our project too. I use Rain for its BT implementation. We have specific requirements that our project has that require support for multiple NavMesh graphs and 64+ layeredGrid graphs.

    I decided to stick with using AIPath and seeker directly. I then made a custom NullMotor and attached it to Rain’s agent that simply skips any modification of position or rotation from Rain.

    Most of the BT functions I’m using are the decision nodes and custom actions.

    This works well for our project.

    -E

    #35958

    ninjapps
    Participant

    Anyone can explain how to implement this in the new Rain version with the latest A *? I have downloaded the files but I can’t seem to figure out which files are used to integrate that dropdown in the Navigation tab.

    What am I missing?

    • This reply was modified 5 months, 1 week ago by  ninjapps.
    #36108

    ninjapps
    Participant

    ** Update ** Ok I have figured out that the script just needs to be dropped in Unity which is great but I am getting a bunch of errors and I bet they all relate to this error.

    AstarNavigator.cs(26,34): error CS0115: `RAIN.Navigation.AstarNavigator.ReInit()' is marked as an override but no suitable method found to override

    This script should be kept updated as honestly these 2 solutions together are dynamite!

    Anyways would appreciate any help given.

    • This reply was modified 5 months, 1 week ago by  ninjapps.
    #36182

    ninjapps
    Participant

    ** FINAL UPDATE **

    Prime helped in identifying the issue so here is the latest script to replace the AstarNavigator script.

    using RAIN.Motion;
    using RAIN.Navigation.Graph;
    using RAIN.Navigation.Pathfinding;
    using RAIN.Serialization;
    using UnityEngine;
    namespace RAIN.Navigation
    {
    	[RAINSerializableClass]
    	public class AstarNavigator : RAINNavigator {
    		public override RAINPath CurrentPath { get; set; }
    		public override RAINNavigationGraph CurrentGraph { get; set; }
    		public override bool IsPathfinding { get { return true;} }
    		public AstarAIPath astarAIPath;
    		private MoveLookTarget moveLookTarget = new MoveLookTarget();
    		public override void AIInit()
    		{	
    			astarAIPath = base.AI.Body.GetComponentInChildren<AstarAIPath>();
    			base.AIInit();
    		}
    		public override bool OnGraph(Vector3 aPosition, float aMaxYOffset = 0.0f){
    			return true;
    		}
    		public override Vector3 ClosestPointOnGraph(Vector3 aPosition, float aMaxYOffset = 0.0f){
    			return aPosition;
    		}
    		public override bool IsAt(MoveLookTarget aPosition)
    		{
    			return true;
    		}
    		public override MoveLookTarget GetNextPathWaypoint(bool allow3DMovement, bool allowOffGraphMovement, MoveLookTarget moveLookTarget){
    			//			Debug.Log( "GetNextPathWaypoint" );
    			//			Debug.Log( "target=" + astarAIPath.target );
    			//			Debug.Log( "pathTarget=" + this.pathTarget.Position );
    			if (this.pathTarget == null || !this.pathTarget.IsValid)
    				return (MoveLookTarget) null;
    			astarAIPath.TargetPosition = this.pathTarget.Position;
    			moveLookTarget.VectorTarget = astarAIPath.TargetPoint;
    			//				Debug.Log( astarAIPath.TargetPosition );
    			//				Debug.Log( string.Format("({0:F4},{1:F4},{2:F4})", astarAIPath.TargetPoint.x, astarAIPath.TargetPoint.y, astarAIPath.TargetPoint.z));
    			return moveLookTarget;
    		}
    		public override bool GetPathToMoveTarget(bool allowOffGraphMovement, out RAINPath path)
    		{
    			path = null;
    			return true;
    		}
    		public override void RestartPathfindingSearch()
    		{
    		}
    		public override bool GetPathTo(Vector3 position, int maxPathfindSteps, bool allowOffGraphMovement, out RAINPath path)
    		{
    			path = null;
    			return true;
    		}
    	}
    }
    • This reply was modified 5 months, 1 week ago by  ninjapps.
    #36220

    Castor
    Participant

    I would like to add to this thread.

    I think the reason why people are trying to integrate both Rain and A* project is that they offer features that compliments each other weakness.

    A*path project doesn’t have nice BT editor and behaviour stuff that Rain has.

    On the other hand, Rain does offer decent path finding features of its own, but it lacks some of critical features that A* offers.

    I am not 100% certain and Rain may support them already, but
    Some of them are things like :

    Dynamic Navmesh cutting
    Dynamic local avoidance
    Ability to save and load navmesh data to/from file

    If Rain can support these and do them well enough to match / surpass in terms of performance and usability there should be very little reason why would anyone to just use Rain all together.

    Also , Rain lacks documentation on “how to” write own custom stuff too. More videos would be nice as well.

    But having said that Rain still offers a lot, just a bit further and it would be really cool.

    • This reply was modified 5 months, 1 week ago by  Castor.
    #36224

    ninjapps
    Participant

    Yes @Castor I agree!

    Another feature about A* which forced me to use it is the graph linking and generation, its too flexible especially when you have multiple levels on top of each other. While this will not affect an FPS or a 3rd person game, its essential for strategy and rpg games.

    • This reply was modified 5 months ago by  ninjapps.
    • This reply was modified 5 months ago by  ninjapps.
    #36232

    prime
    Keymaster

    Thanks for the comments @Castor and @ninjapps. Many of those features are on our roadmap and we’re getting to them as quickly as we can. If someone in the community wants to take on some ownership of maintaining an up-to-date and compatible Navigator that works with Aron’s plugin, then we’re certainly willing to make it available for download from our site (and give some promotion and credit to whoever does it of course.)

    #36244

    Yuewah Chan
    Participant

    @ninjapps, your script is outdated for RAIN >= 2.1.8, the Move node will stop immediately, it should be updated with the bool IsAt(MoveLookTarget aPosition)

    public override bool IsAt(MoveLookTarget aPosition)
            {
                float closeEnoughDistance = Mathf.Max(aPosition.CloseEnoughDistance, this.AI.Motor.CloseEnoughDistance);
                bool result = false;
                if (this.AI.Motor.Allow3DMovement)
                {
                    if ((this.AI.Kinematic.Position - aPosition.Position).magnitude <= closeEnoughDistance)
                        result = true;
                }
                else
                {
                    Vector3 position = aPosition.Position;
                    position.y = this.AI.Kinematic.Position.y;
                    if ((this.AI.Kinematic.Position - position).magnitude <= closeEnoughDistance)
                        result = true;
                }
                MoveLookTarget nextPathWaypoint = this.GetNextPathWaypoint(this.AI.Motor.Allow3DMovement, this.AI.Motor.AllowOffGraphMovement, null);
                if (nextPathWaypoint == null || !nextPathWaypoint.IsValid)
                {
                    if (this.IsPathfinding)
                        result = false;
                }
                return result;
            }
    • This reply was modified 5 months ago by  Yuewah Chan.
    #36399

    ninjapps
    Participant

    Thanks Yuewah! Will giv eit a go. It would be great if you can mantain your package buddy as this is a life saver! perhaps prime as he said can have it as an important post within the forum for other users.

    Cheers.

    #36402

    ninjapps
    Participant

    Looks like that change made it works with the final version. Thanks for your feedback Yuewah!

    • This reply was modified 5 months ago by  ninjapps.
    #36639

    CodersExpo
    Participant

    Hey thanks to all of you for keeping up with this thread. I feel there is some real value to be had here.
    Special thanks to Yuewah Chan for getting us all pointed in the right direction and LordOfDuct for exploring the possibilities!

    I took your lead Yuewah, of moving to a target, and created a RAIN waypoint collection that we can patrolled around.

    To set up,

    1. Start a project and bring in the A* project package and RAIN package.
    2. Follow the instructions for setting up the A* project. (NOTE: in the YT vid where he starts writing his script to work with the seeker just stop.)
    * http://www.youtube.com/watch?feature=player_embedded&v=PUJSvd53v4k - YT vid setting up A*
    * http://arongranberg.com/astar/docs/getstarted.php - Aron A* project
    3. Once you have the A* project set up with a graph generated and your ground and obstacles defined…. Create a capsule character game object to act as our character. Select this character and add the RAIN AI Rig component.
    4. Select the child component ‘AI’ and click the ‘Navigation’ icon. In the dropdown select the AstarNavigator script.
    (MY MODIFIED VERSION OF THE CODE IS PROVIDED BELOW. SAVE THIS TO YOUR ASSETS FOLDER NAMED AstarNavigator.cs)
    5. Just below the AI Rig drag and drop on the AstarAIPath script.
    (MY MODIFIED VERSION OF THE CODE IS PROVIDED BELOW. SAVE THIS TO YOUR ASSETS FOLDER NAMED AstarAIPath.cs)
    6. This should have included the Seeker.cs. You should now have below the RAIN AI Rig the AstarAIPath and Seeker scripts.
    7. Now create a RAIN waypoint set. Add several waypoints and name the waypoint set. Enter the same name of this waypoint set in the AstarAIPath script variable ‘Way Point Route Name’ found below the AI Rig component in the inspector.
    8. Next create a behavior tree. Assign this new behavior tree to your AI character.
    The Behavior tree in this example is simple:
    Root
    ….SEQ
    ……..WAY (Node Type=Patrol, Repeat=Never, Waypoint Route=”Waypoint Route” (Note this is the same name to add in our AstarAIPath as defined above), Loop forward, Move Target Varriable = varNextPoint
    ………….MOVE Move Target = varNextPoint
    9. Again, make sure this behavior tree is assigned to the AI Rig Mind

    You should be able to run this and watch your dude move from waypoint to waypoint.

    ********Scripts follow in the next few threads due to length restrictions************
    Copy save the first
    The second script had to be split in two posts. Just follow the instructions.

    • This reply was modified 4 months, 2 weeks ago by  CodersExpo.
    • This reply was modified 4 months, 2 weeks ago by  CodersExpo.
Viewing 15 posts - 16 through 30 (of 48 total)

You must be logged in to reply to this topic.