Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Making NPC's wander in 2D

Discussion in '2D' started by ElnuDev, Apr 4, 2018.

  1. ElnuDev

    ElnuDev

    Joined:
    Sep 24, 2017
    Posts:
    298
    I am making a 2D top-down RPG. I have animals placed on the terrain at random locations; currently, they are just pacing back and forth. This is pretty boring, so I felt like using something like the wander script from the Unity Community Wiki. Unfortunately, this requires a 3D character controller. Any ideas on getting around this? Thanks!
     
  2. puppeteer

    puppeteer

    Joined:
    Sep 15, 2010
    Posts:
    1,282
    For a top-down 2D game you can simplify the code to work for you. I'm assuming movement is limited to 4 directions, right?

    So this is how I would make a simple wanderer code:

    ezgif.com-video-to-gif(1).gif

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. /// <summary>
    6. /// This makes an object move randomly in a set of directions, with some random time delay in between each decision
    7. /// </summary>
    8. public class Wanderer : MonoBehaviour
    9. {
    10.     internal Transform thisTransform;
    11.  
    12.     // The movement speed of the object
    13.     public float moveSpeed = 0.2f;
    14.  
    15.     // A minimum and maximum time delay for taking a decision, choosing a direction to move in
    16.     public Vector2 decisionTime = new Vector2(1, 4);
    17.     internal float decisionTimeCount = 0;
    18.  
    19.     // The possible directions that the object can move int, right, left, up, down, and zero for staying in place. I added zero twice to give a bigger chance if it happening than other directions
    20.     internal Vector3[] moveDirections = new Vector3[] { Vector3.right, Vector3.left, Vector3.forward, Vector3.back, Vector3.zero, Vector3.zero };
    21.     internal int currentMoveDirection;
    22.  
    23.     // Use this for initialization
    24.     void Start()
    25.     {
    26.         // Cache the transform for quicker access
    27.         thisTransform = this.transform;
    28.  
    29.         // Set a random time delay for taking a decision ( changing direction, or standing in place for a while )
    30.         decisionTimeCount = Random.Range(decisionTime.x, decisionTime.y);
    31.  
    32.         // Choose a movement direction, or stay in place
    33.         ChooseMoveDirection();
    34.     }
    35.  
    36.     // Update is called once per frame
    37.     void Update()
    38.     {
    39.         // Move the object in the chosen direction at the set speed
    40.         thisTransform.position += moveDirections[currentMoveDirection] * Time.deltaTime * moveSpeed;
    41.  
    42.         if (decisionTimeCount > 0) decisionTimeCount -= Time.deltaTime;
    43.         else
    44.         {
    45.             // Choose a random time delay for taking a decision ( changing direction, or standing in place for a while )
    46.             decisionTimeCount = Random.Range(decisionTime.x, decisionTime.y);
    47.  
    48.             // Choose a movement direction, or stay in place
    49.             ChooseMoveDirection();
    50.         }
    51.     }
    52.  
    53.     void ChooseMoveDirection()
    54.     {
    55.         // Choose whether to move sideways or up/down
    56.         currentMoveDirection = Mathf.FloorToInt(Random.Range(0, moveDirections.Length));
    57.     }
    58. }
    59.  
    You can freely set any array of directions so it's not limited to 2D space ( you can make the character move upwards for example). You can make it move in 8 directions too.

    For a 2D top-down game you can also expand it to have a set of Sprites that are switched based on the direction you choose, as well as an area Rect limit to make sure the cow only wanders within the intended area and doesn't go far off.
     
  3. ElnuDev

    ElnuDev

    Joined:
    Sep 24, 2017
    Posts:
    298
    Thanks a bunch! I already have all my animations rigged with xMove and yMove blend tree variables, which shouldn't be too hard to integrate with the script. I'll post how it comes out. :)
     
  4. oliver_unity892

    oliver_unity892

    Joined:
    Oct 28, 2019
    Posts:
    91
    I'm trying to use this script to make an NPC "wander" in my 2D top down game. However the NPC object only moves along the X access, it never moves along the Y access (up and down).

    Any ideas why?

    Olly
     
  5. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @oliver_unity892

    "Any ideas why?"

    Didn't check the code but a general hint;

    2D works on XY plane (most often in Unity), whereas ground plane in 3D (in Unity) is oriented along XZ plane.
     
    ElnuDev likes this.
  6. puppeteer

    puppeteer

    Joined:
    Sep 15, 2010
    Posts:
    1,282

    Try to switch these direction values with ones that fit your plane axis
    internal Vector3[] moveDirections = new Vector3[] { Vector3.right, Vector3.left, Vector3.forward, Vector3.back, Vector3.zero, Vector3.zero }

    instead of Vector3.forward and Vector3.back, try to use Vector3.up and Vector3.down and see how it behaves
     
    slimgiltsoul likes this.
  7. Alex_n_Italy

    Alex_n_Italy

    Joined:
    Sep 22, 2020
    Posts:
    1
    Heyo, so i want to apply a movement animation to the npc when he walks, but i've been struggling as i'm trying to use the same procedure i use with the player, aka:
    • playerAnimator.SetFloat("Movement", moveDirection.sqrMagnitude);
    what should i do, with this code for apply a movement animation?
     
  8. slimgiltsoul

    slimgiltsoul

    Joined:
    Aug 4, 2017
    Posts:
    3
    Posting here to write "Thank you so much!" to puppeteer. If anyone is having problems implementing the NPC/enemy wandering system from Ruby's Adventure (which might be deprecated by now? not sure), this is an amazing solution.

    Obviously, make sure your new script is called 'Wanderer.cs', otherwise it will get confused. Thanks so much Puppeteer.

    Edit:

    If anyone reading this thread would like to fix the Y axis movement, and also add animation. You'll need to make an animator with a state machine set up for directions - you can copy one from the RPG creator kit. You should name the parameters for the state machine 'MoveX' and 'MoveY'. Any issues setting it up, and you can follow the 'Ruby's Adventure' tutorial, under 'animation'.

    This works beginning on an Idle animation, and 4 directional movements.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. /// <summary>
    6. /// This makes an object move randomly in a set of directions, with some random time delay in between each decision
    7. /// </summary>
    8. public class Wanderer : MonoBehaviour
    9.  
    10. {
    11.     internal Transform thisTransform;
    12.     public Animator animator;
    13.  
    14.     // The movement speed of the object
    15.     public float moveSpeed = 0.2f;
    16.  
    17.     // A minimum and maximum time delay for taking a decision, choosing a direction to move in
    18.     public Vector2 decisionTime = new Vector2(1, 4);
    19.     internal float decisionTimeCount = 0;
    20.  
    21.     // The possible directions that the object can move int, right, left, up, down, and zero for staying in place. I added zero twice to give a bigger chance if it happening than other directions
    22.     internal Vector3[] moveDirections = new Vector3[] { Vector3.right, Vector3.left, Vector3.up, Vector3.down, Vector3.zero, Vector3.zero };
    23.     internal int currentMoveDirection;
    24.  
    25.     // Use this for initialization
    26.     void Start()
    27.     {
    28.          // Cache the transform for quicker access
    29.         thisTransform = this.transform;
    30.         // Set a random time delay for taking a decision ( changing direction,or standing in place for a while )
    31.         decisionTimeCount = Random.Range(decisionTime.x, decisionTime.y);
    32.  
    33.         // Choose a movement direction, or stay in place
    34.         ChooseMoveDirection();
    35.     }
    36.  
    37.     // Update is called once per frame
    38.     void Update()
    39.     {
    40.         // Move the object in the chosen direction at the set speed
    41.         Vector3 direction = moveDirections[currentMoveDirection];
    42.         float xDir = direction.x;
    43.         float yDir = direction.y;
    44.  
    45.         thisTransform.position += direction * Time.deltaTime * moveSpeed;
    46.  
    47.         if (animator)
    48.         {
    49.             animator.SetFloat("MoveX", xDir);
    50.             animator.SetFloat("MoveY", yDir);
    51.         }
    52.  
    53.         if (decisionTimeCount > 0) decisionTimeCount -= Time.deltaTime;
    54.         else
    55.         {
    56.             // Choose a random time delay for taking a decision ( changing direction, or standing in place for a while )
    57.             decisionTimeCount = Random.Range(decisionTime.x, decisionTime.y);
    58.  
    59.             // Choose a movement direction, or stay in place
    60.             ChooseMoveDirection();
    61.         }
    62.     }
    63.  
    64.     void ChooseMoveDirection()
    65.     {
    66.         // Choose whether to move sideways or up/down
    67.         currentMoveDirection = Mathf.FloorToInt(Random.Range(0, moveDirections.Length));
    68.      
    69.  
    70.     }
    71. }
     
    Last edited: Oct 18, 2020
  9. puppeteer

    puppeteer

    Joined:
    Sep 15, 2010
    Posts:
    1,282
    Alright, this might be way too late but it may still be useful to some.

    So I tried learning this Blend Tree thing and it looks like an awesome way to control animations ( don't know why I never used it before ). Anyway, import this package into your project to see my suggested solution to this wanderer animation.

    Basically in the Animator controller we have two floats ("Horizontal", "Vertical") and they are assigned as a 2D set in the blend tree, and each animation runs based on the directional values I set. Then in the code I call the X and Z values according to "Horizontal" and "Vertical" floats, respectively.




    It works really nicely, I'm going to start using this more often.
     

    Attached Files:

  10. katematv

    katematv

    Joined:
    May 31, 2021
    Posts:
    1
    How would you add idle animation to this code when the NPC stops?
     
  11. puppeteer

    puppeteer

    Joined:
    Sep 15, 2010
    Posts:
    1,282
    Not sure if this is the right way to do it, but maybe we can check if the player is stopped and then play a random animation from an index, which then goes back to the blend tree.

    RandomIdleWanderer.gif

    With this code modification

    upload_2021-6-12_11-18-18.png
     
    Johan_Liebert123 likes this.
  12. mrseal122

    mrseal122

    Joined:
    Jul 8, 2021
    Posts:
    1
    How to make NPC on stop play IdleAnimation