News Forums RAIN General Discussion and Troubleshooting walk waypoint network (beginner)

This topic contains 22 replies, has 4 voices, and was last updated by  prime 1 year ago.

Viewing 15 posts - 1 through 15 (of 23 total)
  • Author
    Posts
  • #35073

    Arno1975
    Participant

    I had some code , using the buildin unity pathfinding.
    But i wan’t to switch to RAIN, but having trouble to get started.

    I want to achieve the same thing i had with my custom code.

    My AI just needs to walk the waypoint network, if it gets to a waypoint, it randomly chooses its next waypoint which is connected to that waypoint. (but not turn back)
    It doesn’t sound that hard, but i’am having trouble getting started.

    I looked at the wander tutorial, but in this tutorial it is not staying on it’s path and chooses his next point randomly and doesn’t look at the waypoint connected to that waypoint.
    I want him to just follow the network.

    hope someone can help.

    #35079

    prime
    Keymaster

    In this case, you probably want the following setup:
    1) Create a waypoint network so you can manage positions/connections.
    2) Instead of using a waypoint node in your behavior tree, create a custom node that picks a connected waypoint to walk to.
    3) Walk to that waypoint directly (again, not using a waypoint node)
    4) Repeat from (2)

    You custom code would look something like this. NOTE - I haven’t tested this. I just wrote it up, and the code is a little tricky. The idea is that the node will remember the last waypoint it chose and will choose a new waypoint connected to the last one. WaypointSets that represent networks have a Graph associated with them. You can use the graph to get connected waypoints. Because of the construction of WaypointGraphs, there is a direct mapping between waypoint indices and graph node indices.

    using UnityEngine;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using RAIN.Action;
    using RAIN.Core;
    using RAIN.Representation;
    using RAIN.Motion;
    using RAIN.Navigation;
    using RAIN.Navigation.Graph;
    using RAIN.Navigation.Waypoints;
    [RAINAction("Choose Next Waypoint")]
    public class ChooseWaypoint : RAINAction
    {
        public Expression WaypointNetwork = new Expression(); //either the name of the network or a variable containing a network
        public Expression MoveTargetVariable = new Expression(); //the variable you want to use for the output move target
        private MoveLookTarget moveTarget = new MoveLookTarget();
        private int lastWaypoint = -1;
        private WaypointSet lastWaypointSet = null;
        public override ActionResult Execute(AI ai)
        {
            if (!MoveTargetVariable.IsValid || !MoveTargetVariable.IsVariable)
                return ActionResult.FAILURE;
            WaypointSet waypointSet = GetWaypointSetFromExpression(ai);
            if (waypointSet == null)
                return ActionResult.FAILURE;
            if (waypointSet != lastWaypointSet)
            {
                lastWaypoint = -1;
                lastWaypointSet = waypointSet;
            }
            if (lastWaypoint == -1)
            {
                lastWaypoint = waypointSet.GetClosestWaypointIndex(ai.Kinematic.Position);
                if (lastWaypoint < 0)
                    return ActionResult.FAILURE;
                moveTarget.VectorTarget = waypointSet.Waypoints[lastWaypoint].position;
                moveTarget.CloseEnoughDistance = Mathf.Max(waypointSet.Waypoints[lastWaypoint].range, ai.Motor.CloseEnoughDistance);
                if (!ai.Motor.IsAt(moveTarget))
                {
                    ai.WorkingMemory.SetItem<MoveLookTarget>(MoveTargetVariable.VariableName, moveTarget);
                    return ActionResult.SUCCESS;
                }
            }
            Waypoint tCurrent = waypointSet.Waypoints[lastWaypoint];
            NavigationGraphNode tNode = waypointSet.Graph.GetNode(lastWaypoint);
            if (tNode.OutEdgeCount > 0)
            {
                int tRandomEdge = UnityEngine.Random.Range(0, tNode.OutEdgeCount - 1);
                lastWaypoint = ((VectorPathNode)tNode.EdgeOut(tRandomEdge).ToNode).NodeIndex;
            }
            moveTarget.VectorTarget = waypointSet.Waypoints[lastWaypoint].position;
            moveTarget.CloseEnoughDistance = Mathf.Max(waypointSet.Waypoints[lastWaypoint].range, ai.Motor.CloseEnoughDistance);
            ai.WorkingMemory.SetItem<MoveLookTarget>(MoveTargetVariable.VariableName, moveTarget);
            return ActionResult.SUCCESS;
        }
        private WaypointSet GetWaypointSetFromExpression(AI ai)
        {
            WaypointSet waypointSet = null;
            if (WaypointNetwork != null && WaypointNetwork.IsValid)
            {
                if (WaypointNetwork.IsVariable)
                {
                    string varName = WaypointNetwork.VariableName;
                    if (ai.WorkingMemory.ItemExists(varName))
                    {
                        Type t = ai.WorkingMemory.GetItemType(varName);
                        if (t == typeof(WaypointRig) || t.IsSubclassOf(typeof(WaypointRig)))
                        {
                            WaypointRig wgComp = ai.WorkingMemory.GetItem<WaypointRig>(varName);
                            if (wgComp != null)
                                waypointSet = wgComp.WaypointSet;
                        }
                        else if (t == typeof(WaypointSet) || t.IsSubclassOf(typeof(WaypointSet)))
                        {
                            waypointSet = ai.WorkingMemory.GetItem<WaypointSet>(varName);
                        }
                        else if (t == typeof(GameObject))
                        {
                            GameObject go = ai.WorkingMemory.GetItem<GameObject>(varName);
                            if (go != null)
                            {
                                WaypointRig wgComp = go.GetComponentInChildren<WaypointRig>();
                                if (wgComp != null)
                                    waypointSet = wgComp.WaypointSet;
                            }
                        }
                        else
                        {
                            string setName = ai.WorkingMemory.GetItem<string>(varName);
                            if (!string.IsNullOrEmpty(setName))
                                waypointSet = NavigationManager.Instance.GetWaypointSet(setName);
                        }
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(varName))
                            waypointSet = NavigationManager.Instance.GetWaypointSet(varName);
                    }
                }
                else if (WaypointNetwork.IsConstant)
                {
                    waypointSet = NavigationManager.Instance.GetWaypointSet(WaypointNetwork.Evaluate<string>(0, ai.WorkingMemory));
                }
            }
            return waypointSet;
        }
    }
    #35080

    prime
    Keymaster

    I know that seems like a lot of code for something pretty simply (cringeworthy on my part). What it actually does is pretty straightforward though, and we plan to add some support into RAIN to make this sort of thing far simpler.

    To use the custom node, just add it to your BT. Fill in the waypoint network name and the name of the variable you want to use as a move target. Then follow it up with a Move node that uses the same target:

    Sequencer (repeat)
    — Choose Next Waypoint (MoveTargetVariable = moveTarget)
    — Move (MoveTarget = moveTarget)

    #35087

    Arno1975
    Participant

    WoW…thanks for the quick reply and the code….i will implement it this weekend and will get back to you, how it worked.

    #35109

    Arno1975
    Participant

    Yes..He is walking…2 small problems.
    Get an error: tCurrent is assigned but its value is never used.
    This is correct. sometimes the capsule travels back to the waypoint it came from.
    and i watched him for over 5 minutes and he never traveled to the points in the blue circels.

    #35114

    prime
    Keymaster

    I’ll test it this weekend and post an update if I find an error. Thx

    #35120

    prime
    Keymaster

    I’ve tested the code. Works with two minor changes. (1) I forgot that the Random.Range(int, int) call is exclusive of the 2nd int. That means the code picking a random edge to traverse is dropping off the last edge. (2) The tCurrent line can be removed.

    Here’s the updated code:

    using UnityEngine;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using RAIN.Action;
    using RAIN.Core;
    using RAIN.Representation;
    using RAIN.Motion;
    using RAIN.Navigation;
    using RAIN.Navigation.Graph;
    using RAIN.Navigation.Waypoints;
    [RAINAction("Choose Next Waypoint")]
    public class ChooseWaypoint : RAINAction
    {
        public Expression WaypointNetwork = new Expression(); //either the name of the network or a variable containing a network
        public Expression MoveTargetVariable = new Expression(); //the variable you want to use for the output move target
        private MoveLookTarget moveTarget = new MoveLookTarget();
        private int lastWaypoint = -1;
        private WaypointSet lastWaypointSet = null;
        public override ActionResult Execute(AI ai)
        {
            if (!MoveTargetVariable.IsValid || !MoveTargetVariable.IsVariable)
                return ActionResult.FAILURE;
            WaypointSet waypointSet = GetWaypointSetFromExpression(ai);
            if (waypointSet == null)
                return ActionResult.FAILURE;
            if (waypointSet != lastWaypointSet)
            {
                lastWaypoint = -1;
                lastWaypointSet = waypointSet;
            }
            if (lastWaypoint == -1)
            {
                lastWaypoint = waypointSet.GetClosestWaypointIndex(ai.Kinematic.Position);
                if (lastWaypoint < 0)
                    return ActionResult.FAILURE;
                moveTarget.VectorTarget = waypointSet.Waypoints[lastWaypoint].position;
                moveTarget.CloseEnoughDistance = Mathf.Max(waypointSet.Waypoints[lastWaypoint].range, ai.Motor.CloseEnoughDistance);
                if (!ai.Motor.IsAt(moveTarget))
                {
                    ai.WorkingMemory.SetItem<MoveLookTarget>(MoveTargetVariable.VariableName, moveTarget);
                    return ActionResult.SUCCESS;
                }
            }
            //**REMOVED EXTRA LINE HERE
            NavigationGraphNode tNode = waypointSet.Graph.GetNode(lastWaypoint);
            if (tNode.OutEdgeCount > 0)
            {
                int tRandomEdge = UnityEngine.Random.Range(0, tNode.OutEdgeCount); //**FIXED THIS LINE
                lastWaypoint = ((VectorPathNode)tNode.EdgeOut(tRandomEdge).ToNode).NodeIndex;
            }
            moveTarget.VectorTarget = waypointSet.Waypoints[lastWaypoint].position;
            moveTarget.CloseEnoughDistance = Mathf.Max(waypointSet.Waypoints[lastWaypoint].range, ai.Motor.CloseEnoughDistance);
            ai.WorkingMemory.SetItem<MoveLookTarget>(MoveTargetVariable.VariableName, moveTarget);
            return ActionResult.SUCCESS;
        }
        private WaypointSet GetWaypointSetFromExpression(AI ai)
        {
            WaypointSet waypointSet = null;
            if (WaypointNetwork != null && WaypointNetwork.IsValid)
            {
                if (WaypointNetwork.IsVariable)
                {
                    string varName = WaypointNetwork.VariableName;
                    if (ai.WorkingMemory.ItemExists(varName))
                    {
                        Type t = ai.WorkingMemory.GetItemType(varName);
                        if (t == typeof(WaypointRig) || t.IsSubclassOf(typeof(WaypointRig)))
                        {
                            WaypointRig wgComp = ai.WorkingMemory.GetItem<WaypointRig>(varName);
                            if (wgComp != null)
                                waypointSet = wgComp.WaypointSet;
                        }
                        else if (t == typeof(WaypointSet) || t.IsSubclassOf(typeof(WaypointSet)))
                        {
                            waypointSet = ai.WorkingMemory.GetItem<WaypointSet>(varName);
                        }
                        else if (t == typeof(GameObject))
                        {
                            GameObject go = ai.WorkingMemory.GetItem<GameObject>(varName);
                            if (go != null)
                            {
                                WaypointRig wgComp = go.GetComponentInChildren<WaypointRig>();
                                if (wgComp != null)
                                    waypointSet = wgComp.WaypointSet;
                            }
                        }
                        else
                        {
                            string setName = ai.WorkingMemory.GetItem<string>(varName);
                            if (!string.IsNullOrEmpty(setName))
                                waypointSet = NavigationManager.Instance.GetWaypointSet(setName);
                        }
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(varName))
                            waypointSet = NavigationManager.Instance.GetWaypointSet(varName);
                    }
                }
                else if (WaypointNetwork.IsConstant)
                {
                    waypointSet = NavigationManager.Instance.GetWaypointSet(WaypointNetwork.Evaluate<string>(0, ai.WorkingMemory));
                }
            }
            return waypointSet;
        }
    }
    #35126

    Arno1975
    Participant

    Prime, the code is working no errors anymore.
    I’am very happy with it.
    just one request, maybe i explained it wrong in my last post.
    I don’t want the AI to return to the waypoint it came from.
    it doesn’t do a ping pong, the AI alway needs to go “forward”

    I can follow your script for about 50%, so i don’t now how to adjust this on my own.
    Hope you still have some time to look into this.

    Thanks for all the work you have done for me.

    #35159

    prime
    Keymaster

    What does “forward” mean? Do you mean you only want the AI to move to the endpoints of the path before choosing a new location?

    #35172

    Arno1975
    Participant

    If i got three waypoints 1, 2, 3,4. Then when the Ai is moving from 1 to 2. When he or she arives at waypoint to he can only go to waypoint 3 or 4, not back to 1.

    #35202

    prime
    Keymaster

    Simple enough. Make this change:

    NavigationGraphNode tNode = waypointSet.Graph.GetNode(lastWaypoint);
            if (tNode.OutEdgeCount > 0)
            {
                int tRandomEdge = UnityEngine.Random.Range(0, tNode.OutEdgeCount); //**FIXED THIS LINE
                lastWaypoint = ((VectorPathNode)tNode.EdgeOut(tRandomEdge).ToNode).NodeIndex;
            }

    becomes

    NavigationGraphNode tNode = waypointSet.Graph.GetNode(lastWaypoint);
            if (tNode.OutEdgeCount > 0)
            {
                List<int> tConnectedNodes = new List<int>();
                for (int k = 0; k < tNode.OutEdgeCount; k++)
                {
                  int tIndex = ((VectorPathNode)tNode.EdgeOut(k).ToNode).NodeIndex;
                  if ((tIndex != lastWaypoint) && (!tConnectedNodes.Contains(tIndex)))
                    tConnectedNodes.Add(tIndex);
                }
                if (tConnectedNodes.Count == 0)
                  lastWaypoint = ((VectorPathNode)tNode.EdgeOut(0).ToNode).NodeIndex;
                else
                  lastWaypoint = tConnectedNodes[Random.Range(0, tConnectedNodes.Count)];
            }
    • This reply was modified 1 year, 5 months ago by  prime.
    #35217

    Arno1975
    Participant

    Changed the code and got the error
    (67,64) error CS0104: Random is an ambiguos reference between unityengine.Random and System.random

    So i changed Random.Range to UnityEngine.Random.Range
    This resolves the console error but the AI still returns sometimes to the waypoint it came from.

    #35270

    tyoc213
    Participant

    I don’t know if RAIN have a next previous , but you can do previous->previous->… and so on to see if the anterior path is in the previous route.

    If not, you maybe can do one and use it for search before select the next new node.

    #35303

    prime
    Keymaster

    Hmm. @tyoc213 - you’re on the right track. The code I posted doesn’t keep enough history. I’m forgetting that “lastWaypoint” is actually the waypoint we just arrived at, not the one we were at previously. To fix this, you just need to record the actual prior waypoint, and then this line:

    if ((tIndex != lastWaypoint) && (!tConnectedNodes.Contains(tIndex)))
                    tConnectedNodes.Add(tIndex);

    would be

    if ((tIndex != priorWaypoint) && (!tConnectedNodes.Contains(tIndex)))
                    tConnectedNodes.Add(tIndex);

    then at the bottom there you would update priorWaypoint = lastWaypoint, etc.

    #35307

    tyoc213
    Participant

    I was thinking this moment… is possible to add “properties” to waypoints and so on? then you can add “visited” or “visitedBy” which is a list of things that have visited certain point… then you can reset/delete those properties when needed to go back or start again.

Viewing 15 posts - 1 through 15 (of 23 total)

You must be logged in to reply to this topic.