News Forums RAIN General Discussion and Troubleshooting AI Troubleshooting

This topic contains 13 replies, has 2 voices, and was last updated by  Joshichimaru3 5 months, 1 week ago.

Viewing 14 posts - 1 through 14 (of 14 total)
  • Author
    Posts
  • #38925

    Joshichimaru3
    Participant

    I’m having a few issues with my current AI Behavior and was seeking any possible solutions to these issues. I’d like to note I have a a Time restraint of three full in days which I have to solve these issues by and in my desperation have come to seek some advice from other Developers.

    EDIT: http://s000.tinyupload.com/index.php?file_id=57724855371754559402
    this is the current behavior tree I’m using if you were unsure how each node worked.

    Below is an image of the behavior tree my problems will be relating to.
    My AI
    Below is an image of the Navmesh
    The Navmesh

    The first issue is when I walk into a room with a door. When I enter a room and my AI is chasing the player, the rain behavior will not show any state active (no nodes coloured in the debugging) untill it finally stops where he will continue his patrol route or pursue the player., and I will hear a sound similar to when a program crashes (the start of a sound looping). I assume this is caused by the ai jumping in between states and its due to the colliders or the ai being unable to see the player the moment he moves. I’ve tried enlarging the colliders on the doors to ensure there are no gaps although the issue still resides. I later discovered that he can occasionally do this around corners as well so I’m sure its due to the way U;ve build the behavior tree. Optimally id like the ai to do a set animation and just wait for the player until LOS is broken. Additionally I considered a short idle state to wait if the player appears before continuing to resume his patrol rounds although I’m struggling to understand the Behavior system rain uses and have made a few attempts but all resulted in my current behavior breaking.

    The second issue is when the AI gets to the edge of the Navmesh and the player is visible but to far away for him to reach, the ai will just freeze and sit there as one of the nodes returns fail and it becomes stuck in an infinite loop. I’m really struggling to wrap my head around the logic these behavior trees use, and I’m not sure how I can combat these issues.

    I’ve rebuild my behavior tree multiple times and the current version I shared above so far is the most optimal. I’ve read through a few posts over the past 10 weeks and looked a few tutorials but I struggle to understand how to hiearch and build these behavior trees in an optimal. I know the issue is the way I’ve build the Behavior Tree but I’ve tried a variety of builds and just can’t seem to get it right.

    Any solutions or suggestions would be a huge help!

    • This topic was modified 5 months, 2 weeks ago by  Joshichimaru3.
    #38930

    Sigil
    Keymaster

    OK, let’s see if we can’t setup something you can use for your behavior. From the looks of it you need a patrol, chase, and attack setup. Your first issue is that when the AI loses line of sight it doesn’t know what to do anymore, so lets fix that.

    I’d like you to try a tree out real quick and see if the results work for you, I’m just going to type it out here as it won’t be too large. I’m going to skip the audio and animation for now as well since that can be easily added back in with your parallel setup.

    root
       sequencer
          selector
             expression (expression: targetPlayer != null, returns: evaluate)
             parallel (fail: any, succeed: any, tie breaker: fail)
                detect (repeat: until success, form variable: targetPlayer)
                patrol route (waypoint route: "Your Route", move target variable: waypointTarget)
                   move (move target: waypointTarget)
          selector
             parallel (fail: any, succeed: any, tie breaker: fail)
                sequencer (repeat: until failure)
                   expression (expression: targetPosition = position(targetPlayer), returns: success)
                   detect (form variable: targetPlayer)
                move (move target: targetPlayer)
             parallel (fail: any, succeed: any, tie breaker: fail)
                detect (repeat: until success, form variable: targetPlayer)
                move (move target: targetPosition)

    So just to explain what the tree does real quick:

    1. First I check to see if I already have a target (and skip the patrol if I do)
    2. If I don’t have a target, I patrol until I detect a target (I’ll do this forever)
    3. Once I detect a target, I continually note the position of the target while heading towards it
    4. If at any point I can no longer detect the target, I head to the last position I noted, trying to find it again
    5. If I detect the target, or arrive at the position I last saw it at, I start the tree over

    Let’s get this tree going for you, and then we can build on it, adding in the attack. Let me know how it goes.

    #38931

    Sigil
    Keymaster

    Just to note, I tried to fill out everything completely, but there are a few things I don’t know, like the way your detect is setup, or the name of your patrol route, so you’ll have to add some of your information as needed.

    #38942

    Joshichimaru3
    Participant

    Thankyou for the response, yes I’ve implemented the Behavior you recommended and so far its functioning perfectly, the ai is able to detect my last position and then move to the position.

    I have also confirmed that the issue with the ai jumping forwards and back between states was caused by the door colliders to some degree, I think it was also an issue with the behavior tree but I was able to complete eliminate the issue with my fixed collider doors, this didn’t work on the previous behavior.

    So with this I can easily implement my ai states again, although the next issue I now face is the ai standing at the edge of the navmesh continously tying to move towards me although he can’t. What form of check could I implement when the ai can see the player but cannot move towards him? (I have an animation I would like to play if this happens, although while in this state the ai could immediately attack or chase the player again from this stay when he moves back within navmesh / attack range.
    I assume there’s a better system I could use for my attack, I’m currently using a custom RAIN event that sends damage and knock back to the player after an animation. Is there a dynamic way to change between states such as Running / Charging / Atacking based on distance from the player? I understand how visual detection works if you could give me a framework for the system that will dynamically work and the ai will react based on the distance from the player.

    I’ll just summarize what I just said briefly:
    A way to check when the ai can see the player but can’t move closer due to the navmesh restrictions and complete an action when these conditions are met.
    A way change between states depending on the AI’s distance from the player (if hes close he will sprint / charge) is he can see the play he will run and if hes in contact range with the player he will attack.
    That should be the extent of what I need my AI to do. I really appreciate your help, I feel like I’m unable to tap into the power of rain because I struggle to fully understand how each decision interacts, I always misunderstand something and end up confusing myself or breaking my behavior.

    #38947

    Sigil
    Keymaster

    “A way to check when the ai can see the player but can’t move closer due to the navmesh restrictions and complete an action when these conditions are met.”

    The only time a move node returns failure is if it can’t reach the player. The only time this will ever happen is if you have Allow Off Graph Movement turned off and your AI gets to the edge of a Navigation Mesh (exactly your situtation). So to handle this, put your move node into a selector and on failure you can do whatever you like:

    ...
       selector
          move
          animate (animation state: wave)
    ...

    “A way change between states depending on the AI’s distance from the player (if hes close he will sprint / charge) is he can see the play he will run and if hes in contact range with the player he will attack.”

    You could do this with multiple sensors if you like, each one with smaller ranges, detecting them specifically to determine how fast you want to run. I personally think this adds a lot of complexity to your AI and your tree though. I would instead write a very small Custom Action to figure out your speed based on distance, something like this:

    using RAIN.Action;
    using UnityEngine;
    [RAINAction]
    public class DetermineSpeed : RAINAction
    {
        public override ActionResult Execute(RAIN.Core.AI ai)
        {
            GameObject tTarget = ai.WorkingMemory.GetItem<GameObject>("targetPlayer");
            if (tTarget == null)
                return ActionResult.FAILURE;
            float tDistance = Vector3.Distance(tTarget.transform.position, ai.Body.transform.position);
            if (tDistance > 20)
                ai.WorkingMemory.SetItem<float>("moveSpeed", 1);
            else if (tDistance > 10)
                ai.WorkingMemory.SetItem<float>("moveSpeed", 2);
            else
                ai.WorkingMemory.SetItem<float>("moveSpeed", 3);
            return ActionResult.SUCCESS;
        }
    }

    Here’s a modified tree to use that custom action, to also do the above selector for a failing move, and I also set it up to choose a different animation based on the same moveSpeed:

    root
       sequencer
          selector
             expression (expression: targetPlayer != null, returns: evaluate)
             parallel (fail: any, succeed: any, tie breaker: fail)
                detect (repeat: until success, form variable: targetPlayer)
                patrol route (waypoint route: "Your Route", move target variable: waypointTarget)
                   move (move target: waypointTarget)
          selector
             parallel (fail: any, succeed: any, tie breaker: fail)
                sequencer (repeat: until failure)
                   action (class: DetermineSpeed) // This to determines my moveSpeed based on distance
                   expression (expression: targetPosition = position(targetPlayer), returns: success)
                   detect (form variable: targetPlayer)
                selector // This gives me an option if my move fails
                   parallel (fail: any, succeed: any, tie breaker: fail)
                      move (move target: targetPlayer, move speed: moveSpeed)
                      selector // This selector chooses an animation based on my moveSpeed
                         sequencer
                            expression (expression: moveSpeed == 1, returns: evaluate)
                            animate (animation state: walk)
                         sequencer
                            expression (expression: moveSpeed == 2, returns: evaluate)
                            animate (animation state: run)
                         animate (animation state: charge)
                   animate (animation state: CantReachTarget) // This happens if my move fails above
             parallel (fail: any, succeed: any, tie breaker: fail)
                detect (repeat: until success, form variable: targetPlayer)
                selector // This gives me an option if my move fails
                   parallel
                      move (move target: targetPosition, move speed: 1)
                      animate (animation state: walk)
                   animate (animation state: CantReachTarget) // This happens if my move fails above

    As for your frustration with understanding the behavior, the best recommendation I can give you is to always start simple with it. Don’t try to put all of your behavior into the tree right off the bat, because most likely it won’t work, and you’ll end up spending hours tweaking said tree until it does. In your AI’s case the process I took for building it was this:

    1. Setup patrol behavior
    2. Add detection of the player to the patrol behavior
    3. Add follow behavior after detecting the player
    4. Add behavior for losing the player when no longer detected
    5. Add behavior for going to last position when no longer detected
    6. Add animation for the movement
    7. Add custom action for move speed
    8. Add animation based on move speed
    9. Add alternate option for when the move fails

    After every step I made sure I had the behavior I wanted before moving on (sorta, I didn’t *actually* test this, but you get the idea). This doesn’t help later on when you have a really complex tree and something goes wrong, but at least it helps you get going. Once the tree is more complex I try to isolate behavior I need in a separate tree until I get it right, then work on getting it into my main tree after the fact.

    • This reply was modified 5 months, 2 weeks ago by  Sigil. Reason: fix block quotes
    • This reply was modified 5 months, 2 weeks ago by  Sigil. Reason: typos and comments
    #38950

    Joshichimaru3
    Participant

    Thankyou Sigil for all the assistance, I can also implement the attacking based of this distance system and stop him moving so with this it should solve all my problems. I’ll work on implementing it in the morning but as far as I can tell there shouldn’t be any issues. I’ll use your recommendation for approaching task in the future, even when I script I finish everything then test it sometimes makes finding the problem difficult.

    RAIN has allowed me to create an AI within my game that I otherwise would have been incapable of and I really appreciate all the work Rival Theory has put into developing it.

    #38953

    Joshichimaru3
    Participant

    sequencer (repeat: until failure)

    This Sequencer causes my animations to never check again, although if I have it off the animations just won’t play correctly. Not sure how to fix this.

    UPDATE - This seems to be due to the animations Wrap mode, I have to Loop the patrol walk animation but any animation assigned via the animation selection system made needs to be wrapped Default/Once or default it seems.

    • This reply was modified 5 months, 2 weeks ago by  Joshichimaru3.
    #38959

    Joshichimaru3
    Participant

    So I have all my systems working now, I realize I had to use default / once warped animations to exit out of their animate stats as having them loop will cause the AI to be permanently stuck within a state. Visually this isn’t to bad although the issue I have is Audio, so I can’t have looping audio play because It will result in the same issue, but having a paralegal waiting for the sound or the animation causes one to wait on the other unless i synced them which is an undesired outcome for me, I’m working on a way around this but if you know how it’s done please let me know.

    #38960

    Sigil
    Keymaster

    It is ok for the animation or audio to loop indefinitely, you just have to make sure that you have another node in the parallel (assuming there is a parallel in the hierarchy) that can help it exit. In the tree I wrote above, I relied on the parallel being set to “succeed: any” and “fail: any” plus at least one of its children being capable of returning success or failure. I’ll break it down here real quick:

    parallel (fail: any, succeed: any, tie breaker: fail)
        sequencer (repeat: until failure)
            ...
            // This will fail if we lose our player
            detect (form variable: targetPlayer)
        selector
            parallel (fail: any, succeed: any, tie breaker: fail)
                // The will fail if we can't get to the player, or succeed if we reach them
                move (move target: targetPlayer, move speed: moveSpeed) 
                ...
            animate (animation state: CantReachTarget)

    Now I left quite a few things out in the overall tree that would need an answer. For instance the animation above (CantReachTarget): if it were to loop it would need to be placed in a parallel with a timer, or have a constraint around it so that it will eventually return. Also, anywhere I relied on a move node returning success it implies that the close enough distance can be hit, if the close enough is smaller than the collision area around the AI and target it will never reach its target.

    Identify the sections that are running forever, make sure they are in a parallel (and usually it needs to be set to “succeed: any” and “fail: any”) and if they are, identify the child that needs to return in order to make it work.

    #38966

    Joshichimaru3
    Participant

    I understand how the parallels were working and tried creating children parallels but the issue I had is that if I wanted something to loop but be able to change stat it would eventually have to fail to allow the transition, the issue I have is that for sound this resets the sound constantly unless the timing for the sound was exactly the same as the animations. From what I gather it’s impossible to have a looping sound playing without creating a script that pulls from the rain AI variables and plays based of that. That’s how I think I’m going to have to do it.
    I’ll give this a shot but I I’m really short on time. I only have 12 hours more to work on this, so I’ll just give this a wild swing hopefully it will all be okay.
    I appreciate the assistance.

    #38968

    Sigil
    Keymaster

    If the sounds line up directly with specific times in the animation, like footsteps, it may be better to have a component that runs on top of the body, looking at the Animation component and what it is currently playing, and lining up audio with that. It has been awhile since I’ve worked with the legacy animation system but I believe this is possible.

    #38969

    Joshichimaru3
    Participant

    I know ho to setup sounds that play at set times, I’ve already got that working. (My attack animations hit sound) and I’ve done it with footsteps aswell but I have a second sound that is the huffing / grunting of my AI as it chases the player, which I just want to loop over and over. Its only for two states so I just want to check the moveSpeed of the monster and then play a sound if they are at a certain value. If I wrote a C# script to do this what systems would I need to be included (such as Using.UnityEngine)
    I noticed with the custom scripts my custom functions couldn’t call ai.WorkingMemory only the overrides could.

    #38970

    Sigil
    Keymaster

    In Custom Actions, the AI is passed into the function as an argument (as the variable ai), so if you want to use it in other functions you’ll have to send it to them as well, or assign it to a member variable in the action.

    If you aren’t in a custom action at all (like a component maybe) you can do something like this:

    using RAIN.Core;
    using UnityEngine;
    public class GetMoveSpeed : MonoBehaviour
    {
        private AIRig _rig = null;
        void Start()
        {
            _rig = gameObject.GetComponentInChildren<AIRig>();
        }
        void Update()
        {
            float tMoveSpeed = _rig.AI.WorkingMemory.GetItem<float>("moveSpeed");
            // Do stuff with the move speed
        }
    }

    This would need to be put on the AI’s body or on the AI object itself in order to work.

    #38971

    Joshichimaru3
    Participant

    Okay excellent, exactly what I needed thank you Sigil for wall your help.

Viewing 14 posts - 1 through 14 (of 14 total)

You must be logged in to reply to this topic.