News › Forums › RAIN › General Discussion and Troubleshooting › walk waypoint network (beginner)
Tagged: waypoint network
This topic contains 22 replies, has 4 voices, and was last updated by prime 1 year ago.
-
AuthorPosts
-
January 14, 2023 at 1:02 am #35073
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.
January 14, 2023 at 8:29 am #35079In 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; } }
January 14, 2023 at 8:32 am #35080I 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)January 14, 2023 at 1:51 pm #35087WoW…thanks for the quick reply and the code….i will implement it this weekend and will get back to you, how it worked.
January 16, 2023 at 12:32 pm #35109Yes..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.
January 17, 2023 at 8:28 am #35114I’ll test it this weekend and post an update if I find an error. Thx
January 18, 2023 at 6:04 pm #35120I’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; } }
January 19, 2023 at 8:25 am #35126Prime, 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.
January 21, 2023 at 9:46 am #35159What does “forward” mean? Do you mean you only want the AI to move to the endpoints of the path before choosing a new location?
January 21, 2023 at 12:38 pm #35172If 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.
January 22, 2023 at 8:01 am #35202Simple 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.
January 22, 2023 at 10:40 am #35217Changed the code and got the error
(67,64) error CS0104: Random is an ambiguos reference between unityengine.Random and System.randomSo 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.January 25, 2023 at 8:02 am #35270I 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.
January 26, 2023 at 10:27 am #35303Hmm. @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.
January 26, 2023 at 11:18 am #35307I 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.
-
AuthorPosts
You must be logged in to reply to this topic.