NPC Motion Solutions




Basic NPC Behaviors:


DESCRIPTION:
This example requires that we flip our direction on collisions. This is a simple example of using a vector to keep track of what direction a character is moving.

KEY CONCEPTS:
using vectors as directions
collision detection

PSEUDOCODE:
let there be a vector2 variable called direction,
with a value of Vector2.right (aka 1,0)

let there be a variable called speed, 

every fixed update,
move the ghost in direction by speed

on a collision,
flip direction by multiplying it by -1

DESCRIPTION:
This example requires getting random values , which we can easily accomplish with the Random.Range function. Also check out these other methods for generating random values. Note that picking truly random values looks pretty unnatural or jittery!

KEY CONCEPTS:
Random variables

PSEUDOCODE:
Let there be a variable called speed

every fixed update,
pick a random direction
move the ghost in that direction by speed
DESCRIPTION:
This example requires perlin noise, which is a good way to generate “pseudo-random” values that look natural, not too chaotic or jittery!

KEY CONCEPTS:
perlin noise

PSEUDOCODE:
let there be a variable called speed
let there be a variable called seedX;
let there be a variable called seedY;

when the game starts,
set seedX to a random decimal value between 0 and 100
set seedY to a random decimal value between 0 and 100

every fixed update,
let there be a new vector2 variable called direction
get a value with perlin noise using seedX and the current time in seconds, and assign it to direction.x
get a value with perlin noise using seedY and the current time in seconds, and assign it to direction.y
move the ghost by direction multiplied by speed



Wandering NPC Behaviors


DESCRIPTION:
This example is tricky because unlike the linear bouncer / goomba, you can’t just flip the direction by multiplying it by -1. Doing so would send the ghost back directly where it came from!

The simplest way to handle this is to flip just one part of the direction vector depending on where the NPC collided with something. 
  • If the NPC hit its top or bottom side, flip only the Y axis.
  • If the NPC hits its left or right side, flip only the X axis. 

KEY CONCEPTS:
direction vector, contact points, modifying the components of a vector (the x and y values) directly

PSEUDOCODE:
let there be a vector2 variable called direction
let there be a float variable called speed

when the game starts,
pick a diagonal direction to start moving in. for example (1,1), (-1,1), and so on

every fixed update,
move the ghost by direction multiplied by speed

on a collision
get the contact point of the collision
If the contact point is above or below the ghost, flip direction.y
if the contact point is to the left or right of the ghost, flip direction.x





DESCRIPTION:
I like this behavior for NPCs that wander aimlessly around town, like you might see in any JRPG or adventure game. This example alternates between two states:
  1. standing still
  2. moving along a cardinal direction (north south east west), chosen at random
the character switches between these states on a timer. We can use a bool (aka a true or false) variable to keep track of which state the ghost is in.

We will use the random.range function to pick one of four directions. Random range for integers takes two arguments, a minimum value (inclusive) and a max value (exclusive). To get one of four values at random, we need to then write it like so:
int choice = Random.Range(0,4);
which will return 0, 1, 2, or 3. Computers always start counting at 0!

KEY CONCEPTS:
making choices randomly with random.range
timing / delaying with a float timer variable
using a bool to keep track of NPC’s state

PSEUDOCODE:
Let there be a vector2 variable called direction
Let there be a float variable called timer
Let there be a float variable called speed
Let there be a bool variable called isMoving

every fixed update,
if the timer is  done and isMoving is false
pick a direction (see below)
reset the timer to 1 second
set isMoving to true
else if the timer done and isMoving is true
reset the timer to 1 second
set isMoving to false
run down the timer
if isMoving is true
move the ghost by direction multiplied by speed

To pick a direction:
let there be an integer called choice. assign it a random number from 0 (inclusive) to 4 (exclusive)
if choice is 0, set direction to up
if choice is 1, set direction to down
if choice is 2, set direction to left
if choice is 3, set direction to right



DESCRIPTION:
In this example, the ghost turns pseudo-randomly with perlin noise, and moves along it’s own “up” axis. When the ghost collides with something, it rotates 180 degrees.
This approach works well for cars, fish, missiles, and more.

KEY CONCEPTS:
Noise, Rotation

PSEUDOCODE:
  • let there be a float variable called maxTurn (in degrees), assign it 360

When the game starts,
  • use Rigidbody2D.SetRotation and Random.Range to set the character to a randomized starting rotation

Every fixed update,
  • get a noise value with perlinNoise
  • remap noise from 0:1 to -1 : 1 by multiplying it by 2 and subtracting 1
  • multiply noise by maxTurn to determine how much to rotate the NPC this frame
  • use Rigidbody2D.MoveRotation to set the rotation
  • move the rigidbody along the NPC’s up vector (transform.up)

On a collision,
  • Rotate the NPC 180 degrees



Interactive NPC Behaviors



DESCRIPTION:
In this example, the ghost moves towards the player character, and stops when it is touching the player.

We’ll need to determine the direction from the ghost to the player. For that, we can use a little vector math.

Remember, to get a vector going from poitn A to B is B - A!

KEY CONCEPTS:
getting a direction vector from one point to another

PSEUDOCODE:
let there be a bool variable called touching

Every fixed update, if touching is false:
Get a a direction vector going from the NPC to the target
On a collision enter,
if the NPC touched the player character, set touching to true
On a collision exit,
if the NPC was touching the player character, set touching to false




DESCRIPTION:
This example is similar to the Seeker, with a few differences:
  • the NPC moves away from the player
  • the NPC only moves when the player is within a certain distance

KEY CONCEPTS:
getting direction away from a given point
getting the distance between two points

PSEUDOCODE:
let there be:
a Vector2 called direction
a public Transform called avoidTarget
a float called avoidThreshold, with a value of 3
a float called speed, with a value of 1

every fixed update:
get a direction vector from the NPC to avoidTarget, and assign that value to a vector2 called toTarget

if the length of toTarget is greater than avoidThreshold:
set direction to (0,0)
else
set direction to toTarget.normalized * -1 (this creates a vector with a length of one, pointing away from the target)

move  the npc by direction multiplied by speed



DESCRIPTION:
Companion is a combination of Seeker and Avoider, where the NPC tries to stay a specific distance away from the player.

KEY CONCEPTS:
direction vectors
distance between two points

PSEUDOCODE:

let there be a public float called followDistance
let there be a public Transform called followTarget

every fixed update,
get the distance between the NPC and followTarget
if the distance is greater than followDistance,
move towards followTarget
if the distance is less than followDistance,
move away from followTarget


DESCRIPTION:
Blade trap is essentially a linear bouncer / goomba behavior that only “goes” when something moves through a trigger attached to the NPC. It has two states:
  • Rest: the NPC slowly returns to its starting position and waits there
  • Attack: the NPC rushes forward until it collides with something

NOTE that this NPC will need a trigger volume attached to it. I used a box collider that is offset from the NPC’s body

KEY CONCEPTS:
using triggers with npcs
complex behaviors with multiple states
collision detection

PSEUDOCODE:
Let there be:
a Vector2 called startPosition
a public Vector2 called direction, assign this in the editor to determine which way the ghost will go
a float called attackSpeed, set to a high value
a float called retreatSpeed, set to a small value
a bool called running, set to false

When the game starts,
assign the NPC’s current position to startPosition

every fixed update,
if running is true
move the ghost along direction
else
move the ghost towards its starting position

when an object is within the trigger, (ontriggerstay2D)
if NPC is close to the starting position, and running is false,
set running to true

when the NPC collides with something
if running is true
set running to false