Making NPC's wander in 2D

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!

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:

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

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// This makes an object move randomly in a set of directions, with some random time delay in between each decision
/// </summary>
public class Wanderer : MonoBehaviour
{
    internal Transform thisTransform;

    // The movement speed of the object
    public float moveSpeed = 0.2f;

    // A minimum and maximum time delay for taking a decision, choosing a direction to move in
    public Vector2 decisionTime = new Vector2(1, 4);
    internal float decisionTimeCount = 0;

    // 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
    internal Vector3[] moveDirections = new Vector3[] { Vector3.right, Vector3.left, Vector3.forward, Vector3.back, Vector3.zero, Vector3.zero };
    internal int currentMoveDirection;

    // Use this for initialization
    void Start()
    {
        // Cache the transform for quicker access
        thisTransform = this.transform;

        // Set a random time delay for taking a decision ( changing direction, or standing in place for a while )
        decisionTimeCount = Random.Range(decisionTime.x, decisionTime.y);

        // Choose a movement direction, or stay in place
        ChooseMoveDirection();
    }
  
    // Update is called once per frame
    void Update()
    {
        // Move the object in the chosen direction at the set speed
        thisTransform.position += moveDirections[currentMoveDirection] * Time.deltaTime * moveSpeed;

        if (decisionTimeCount > 0) decisionTimeCount -= Time.deltaTime;
        else
        {
            // Choose a random time delay for taking a decision ( changing direction, or standing in place for a while )
            decisionTimeCount = Random.Range(decisionTime.x, decisionTime.y);

            // Choose a movement direction, or stay in place
            ChooseMoveDirection();
        }
    }

    void ChooseMoveDirection()
    {
        // Choose whether to move sideways or up/down
        currentMoveDirection = Mathf.FloorToInt(Random.Range(0, moveDirections.Length));
    }
}

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.

8 Likes

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. :slight_smile:

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

@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.

1 Like

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

1 Like

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?

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.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// This makes an object move randomly in a set of directions, with some random time delay in between each decision
/// </summary>
public class Wanderer : MonoBehaviour

{
    internal Transform thisTransform;
    public Animator animator;

    // The movement speed of the object
    public float moveSpeed = 0.2f;

    // A minimum and maximum time delay for taking a decision, choosing a direction to move in
    public Vector2 decisionTime = new Vector2(1, 4);
    internal float decisionTimeCount = 0;

    // 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
    internal Vector3[] moveDirections = new Vector3[] { Vector3.right, Vector3.left, Vector3.up, Vector3.down, Vector3.zero, Vector3.zero };
    internal int currentMoveDirection;

    // Use this for initialization
    void Start()
    {
         // Cache the transform for quicker access
        thisTransform = this.transform;
        // Set a random time delay for taking a decision ( changing direction,or standing in place for a while )
        decisionTimeCount = Random.Range(decisionTime.x, decisionTime.y);

        // Choose a movement direction, or stay in place
        ChooseMoveDirection();
    }

    // Update is called once per frame
    void Update()
    {
        // Move the object in the chosen direction at the set speed
        Vector3 direction = moveDirections[currentMoveDirection];
        float xDir = direction.x;
        float yDir = direction.y;

        thisTransform.position += direction * Time.deltaTime * moveSpeed;

        if (animator)
        {
            animator.SetFloat("MoveX", xDir);
            animator.SetFloat("MoveY", yDir);
        }

        if (decisionTimeCount > 0) decisionTimeCount -= Time.deltaTime;
        else
        {
            // Choose a random time delay for taking a decision ( changing direction, or standing in place for a while )
            decisionTimeCount = Random.Range(decisionTime.x, decisionTime.y);

            // Choose a movement direction, or stay in place
            ChooseMoveDirection();
        }
    }

    void ChooseMoveDirection()
    {
        // Choose whether to move sideways or up/down
        currentMoveDirection = Mathf.FloorToInt(Random.Range(0, moveDirections.Length));
     

    }
}
2 Likes

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.

100000001076017–765145–Wanderer.unitypackage (17.5 KB)

How would you add idle animation to this code when the NPC stops?

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.

7231210--870166--RandomIdleWanderer.gif

With this code modification

7231210--870172--upload_2021-6-12_11-18-18.png

1 Like

How to make NPC on stop play IdleAnimation