News Forums Search Search Results for 'timer'

Viewing 15 results - 16 through 30 (of 163 total)
  • Author
    Search Results

  • Sigil
    Keymaster

    Now adding in an attack animation and game logic:

    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)
                    sequencer
                        move (move target: playerTarget)
                        move (face target: playerTarget)
                        mecanim parameter (parameter: Attack, parameter type: Trigger), value: true)
                        custom action (class: AttackTarget, target: playerTarget)
                parallel (fail: any, success: any, tie breaker: fail)
                    detect (repeat: Until Success, aspect: "Player", form variable: playerTarget)
                    move (move target: playerTargetPosition)

    So in the second to last parallel where it used to be:

    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)

    I’ve changed that now to be this:

    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)
        sequencer
            move (move target: playerTarget)
            move (face target: playerTarget)
            mecanim parameter (parameter: Attack, parameter type: Trigger), value: true)
            custom action (class: AttackTarget, target: playerTarget)

    So what should happen now, is that while we are making sure we can still detect the player (and storing their previous position), we do the following (this is all in the sequencer):

    • Get within our close enough distance to the player.
    • Make sure we are facing the player.
    • Set a trigger to play our attack animation.
    • Run the AttackTarget custom action to do the actual game logic.

    The separate move node with the face target is for making sure we’re still facing the player after the first attack. The AttackTarget custom action is also going to do a bit of extra work to make sure we’re actually playing the attack animation, and it won’t return success until it finishes.

    Here is the AttackTarget custom action:

    using RAIN.Action;
    using RAIN.Representation;
    using UnityEngine;
    [RAINAction]
    public class AttackTarget : RAINAction
    {
        public Expression Target = new Expression();
        public Expression AttackTime = new Expression();
        private Animator _animator = null;
        private float _attackTime = 0.5f;
        private bool _doneDamage = false;
        public override void Start(RAIN.Core.AI ai)
        {
            base.Start(ai);
            _animator = ai.Body.GetComponent<Animator>();
            if (AttackTime.IsValid)
                _attackTime = Mathf.Clamp01(AttackTime.Evaluate<float>(ai.DeltaTime, ai.WorkingMemory));
            _doneDamage = false;
        }
        public override ActionResult Execute(RAIN.Core.AI ai)
        {
            if (!Target.IsValid)
                return ActionResult.FAILURE;
            // We'll just grab this here for reference later
            AnimatorStateInfo tCurrentState = _animator.GetCurrentAnimatorStateInfo(0);
            // If we haven't done damage yet, we're just starting out so
            if (!_doneDamage)
            {
                // There are many ways to do the timing for the attack, in this case
                // I'll go the easy route and just wait until the attack time has been hit
                if (tCurrentState.IsName("Base Layer.Attack") &&
                    tCurrentState.normalizedTime >= _attackTime)
                {
                    // Here you should call out to your player and make them take damage in some way
                    GameObject tTarget = Target.Evaluate<GameObject>(ai.DeltaTime, ai.WorkingMemory);
                    _doneDamage = true;
                }
                // We'll always return running until we do damage
                return ActionResult.RUNNING;
            }
            // In this case, we just need to wait for our animation to finish
            // you may need to check for transitioning here too
            if (tCurrentState.IsName("Base Layer.Attack"))
                return ActionResult.RUNNING;
            // And we finished attacking
            return ActionResult.SUCCESS;
        }
    }

    So the AttackTarget custom action will wait until the attack animation has played a certain amount (AttackTime). The AttackTime represents normalized time so it will always be from 0 to 1, 0 being the beginning of the state and 1 being the end. And after the damage has been done the custom action will continue to wait until the animation finishes playing.

    So at this point I still haven’t put together a project to test all of these. The concepts are all sound though, I just may have some bugs. I’ll work on that next.

    • This reply was modified 1 month, 4 weeks ago by  Sigil. Reason: fixed whitespace

    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.


    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 1 month, 3 weeks ago by  Sigil. Reason: bug in the entity code

    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.

    #39025

    In reply to: Animal Wandering


    Sigil
    Keymaster

    Hmmm… well this is all assuming you are using RAIN and the RAIN behavior trees and navigation mesh. So this isn’t particular to coding or Unity, but particular to RAIN. The pseudo code for your tree would be something like this (you would need to put this tree together on your own):

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

    Just to explain that a bit, the sequencer node will allow each of its children to run as long as they continue to return running or success. So the ChooseRandomNavMeshTarget custom action (I’ll define it below) will always return success if it can find a Navigation Mesh. The move node will return success once the AI reaches its target. The timer node will return success after 1 to 5 seconds. After all of this it will repeat, choosing a new nav mesh target, moving towards it, and then waiting 1 to 5 seconds.

    The ChooseRandomNavMeshTarget would look something 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 ChooseRandomNavMeshTarget : RAINAction
    {
        public Expression Target = 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];
            // Pick a random node
            int tNodeIndex = UnityEngine.Random.Range(0, tGraph.Size - 1);
            NavigationGraphNode tNode = tGraph.GetNode(tNodeIndex);
            // We'll look for a poly in particular (it could be an NavMeshEdge and not a poly)
            while (!(tNode is NavMeshPoly))
            {
                tNodeIndex = (tNodeIndex + 1) % tGraph.Size;
                tNode = tGraph.GetNode(tNodeIndex);
            }
            // You can randomize the position within the poly as well
            NavMeshPoly tPoly = (NavMeshPoly)tNode;
            // Grab a random edge
            NavMeshEdge tEdge = tPoly.GetEdgeNode(UnityEngine.Random.Range(0, tPoly.EdgeCount - 1));
            // And adjust our position to be random between the center and that edge
            Vector3 tRandomPosition = Vector3.Lerp(tNode.Position, tEdge.Position, UnityEngine.Random.value);
            // If the user set a Target variable, use it
            if (Target.IsVariable)
                ai.WorkingMemory.SetItem<Vector3>(Target.VariableName, tRandomPosition);
            // Otherwise juse use some default
            else
                ai.WorkingMemory.SetItem<Vector3>("randomNavMeshTarget", tRandomPosition);
            return ActionResult.SUCCESS;
        }
    }

    See if you can’t get that going in your scene and then we can build upon it.

    #38964

    Sigil
    Keymaster

    New and improved. I added a DefaultSpeed to the motor (removed the acceleration), and I now override the speed value on Nav Mesh Agent, so if you want to affect speed you will have to use the one on the UnityNavMeshMotor instead. In addition, I changed my behavior tree a bit to give a more obvious result (not that it really matters).

    using RAIN.Motion;
    using RAIN.Serialization;
    using UnityEngine;
    [RAINSerializableClass]
    public class UnityNavMeshMotor : RAINMotor
    {
        [RAINSerializableField]
        private float _closeEnoughDistance = 0.1f;
        [RAINSerializableField]
        private float _speed = 3.5f;
        private NavMeshAgent _agent = null;
        private Vector3 _lastPosition = Vector3.zero;
        public override float DefaultCloseEnoughDistance
        {
            get { return _closeEnoughDistance; }
            set { _closeEnoughDistance = value; }
        }
        public override float DefaultSpeed
        {
            get { return _speed; }
            set { _speed = value; }
        }
        // It seems like the NavMeshAgent forces positions on
        // the NavMesh, so 3D Movement doesn't make sense
        public override bool Allow3DMovement
        {
            get { return false; }
            set { }
        }
        // Don't support this at the moment, not sure if the NavMeshAgent
        // can go off of the mesh
        public override bool AllowOffGraphMovement
        {
            get { return false; }
            set { }
        }
        // 3D Rotation is technically doable, but not like
        // we support it with the BasicMotor
        public override bool Allow3DRotation
        {
            get { return false; }
            set { }
        }
        public override void BodyInit()
        {
            base.BodyInit();
            if (AI.Body == null)
                _agent = null;
            else
            {
                _agent = AI.Body.GetComponent<NavMeshAgent>();
                if (_agent == null)
                    _agent = AI.Body.AddComponent<NavMeshAgent>();
            }
        }
        public override void UpdateMotionTransforms()
        {
            // I don't believe the Unity Navigation Mesh can handle transforms, so this stays as identity
            AI.Kinematic.ParentTransform = Matrix4x4.identity;
            AI.Kinematic.Position = AI.Body.transform.position;
            AI.Kinematic.Orientation = AI.Body.transform.rotation.eulerAngles;
            // Velocities likely won't matter as we never actually use them in this motor
            AI.Kinematic.ResetVelocities();
            // Set our speed to zero, we'll set it when we are using it
            _agent.speed = 0;
        }
        public override void ApplyMotionTransforms()
        {
        }
        public override bool Move()
        {
            if (!MoveTarget.IsValid)
                return false;
            // Set our acceleration
            _agent.speed = _speed;
            // We'll just update these constantly as our value can change when the MoveTarget changes
            _agent.stoppingDistance = Mathf.Max(DefaultCloseEnoughDistance, MoveTarget.CloseEnoughDistance);
            // Have to make sure the target is still in the same place
            Vector3 tEndMoved = _lastPosition - MoveTarget.Position;
            tEndMoved.y = 0;
            // If we don't have a path or our target moved
            if (!_agent.hasPath || !Mathf.Approximately(tEndMoved.sqrMagnitude, 0))
            {
                _agent.destination = MoveTarget.Position;
                _lastPosition = MoveTarget.Position;
                // We can return at least if we are at our destination at this point
                return IsAt(_agent.destination);
            }
            // Still making a path or our path is invalid
            if (_agent.pathPending || _agent.pathStatus == NavMeshPathStatus.PathInvalid)
                return false;
            return _agent.remainingDistance <= _agent.stoppingDistance;
        }
        public override bool IsAt(Vector3 aPosition)
        {
            Vector3 tPosition = AI.Body.transform.position - aPosition;
            tPosition.y = 0;
            return tPosition.magnitude <= _agent.stoppingDistance;
        }
        public override bool IsAt(MoveLookTarget aTarget)
        {
            return IsAt(aTarget.Position);
        }
        public override bool Face()
        {
            // Too tired to do this
            return true;
        }
        public override bool IsFacing(Vector3 aPosition)
        {
            // Too tired to do this
            return true;
        }
        public override bool IsFacing(MoveLookTarget aTarget)
        {
            // Too tired to do this
            return true;
        }
        public override void Stop()
        {
            _agent.Stop();
        }
    }

    And the behavior tree:

    root
       sequencer
          parallel (fail: any, succeed: any, tie breaker: fail)
             waypoint patrol (waypoint route: "route", move target variable: moveTarget)
                 move (move target: moveTarget)
             timer (seconds: 2)
          timer (seconds: 2)

    Let me know how it works.

    #38960

    In reply to: AI Troubleshooting


    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.

    #38946

    Sigil
    Keymaster

    Hmmm I put this together and it worked with the code above, it could be something particular with your behavior tree though, or perhaps you are using the version I posted briefly before editing it. This should behave similarly to what happens when a constraint interrupts a move:

    root
       waypoint patrol (waypoint route: "route", move target variable: moveTarget)
          parallel (fail: any, succeed: any, tie breaker: fail)
              move (move target: moveTarget)
              timer (seconds: 4)
          timer (seconds: 2)

    This is making him walk for 4 seconds, then pause for 2, and then repeats. If that doesn’t work for you, double check the code. If it does work, but your tree still doesn’t, post the problem area and I’ll test it out.


    rizzlar22
    Participant

    hi guys am new to scripting the script works just the time not working was wondering if you guys cud help thx

    #pragma strict

    var fireball : Transform;
    var spawnPoint : Transform;
    var openFire = true;
    var FireSound : AudioClip;
    var hit : RaycastHit;

    function Start () {

    }

    function Update () {

    if (Physics.Raycast (transform.position, transform.forward, hit, 10) && hit.collider.gameObject.CompareTag(“beast”)){

    {
    Fire();
    }

    }
    }
    function Fire()
    {
    var firebullet = Instantiate (fireball, spawnPoint.position, Quaternion.identity);
    firebullet.GetComponent.<Rigidbody>().AddForce(transform.forward * 1000);
    GetComponent.<AudioSource>().Stop();
    GetComponent.<AudioSource>().PlayOneShot(FireSound);
    openFire = false;
    Timer();
    }

    function Timer ()
    {
    if (openFire == false)
    {
    yield WaitForSeconds (20);
    openFire = true;
    }

    }

    #38850

    Sigil
    Keymaster

    The AI doesn’t happen to be moved manually at some point, like a transform.position = ? call at some point? I don’t have any issues interrupting the move call, I threw a timer in my behavior tree to reset the move every now and then and it keeps going. The only time I can replicate the issue is if the AI is part way through the path and I move it away from its target.

    Is there anything else going on, other than just restarting the behavior tree?

    #38838

    Sigil
    Keymaster

    Hmmmm, normally it should recalculate the path if it detects that the target has moved significantly. Perhaps there is a bug here somewhere. Can you take a screenshot of what the path looks like initially, before your timer resets the BT? Assuming it starts with a good path, and then you drop out of the BT, and then it comes up with a really wrong path it may explain what is happening a bit better.

    #38821

    KingKrawl
    Participant

    Ok I dont know if this goes in here but I cant seem to get my NPC to go to a Waypoint route!!! what am i doing wrong?

    any help is greatly appreciated

    <behaviortree version=”1.1″ repeatuntil=”” name=”Guard” debugbreak=”False”><sequencer usepriorities=”False” repeatuntil=”running” name=”Patrol and Idle” debugbreak=”False”><parallel tiebreaker=”fail” succeed=”all” repeatuntil=”” priority=”” name=”Patrol” fail=”any” debugbreak=”False”><waypointpatrol waypointsetvariable=”"PatrolRoute"” waypointactiontype=”patrol” traversetype=”loop” traverseorder=”forward” repeatuntil=”” pathtargetvariable=”” name=”Route” movetargetvariable=”” debugbreak=”False”><parallel tiebreaker=”fail” succeed=”all” repeatuntil=”” name=”Move” fail=”any” debugbreak=”False”><animate repeatuntil=”” name=”animate” debugbreak=”False” animationstate=”Walk” /><move turnspeed=”” repeatuntil=”” name=”Walk Patrol” movetarget=”” movespeed=”” facetarget=”” debugbreak=”False” closeenoughdistance=”” closeenoughangle=”” /></parallel></waypointpatrol><timer waitforsec=”30″ returnvalue=”success” name=”timer” debugbreak=”False” /></parallel><parallel tiebreaker=”fail” succeed=”all” repeatuntil=”” priority=”” name=”Pause” fail=”any” debugbreak=”False”><timer waitforsec=”15″ returnvalue=”success” name=”timer” debugbreak=”False” /><animate repeatuntil=”” name=”animate” debugbreak=”False” animationstate=”idle” /></parallel></sequencer></behaviortree>

    #38802

    RinAldrin
    Participant

    It can work via the sensors and behavior tree. You can have a script in your predators or prey that will tick a timer down then once that timer is low enough they will start hunting anything with a “prey” aspect in the entity object. Same thing with Prey where they look for objects that have the food aspect on them and then they will stand by it and start eating which where they are in that area around it their hunger goes up instead of down via a trigger zone and once they are full they will move on. With the predators you would need a health manager on the prey that when they are attacked their “health” drops and once that reaches 0 it deletes the AI component to save processing time and creates a trigger zone for the body for the animal to eat which again increases the hunger timer for the predator. I will also note that for the predators killing the prey the prey should have their entity swap from one that represents living prey and another that represents food so that then you can have the predator face it and play their animation. It might sound a bit complicated but thankfully Unity makes it rather easy with prefabs and whatnot and with RAIN you won’t have to do nearly as much scripting as you would otherwise. Trust me before RAIN I could never get my AI’s working but RAIN makes it far easier to do.

    #38769

    rbranch1
    Participant

    my tree corresponds to your logic as follows:
    root
    -sequencer
    -mecparam: (to give running animation)
    -move: (movetarget: followObject, facetarget: followObject — repeat:never)
    -parallel (succeed: any, fail: any, tie breaker: succeed)
    —mecparam: (for idle animation)
    —move (face target:followObject, repeat forever)
    —timer (seconds: 5, returns: success)

    It seems I that am still not getting the transition into the parallel, hence the mecparam not changing and the character not standing still

    • This reply was modified 3 months, 1 week ago by  rbranch1.
    #38761

    Sigil
    Keymaster

    Here is a possibility, this assumes you already have a target from a detect or otherwise:

    root
       sequencer
          move (move target: mytarget)
          parallel (succeed: any, fail: any, tie breaker: succeed)
             move (face target: mytarget, repeat forever)
             timer (seconds: 5, returns: success)

    Once the move gets to the target it will succeed, moving on to the parallel, which will look at the player until the timer succeeds after five seconds.

Viewing 15 results - 16 through 30 (of 163 total)