News Forums RAIN Sample Projects, How To, and Code Example XML Files OR A Tutorial For Wander, Eat, Attack, and Reset.

This topic contains 55 replies, has 4 voices, and was last updated by  Sigil 4 months, 4 weeks ago.

Viewing 15 posts - 1 through 15 (of 56 total)
  • Author
    Posts
  • #38972

    Mad_Mark
    Participant

    I’m down to my last few strands of hair. I have looked at the Starter Kit, and it doesn’t seem to help since the AI included are VERY skinny examples. Are there any examples of actual working functional AI that can be downloaded and examined, modified, or just straight out poached?

    I am simply trying to get what I consider the basics working. I have tried everything I can think of, read the entire WIKI, combed through the Forums, and just don’t get it!!
    My AI should:
    - Select to a random location.
    - Make sure it is in the bounds of the NavMesh.
    - Move to that location using Mechanim walk animation.
    - Once at the location, load the Mechanim idle animation.
    - Pause for a few seconds.
    - Pick another target location.
    - If the player is within line of sight, head in that direction.
    - If the player is within 1/2 of the sight distance, load the Mechanim run animation and move at double speed towards him.
    - If within close proximity, play Mechanim attack animation.
    - If player escapes line of sight, pick 6 random locations around the last known position of player and search.
    - Nothing found, resume random wandering.
    - If a food item is in sight, head towards it.
    - If 1/2 distance, run.
    - If within close proximity, play Mechanim eating animation for 10 seconds.
    - If player comes by while eating, ignore player unless attacked.
    - Resume random wandering.

    I really want to understand this tool, but chaining these actions together without something going red in the BT just seems impossible. The examples in the starter kit show how to do EACH of these things, but not how to get them operating together.
    Any help, please?

    Mark

    #38973

    Mad_Mark
    Participant

    Does Squad Commander offer the same functionality as Rain? Is it simpler? Should I be using that instead?

    I bought it a couple of months ago for an FPS I have planned, but haven’t “broken the seal” yet, as I am still trying to finish up this TPS zombie shooter…

    Mark

    #38979

    Mad_Mark
    Participant

    -=[BUMP]=-

    #38983

    Sigil
    Keymaster

    Squad Command will show you a few of these things… it isn’t simpler than RAIN though, it is really just a full example of what you can do with RAIN. It will show you more complex behavior trees, using multiple behavior trees, communicating between AI, and several other things.

    I will put together an example behavior tree for what you are thinking about, I think I can nail most of the things you’ve described. You are correct that we don’t have examples that simply give you the AI you need, but I think that has more to do with the fact that everyone needs something different. Most people have asked for patrol, attack, engage, disengage, and similar behavior so we tried to cover those points.

    This doesn’t change that we can probably make it easier, I may not be able to get back to this until later this next week, but I’ll come back and post an example tree for at least some of the things you want (it will be pseudo code though, not actual XML, it is usually really easy to convert to a tree though).

    #38990

    Mad_Mark
    Participant

    I would greatly appreciate any help you can offer, Sigil. Pseudo-code that outlines the node types, settings, and parameters would be a better start than I have at the moment. I don’t mind doing the work, but lack the understanding. I’ve tried to build what I can from outdated YouTube materials, but when options aren’t present, or the UI varies, it can be quite confusing. For instance, where is the “Line of Sight” option? Is it now inherent in the Visual Sensor by default? Many of the videos I find also show a ton of parameters or options that are not exposed in my version (2.1.11.0).

    I am hoping to create this TPS Zombie game for my grandkids by Christmas. I have created 3D models of them, modeled their house and neighborhood, and have dropped them all into a rather dark place. (By request. They love The Walking Dead…) Currently AI is controlled by script, and it is getting more and more complex. I was hoping RAIN would clean that up a bit, and allow me to move further along with the AI to simulate intelligent actions.

    Thanks,
    Mark

    #38995

    Sigil
    Keymaster

    For the options you are missing, we added a Show Advanced Settings in the component menu, it is probably where a lot of your missing options are (including line of sight). Once you turn it on all of the RAIN components will show the additional options, as it’s a global setting.

    I’ll get back here with some examples as well (I haven’t forgot), just a bit busy at the moment.

    A quick question for you, are you using root motion with your animations?

    #39001

    Mad_Mark
    Participant

    Yes, and no. I use root motion, but can NOT if required. (I do most of my own modelling and animating…)

    Mark

    #39014

    Mad_Mark
    Participant

    You forgot me, didn’t ya. Sure could use some help over here….

    Cheers,
    Mark

    #39015

    Sigil
    Keymaster

    Did not forget you Mark, just a bit overwhelmed at the moment. I have some time off tomorrow so I’m going to be going through a lot of the forums posts that I have been falling behind on.

    I’ll see if I can’t put something together tonight or tomorrow for you.

    #39026

    Sigil
    Keymaster

    Alright, I’m here right now and I’m putting together a post. I may do several posts if it gets late and I can’t finish as the list of actions defined initially are long.

    Let’s see what I can come up with.

    #39027

    Sigil
    Keymaster

    Here’s the original options:
    – Select to a random location.
    – Make sure it is in the bounds of the NavMesh.
    – Move to that location using Mechanim walk animation.
    – Once at the location, load the Mechanim idle animation.
    – Pause for a few seconds.
    – Pick another target location.
    – If the player is within line of sight, head in that direction.
    – If the player is within 1/2 of the sight distance, load the Mechanim run animation and move at double speed towards him.
    – If within close proximity, play Mechanim attack animation.
    – If player escapes line of sight, pick 6 random locations around the last known position of player and search.
    – Nothing found, resume random wandering.
    – If a food item is in sight, head towards it.
    – If 1/2 distance, run.
    – If within close proximity, play Mechanim eating animation for 10 seconds.
    – If player comes by while eating, ignore player unless attacked.
    – Resume random wandering.

    So the way that I normally handle locomotion animation with RAIN is that I create an Animator Controller that handles it and either use triggers or the Mecanim Motor to forward parameters that set it up properly. Here is the pseudo code for the state machine I would set up for your AI:

    Idle (Base Layer.Idle)
        transition to Walk if Speed (float) > 0 
        transition to Attack if Attack (trigger) is true 
        transition to Eat if Eat (trigger) is true 
    Walk (Base Layer.Walk)
        transition to Idle if Speed (float) < 0.1
    Attack (Base Layer.Attack)
        transition to Idle when done
    Eat (Base Layer.Eat)
        transition to Idle when done

    So the parenthetical name on those states is the full state name that Unity (and RAIN) recognizes and we’ll use it later. So that describes the state machine. The initial use of this state machine is to set our AI up with a Mecanim Motor, and forward the Speed parameter and leave the name as Speed (given the example so far). So at this point, if we were to define a simple behavior tree that simply moved the AI across the world, the Speed parameter would be forwarded and we should see the Walk state play. You could adjust the speeds appropriately to reduce sliding, or use root motion to get rid of it altogether.

    Next up: the initial behavior tree I would setup.

    • This reply was modified 9 months, 1 week ago by  Sigil. Reason: Fixed random things
    • This reply was modified 9 months, 1 week ago by  Sigil.
    #39029

    Sigil
    Keymaster

    So what I usually try to identify in any behavior is what my AI is going to do be doing most of the time if I don’t mess with it (as a player or otherwise). So in your case, the general behavior is to pick a random location, move to that location, pause for a few seconds, and repeat (at least at the simplest level).

    Here is the pseudo code for that behavior tree:

    root
        sequencer
            custom action (class: ChooseRandomLocation, target: moveTarget, distance: 20)
            move (move target: moveTarget)
            timer (seconds: random(1, 5))

    The custom action (ChooseRandomLocation) picks a random location, on the navigation mesh, within the given distance, and assigns the position to the target variable. We don’t handle the failure case here, which would be if the AI wasn’t located on a Navigation Mesh.

    The custom action would look like this:

    using RAIN.Action;
    using RAIN.Navigation;
    using RAIN.Navigation.Graph;
    using RAIN.Navigation.NavMesh;
    using RAIN.Representation;
    using System.Collections.Generic;
    using UnityEngine;
    [RAINAction]
    public class ChooseRandomLocation : RAINAction
    {
        public Expression Target = new Expression();
        public Expression Distance = new Expression();
        public override ActionResult Execute(RAIN.Core.AI ai)
        {
            // Get any graphs at our AI's position
            List<RAINNavigationGraph> tGraphs = NavigationManager.Instance.GraphForPoint(ai.Kinematic.Position);
            if (tGraphs.Count == 0)
                return ActionResult.FAILURE;
            // Pretty much guaranteed it will be a navigation mesh
            NavMeshPathGraph tGraph = (NavMeshPathGraph)tGraphs[0];
            // Look for polys within the given distance
            float tDistance = 20;
            if (Distance.IsValid)
                tDistance = Distance.Evaluate<float>(ai.DeltaTime, ai.WorkingMemory);
            // We'll use the recently added oct tree for this
            List<NavMeshPoly> tPolys = new List<NavMeshPoly>();
            tGraph.PolyTree.GetCollisions(new Bounds(ai.Kinematic.Position, Vector3.one * tDistance * 2), tPolys);
            if (tPolys.Count == 0)
                return ActionResult.FAILURE;
            // Pick a random node
            NavMeshPoly tRandomPoly = tPolys[UnityEngine.Random.Range(0, tPolys.Count - 1)];
            // If the user set a Target variable, use it
            if (Target.IsVariable)
                ai.WorkingMemory.SetItem<Vector3>(Target.VariableName, tRandomPoly.Position);
            // Otherwise just use some default
            else
                ai.WorkingMemory.SetItem<Vector3>("randomLocation", tRandomPoly.Position);
            return ActionResult.SUCCESS;
        }
    }

    You should try all of this up to this point and make sure you can get it going. Given the previous post you should have an AI that chooses a random location on the Navigation Mesh and moves towards it. If the state machine is set up as well, it will also play the necessary animations.

    Next up: adding that whole eating the brains thing.

    #39031

    Sigil
    Keymaster

    So as I said previously, I usually try to identify what my AI is going to do be doing most of the time. Aside from moving around randomly, locating food sources and eating is probably the next up.

    Here is the pseudo code for the behavior tree (including the previous defined behavior):

    root
        sequencer
            parallel (fail: any, success: any, tie breaker: fail)
                detect (repeat: Until Success, aspect: "Food", form variable: foodTarget)
                sequencer (repeat: Forever)
                    custom action (class: ChooseRandomLocation, target: moveTarget, distance: 20)
                    move (move target: moveTarget)
                    timer (seconds: random(1, 5))
            move (move target: foodTarget, close enough distance: 1)
            mecanim parameter (parameter: Eat, parameter type: Trigger), value: true)
            timer (seconds: random(5, 10))
            custom action (class: ConsumeFood, target: foodTarget)

    Alright, so in this case, we constantly look for food in all of our sensors while we wander around aimlessly. If we ever detect food we drop out of the wandering and move directly to the food target. Once we are close enough (1 meter in this case), we trigger our eating animation, and then wait for 5 to 10 seconds. After this we call a custom action to disable our aspect so we no longer see it. In the end game this custom action could also cause the fading out of a corpse, or play some fade out animation, or something.

    The ConsumeFood custom action would look like this:

    using RAIN.Action;
    using RAIN.Entities;
    using RAIN.Representation;
    using UnityEngine;
    [RAINAction]
    public class ConsumeFood : RAINAction
    {
        public Expression Target = new Expression();
        public override ActionResult Execute(RAIN.Core.AI ai)
        {
            if (!Target.IsValid)
                return ActionResult.FAILURE;
            // This has played assuming we've consumed the target as food, so find any aspect and disable it
            GameObject tTarget = Target.Evaluate<GameObject>(ai.DeltaTime, ai.WorkingMemory);
            // Grab the entity rig off of our target (again assuming it was detected properly) and mark it inactive
            EntityRig tEntityRig = tTarget.GetComponentInChildren<EntityRig>();
            tEntityRig.Entity.IsActive = false;
            return ActionResult.SUCCESS;
        }
    }

    At this point, if we defined all of the previously posted (assuming no bugs), we should have an AI that wanders around aimlessly, looking for anything marked as “Food”. If it finds any food it should eat it and then continue on wandering.

    • This reply was modified 9 months ago by  Sigil. Reason: bug in the entity code
    #39037

    Sigil
    Keymaster

    Back again.

    The next part is to make the AI aware of a player and to chase after them, and in the event that they lose them, move to their previous location (we’ll worry about the searching and speed after this).

    So something to notice about the behavior tree so far: every time I determine that there is something more important, the current tree becomes a child of the more important part. So initially, all we had was a sequencer for choosing a random location, but then when I decided finding food was more important, the sequencer became a child in a parallel that was detecting food. Similarly, detecting the player is more important than moving randomly and eating. So again the tree becomes a child of a larger tree:

    root
        sequencer
            parallel (fail: any, success: any, tie breaker: fail)
                detect (repeat: Until Success, aspect: "Player", form variable: playerTarget)
                sequencer (repeat: Forever)
                    parallel (fail: any, success: any, tie breaker: fail)
                        detect (repeat: Until Success, aspect: "Food", form variable: foodTarget)
                        sequencer (repeat: Forever)
                            custom action (class: ChooseRandomLocation, target: moveTarget, distance: 20)
                            move (move target: moveTarget)
                            timer (seconds: random(1, 5))
                    move (move target: foodTarget, close enough distance: 1)
                    mecanim parameter (parameter: Eat, parameter type: Trigger), value: true)
                    timer (seconds: random(5, 10))
                    custom action (class: ConsumeFood, target: foodTarget)
            selector
                parallel (fail: any, success: any, tie breaker: fail)
                    sequencer (repeat: Until Failure)
                        expression (expression: playerTargetPosition = position(playerTarget), returns: Success)
                        detect (aspect: "Player", form variable: playerTarget)
                    move (move target: playerTarget)
                parallel (fail: any, success: any, tie breaker: fail)
                    detect (repeat: Until Success, aspect: "Player", form variable: playerTarget)
                    move (move target: playerTargetPosition)

    So we added quite a bit at this point. I buried the previous tree into the first parallel that is also trying to detect the player. I put the parallel into a sequencer, so that in the event we detected the player I could continue working in the tree. After the parallel is a selector, which will only execute if we’ve detected a player. Now this selector starts by recording the players position, attempting to detect the player, and then continuing to move towards the player. At any point if we don’t detect the player we’ll drop out of that parallel and start on the next one. The next parallel continues to try and detect the player while moving towards the last position. If we detect the player it’ll return success starting the tree over, which in turn will detect the player and start chasing again. If we make it to the end without detecting the player, it’ll also return success, starting the tree over, and most likely just wandering and eating again.

    I realize this is quite the wall of text, and may be difficult to follow. Try to implement the tree I’ve described and the custom actions as well. If you have issues let me know and I’ll address them in the post.

    I’ll be back on labor day to add more to this and answer any questions/issues you may run into.

    #39038

    Sigil
    Keymaster

    I’ve stickied this post, as it is becoming a tutorial. I’ll see if I can rename the topic to be more appropriate.

    Anyone who wants behavior that involves wandering, detecting, attacking, searching, and resuming behavior should take a look at this and try it out.

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

You must be logged in to reply to this topic.