News › Forums › RAIN › General Discussion and Troubleshooting › Keep looking for target for a while after losing him from view
Tagged: follow, last position, Patrol, Repeat
This topic contains 3 replies, has 2 voices, and was last updated by Sigil 1 month, 2 weeks ago.
September 16, 2022 at 9:23 am #39099
Hi! You’ll see, i hope you can help me. I’m pretty new to coding & behaviour trees, so i’m having a hard time trying to fix this…
I have an IA and a First Person Character controler, the behaviour i wan’t is that the IA should patrol & when he sees the player, go after him while he can see him, this works ok, problem is that when he’s out of his vision range & line of sight he goes inmediatly back to the patrol, this is an specially noisy thing since there are tons of narrow corners, props & stuff that causes this issue to be very frequent. This is my Behaviour Tree, which is not working:
September 20, 2022 at 4:19 pm #39170
- This topic was modified 1 month, 3 weeks ago by LudwigVanKinder.
Let’s reorganize your tree a little to get this to work right, and then I’ll add the bit to record the last position of the AI. For my purposes I may be using different variable names, but you can switch them around:
root sequencer parallel (succeed: any, failure: any, tie breaker: succeed) detect (repeat until success, aspect: "player", form variable: playerTarget) waypoint patrol (waypoint route: "Waypoint Route", move target variable: patrolTarget) move (move target: patrolTarget) parallel (succeed: any, failure: any, tie breaker: succeed) detect (repeat until failure, aspect: "player", form variable: playerTarget) move (move target: playerTarget) custom action (InstantiateSound)
So this should pretty much be what your current tree was doing (minus a few things I see in it that look like you were testing something): patrolling while looking for the player, then chasing the player unless they can’t see them anymore.
So to get the last bit, you need to record the position of the player while you’re chasing them, just in case you lose them. There is an expression function for that called “position”. In the event that we lose the player, we’ll attempt to follow their last good position instead.
root sequencer parallel (succeed: any, failure: any, tie breaker: succeed) detect (repeat until success, aspect: "player", form variable: playerTarget) waypoint patrol (waypoint route: "Waypoint Route", move target variable: patrolTarget) move (move target: patrolTarget) selector parallel (succeed: any, failure: any, tie breaker: succeed) sequencer (repeat until failure) expression (expression: playerPosition = position(playerTarget), return: success) detect (aspect: "player", form variable: playerTarget) move (move target: playerTarget) custom action (InstantiateSound) parallel (succeed: any, failure: any, tie breaker: succeed) detect (repeat until success, aspect: "player", form variable: playerTarget) sequencer move (move target: playerPosition) timer (seconds: random(2, 3))
So I took the second parallel and put it in a selector to give us a failure case (the case where we lose track of the player). I changed the detect that was repeating until failure to a sequencer, so that I can record the position of the player, before attempting to detect them again.
In the second part of the selector, where we know we’ve lost sight of our player, I attempt to detect them again, while moving towards their last position. The timer is just to make the AI pause at the position, you could replace that with a look around animation or something similar.
I recommend you trying to get my first example tree working, then see if you can get the second one going. Let me know if you have questions/problems.September 22, 2022 at 9:08 pm #39194
Thnks for the reply, i figured it out by my own. As you may see i took the tree from the example at the tutorial section & expanded it upon my needs, This is the first time i get into Behaviour trees & whatever outside JS & C#. As you may see i’m having a hard time at it. however i got this to work, not in the best possible way, but whatever it works i guess, hehe. I’m open to learn how to program more efficient behavior trees & if you notice something stupid i’m doing (if not all) please let me know.
Also i have a few questions i’d love to be clarified about, if you don’t mind (the wiki is not very detailed about this)
having a node marked as a loop calls whatever is inside every frame like Update function?
how is a Timer node affected by a loop node?
is there a way of reseting the timer node?
Thank you very much
Root Parallel (repeat: never) Detect (repeat: forever, sensor: “Visual Sensor”, aspect: “person”, detect: Best match, form variable: PersonOfInterest) Selector (Repeat: forever) //Select Current State Constraint (PersonOfInterest==null) Selector (repeat: never) Constraint (stopSearching==true) //Patrolling Selector (repeat: never) Constraint (takingBreak==false) Parallel (repeat: never) Waypoint Patrol (Waypoint Route: myRoute, loop type: loop, direction: forward, Move Target Variable: moveTarget) Move (Move Target: moveTarget) Expression (Expression: countSecondsWalking=true) Constraint (takingBreak==true) Expression (Expression: countSecondsTakingBreak=true) Constraint (stopSearching==false) //Saw the character not so long ago so still looking for him Selector (repeat: never) Constraint (justSawHim==true) Parallel (repeat: never) Expression (Expression: countSecondsToLoseInterest=true) Expression (justSawHim=false) Constraint (justSawHim==false) Move (Move Target: character) Constraint (PersonOfInterest != null) //Is watching the character so it’s walking towards him Parallel (repeat: never) Move (Move Target: personOfInterest) Expression (stopSearching==false) Expression (justSawHim = true) Constraint (talkedToHim==false) parallel (repeat: never) Custom Action (triggers a script i made to play a random Audio Fx) Expression (talkedToHim=true) Selector (Repeat: forever) Constraint (countSecondsToLoseInterest=true) //this is called if the enemy just lost the character from sight Sequencer (Repeat: never) Timer (seconds: secondsToLoseInterest) Parallel (repeat: never) Constraint (stopSearching==false) Expression (stopSearching=true) Expression (talkedToHim=false) Expression (CountSecondsToLoseInterest=false) Selector (Repeat : forever) //this is called while walking to then switch to idle or “taking a break” Constraint (countSecondsWalking==true) Sequencer (repeat: never) Timer (seconds: secondsWalking) Parallel (repeat: never) Expression (takingBreak=true) Expression (countSecondsWalking=false) Selector (Repeat: forever) // this one is called when idle to know when to continue patrolling Constraint (countSecondsTakingBreak==true) Sequencer (Repeat:never) Timer (seconds: secondsTakingBreak) Parallel (repeat: never) Expression (countSecondsTakingBreak=false) Expression (takingBreak=false)
September 22, 2022 at 11:37 pm #39200
- This reply was modified 1 month, 2 weeks ago by Sigil. Reason: fixed formatting
I’ll have to come back and take a look at your tree as it is rather large. I’m glad it is working for you though.
As for your questions, these answers may lead to even more confusion, but I’ll try anyhow:
“having a node marked as a loop calls whatever is inside every frame like Update function?”
When a node is marked to repeat it runs like it always would, but when the node returns SUCCESS or FAILURE the behavior tree “pretends” it returned RUNNING instead, and the next Update it comes back to the same node again, and restarts it as if it was the first time it ran into it.
“how is a Timer node affected by a loop node?”
The timer will wait the designated time limit, and then return SUCCESS or FAILURE (depending on settings). If it is set to repeat forever, the tree will ignore the return value (it will return RUNNING instead), and start the timer over again. From the outside this will just look like a timer that is running forever.
“is there a way of resetting the timer node?”
A repeat is a good way of resetting a timer node, the following shows that:
root sequencer expression (expression: debug("Hi"), return: success) sequencer (repeat: forever) expression (expression: debug("Hello"), return: success) timer (seconds: 5)
This would print “Hi” to the console, followed by an additional “Hello” every five seconds. Every time the timer finishes and returns success, the repeat on the sequencer kicks in and resets the subtree, allowing it to start the timer again next Update.
You must be logged in to reply to this topic.