Search Unity

AI NPC switch randomly between Idle and Patrolling state when Player is not in range

Discussion in 'Scripting' started by martinamenegon, Apr 23, 2017.

  1. martinamenegon

    martinamenegon

    Joined:
    Aug 28, 2014
    Posts:
    31
    Hi all,
    so I have this script for a simple AI thanks to this super tutorial of Holistic3D


    so far the character is in Idle state (random picked from different idles), if you come near and are on sight it walks towards you and when it gets nearer it stops and attacks you.


    thing is, I want that when the NPC is in Idle state (meaning i am not in range), sometimes it goes into a Patrolling state (just walking for some seconds in a random direction).. I am not super in scripting and after hours of search i could not manage much.. any help would be much appreciated! thanks in advance!
    I already set the Animator for the "isPatrolling" state... but I have no idea how to switch between "isIdle" and "isPatrolling"..

    this is the basic script provided by Holistic3D

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class chase : MonoBehaviour {
    5.  
    6.     public Transform player;
    7.     static Animator anim;
    8.  
    9.     // Use this for initialization
    10.     void Start ()
    11.     {
    12.         anim = GetComponent<Animator>();
    13.     }
    14.    
    15.     // Update is called once per frame
    16.     void Update ()
    17.     {
    18.         Vector3 direction = player.position - this.transform.position;
    19.         float angle = Vector3.Angle(direction,this.transform.forward);
    20.         if(Vector3.Distance(player.position, this.transform.position) < 10 && angle < 30)
    21.         {
    22.            
    23.             direction.y = 0;
    24.  
    25.             this.transform.rotation = Quaternion.Slerp(this.transform.rotation,
    26.                                         Quaternion.LookRotation(direction), 0.1f);
    27.  
    28.             anim.SetBool("isIdle",false);
    29.             if(direction.magnitude > 5)
    30.             {
    31.                 this.transform.Translate(0,0,0.05f);
    32.                 anim.SetBool("isWalking",true);
    33.                 anim.SetBool("isAttacking",false);
    34.             }
    35.             else
    36.             {
    37.                 anim.SetBool("isAttacking",true);
    38.                 anim.SetBool("isWalking",false);
    39.             }
    40.  
    41.         }
    42.         else
    43.         {
    44.             anim.SetBool("isIdle", true);
    45.             anim.SetBool("isWalking", false);
    46.             anim.SetBool("isAttacking", false);
    47.         }
    48.  
    49.     }
    50. }
    51.  
     
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    You're going to have to consider some sort of timer. So right now it's default to idle. Let's say you want it to idle for 3 secs. So you would idle and then while idling, you can add Time.DeltaTime to another value and then check to see if that value is greater than or equal to your idle time. Once it is, you'll change it to patrol. After x time of patrolling, you change back to idle. It shouldn't be to difficult to set up over all.
     
  3. martinamenegon

    martinamenegon

    Joined:
    Aug 28, 2014
    Posts:
    31
    yes i feel super stupid because theoretically I know what I need but.. I rly suck at scripting apparently because I can not get how to set it up..
    I managed so far to have them from Idle to Patrolling.. but they do not come back into Idle..

    here is my code so far..

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Chase : MonoBehaviour {
    5.     private Transform player;
    6.     private Animator anim;
    7.  
    8.     void Start ()
    9.     {
    10.         anim = GetComponent<Animator>();
    11.         player = GameObject.FindWithTag("Player").transform;
    12.     }
    13.  
    14.     void Update ()
    15.     {
    16.         Vector3 direction = player.position - this.transform.position;
    17.         float angle = Vector3.Angle (direction, this.transform.forward);
    18.  
    19.         //if it sees me and i am in range
    20.         if (Vector3.Distance (player.position, this.transform.position) < 10 && angle < 30) {
    21.  
    22.             direction.y = 0;
    23.  
    24.             this.transform.rotation = Quaternion.Slerp (this.transform.rotation,
    25.                 Quaternion.LookRotation (direction), 0.1f);
    26.  
    27.             anim.SetBool ("isIdle", false);
    28.  
    29.             //if im in range but not it walks
    30.             if (direction.magnitude > 5) {
    31.                 this.transform.Translate (0, 0, 0.05f);
    32.                 anim.SetBool ("isWalking", true);
    33.                 anim.SetBool ("isAttacking", false);
    34.                 anim.SetBool ("isPatrolling", false);
    35.             }
    36.  
    37.             //if im in range and near it meows
    38.             else {
    39.                 anim.SetBool ("isAttacking", true);
    40.                 anim.SetBool ("isWalking", false);
    41.                 anim.SetBool ("isIdle", false);
    42.                 anim.SetBool ("isPatrolling", false);
    43.             }
    44.         }
    45.  
    46.         //otherwise it go Idle
    47.         else {
    48.             StartCoroutine(IdleAndPatrolling());
    49.             }
    50.         }
    51.  
    52.     IEnumerator IdleAndPatrolling(){
    53.  
    54.         anim.SetBool ("isIdle", true);
    55.         anim.SetBool ("isWalking", false);
    56.         anim.SetBool ("isAttacking", false);
    57.         anim.SetBool ("isPatrolling", false);
    58.  
    59.         yield return new WaitForSeconds(10);
    60.  
    61.         this.transform.Translate (0, 0, 0.05f);
    62.         anim.SetBool ("isPatrolling", true);
    63.         anim.SetBool ("isIdle", false);
    64.         anim.SetBool ("isAttacking", false);
    65.         anim.SetBool ("isWaiting", false);
    66.     }
    67. }      
    68.  
     
  4. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    First, running a coroutine from Update is generally bad, unless you account for it, you are actually running a call to start the coroutine once every frame. Which means that if for example, you have 60 frames a sec, you would call that coroutine 600 times during the 10 sec wait, which also means you'll get some strange behavior from this.

    You could have a coroutine that simply switches between two states, but only if it's not suppose to be attacking. While probably not the best design, you could have it do another wait at the end and then start the coroutine over again. Just make sure you don't try to idle/patrol while it's suppose to be attacking.
     
    martinamenegon likes this.