Create dynamic enemies
Tutorial
·
Beginner
·
+10XP
·
0 mins
·
(20)
Unity Technologies

In this tutorial, you’ll create dynamic enemies that move through the level and also detect the player, making the gameplay more challenging.
Languages available:
1. Overview
In this tutorial, you’ll enhance your haunted house level by introducing a new type of dynamic enemy: the Ghost. Unlike the static Gargoyle, the Ghost will move through the environment. By the end of this tutorial, you’ll have a roaming enemy that actively patrols the hallways, making the player character’s escape more challenging and immersive.
2. Set the Ghost prefab
The first step in setting up your moving enemy is to create a prefab using the Ghost model. This will allow you to reuse and edit the Ghost enemy consistently across your scene. Since you’ve already worked with prefabs in previous steps, this process should feel familiar.
We’ve provided two different types of ghosts for you to choose from: an Eye Ghost and a regular Ghost. Pick the one you like best!
1. Create an empty container for your Ghost character:
- Right-click in the Hierarchy window (or open the Add (+) dropdown in the upper-left corner) and select Create Empty. This will create a new empty GameObject in your scene.
- Rename the empty GameObject “Ghost”.
2. Add the Ghost to the scene:
- In the Project window, navigate to _3DStealthGame > Art > Models > Characters > Enemies and examine the two ghost models (EyeGhost_model, RegularGhost_Model)
- Drag the [Type]Ghost_Model of your choice onto the Ghost GameObject in the Hierarchy window.
- In the Inspector window, ensure the Transform component of the [Type]Ghost_Model has its Position property set to X = 0, Y = 0, and Z = 0.
- While you work on setting up the Ghost properly, place it near the Player GameObject using the Move Tool. Don’t worry, you’ll position it more accurately later on.
3. Create a Ghost prefab:
- From the Hierarchy window, drag the Ghost GameObject into the _3DStealthGame > Prefabs > Characters folder in the Project window.
- If prompted, select Original Prefab.
In the next step you’ll bring the Ghost GameObject to life with animations. Let’s make this creepy creature move!
3. Animate the Ghost
Now that your Ghost prefab is in place, it’s time to bring it to life with animation. The Ghost will have a simple looping walk animation using an Animator Controller, just like you did with the Gargoyle. This will make the enemy look like it’s actively patrolling the haunted house.
1. Make sure you're in prefab editing mode:
If you're not in prefab editing mode, double-click the Ghost prefab in the Project window to enter it.
2. Create an Animator Controller:
- In the Project window, navigate to _3DStealthGame > Art > Animation > Animators.
- Right-click the Animators folder and select Create > Animation > Animator Controller.
- Name the new Animator Controller “Ghost_Controller”.
3. Set the Animator component:
- In the Hierarchy window, use the foldout (triangle) to expand the Ghost GameObject and select the [Type]Ghost_Model GameObject.
- In the Inspector window, locate the Animator component.
- Click and drag the Ghost_Controller Animator Controller onto the Animator component’s Controller property box.
4. Add the walking animation:
- Double-click the Ghost_Controller Animator Controller to open it in the Animator window.
- In the Project window, navigate to _3DStealthGame > Art > Animation > Animations.
We’ve provided two different walking animations for your ghost; feel free to choose the one you like best.
- Expand the following model assets:
- Ghost@Walk1
- Ghost@Walk2
- Inside each, locate the animation clips named Walk1 and Walk2.
- To preview the animations, select each one. In the Inspector window, scroll down to find the Preview section, then select the Play button to see how it looks.
Note: You’ll notice that each animation plays with a different character model. But don’t worry, both Ghost characters share the same skeleton (that does sound a little weird), which basically means they have the same body structure underneath. So the animations are fully compatible and will work perfectly on both models.
- Once you’ve chosen your favorite walking animation, click and drag the Walk animation into the Animator window.
- Select the Play button to enter Play mode and check out your animated Ghost.
Your Ghost enemy is now animated and ready to haunt the hallways. In the next step, you’ll add a Capsule Collider component and a Rigidbody component so the Ghost can physically interact with the player character.
4. Add physics to the Ghost
Just like the player character and the Gargoyles, Ghosts also need a physical presence in the scene to interact properly with other GameObjects. This means they require a collider.
Since Ghosts move, they also need a Rigidbody component, but it must be set up carefully: colliding with the Player GameObject shouldn’t push the Ghost GameObject around.
1. Add a collider:
- Confirm that you are still in prefab editing mode for the Ghost prefab.
- In the Inspector window, select the Add Component button.
- Search for and add the Capsule Collider component.
- Adjust the collider to fit the Ghost prefab’s shape; the recommended values are as follows:
- Set the Center property to X = 0, Y = 0.6, and Z = 0.
- Set the Radius property to 0.25.
- Set the Height property to 1.2.
2. Add a Rigidbody component:
- In the Inspector window, select the Add Component button.
- Search for and add the Rigidbody component.
- Enable Is Kinematic.
Note: A kinematic Rigidbody component won’t react to forces like collisions, but it will still be able to detect them.
Your Ghost enemy now has boundaries and the ability to interact with other GameObjects in the scene. In the next step, you’ll add the PointOfView prefab so that the Ghost prefab will be able to detect the Player GameObject, just like the Gargoyle prefab.
5. Make the Ghost an observer
Your Ghost prefab looks really cute, but it currently lacks the capacity to detect the Player GameObject that the other Gargoyle enemy has.
Luckily, you’ve already set up a PointOfView prefab for the Gargoyle that handles detection. Instead of building it from scratch again, let’s reuse that same prefab and attach it to the Ghost prefab.
1. Attach the PointOfView prefab:
- Confirm that you are still in prefab editing mode for the Ghost prefab.
- Drag the PointOfView prefab from the _3DStealthGame > Prefabs folder onto the Ghost GameObject in the Hierarchy window.
Now the Ghost prefab has its own PointOfView and can use the detection system!
2. Adjust the PointOfView for the Ghost prefab’s height and orientation:
- In the Hierarchy window, select the PointOfView GameObject.
- In the Inspector window, update the PointOfView GameObject’s Transform component as follows:
- Position property: X = 0, Y = 0.75, and Z = 0.4.
- Rotation property: X = 0, Y = 0, and Z = 0.
Note: We set the Rotation property to X = 0, Y = 0, and Z = 0 because the Ghost GameObject looks forward, unlike the Gargoyle GameObject that stands on a podium and needs to look slightly downwards.
3. Set the Player reference for detection:
- Exit prefab editing mode to go back to the normal Scene view.
- In the Inspector window, locate the Ghost GameObject’s Observer script component and click and drag the Player GameObject from the Hierarchy window onto the Player property box to assign its Transform component
4. Observe the detection:
- Select the Play button to enter Play mode.
- Move the Player GameObject into the Ghost GameObject's field of vision to see the console message showing it detected the Player GameObject, just like the Gargoyle GameObject did.
Note: Remember to exit Play mode when you’re done testing.
Now your Ghost prefab is set up as an Observer and can detect the Player GameObject. In the next step, you’ll add movement functionality so that the Ghost GameObject can dynamically move along a determined path.
6. Create the WaypointPatrol script
Now you’re ready to set up the Ghost GameObject so it can move through your game environment. You’ll create a script that manages how the Ghost moves around the space.
This script uses a simple graph-based approach: you provide a set of points for the Ghost to visit, and the script calculates the shortest path between each point in order. It’s a straightforward way to achieve the behavior we want.
However, another common method for dynamic enemy movement in Unity is using Unity’s AI Navigation package. If you’d like to try that approach instead, head to Step 4 of the Bonus Features tutorial.
Also, if you're curious to understand how the script works, check out Step 9, where we explain the key parts in more detail. If you already have some experience, you’re welcome to write your own version of the WaypointPatrol script and customize it as you like!
1. Create a new script:
- In the Project window, navigate to _3DStealthGame > Scripts.
- Right-click the Scripts folder.
- Select Create > MonoBehaviour Script.
- Rename the script “WaypointPatrol” and press Enter.
2. Add the script to the Ghost prefab:
- In the Project window, double-click the Ghost prefab to open it in prefab editing mode.
- Select the Ghost GameObject and click and drag the WaypointPatrol script from the Project window into the Inspector window to add it as a component.
3. Open the script:
- Double-click the newly created WaypointPatrol script.
- This will open the script in the code editor or IDE you've set up (such as Visual Studio).
4. Copy the provided code:
- Once the script is open, delete any pre-filled content.
- Copy and paste the following lines of code into the script, then save it:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class WaypointPatrol : MonoBehaviour
{
public float moveSpeed = 1.0f;
public Transform[] waypoints;
private Rigidbody m_RigidBody;
int m_CurrentWaypointIndex;
void Start ()
{
m_RigidBody = GetComponent<Rigidbody>();
}
void FixedUpdate ()
{
Transform currentWaypoint = waypoints[m_CurrentWaypointIndex];
Vector3 currentToTarget = currentWaypoint.position - m_RigidBody.position;
if (currentToTarget.magnitude < 0.1f)
{
m_CurrentWaypointIndex = (m_CurrentWaypointIndex + 1) % waypoints.Length;
}
Quaternion forwardRotation = Quaternion.LookRotation(currentToTarget);
m_RigidBody.MoveRotation(forwardRotation);
m_RigidBody.MovePosition(m_RigidBody.position + currentToTarget.normalized * moveSpeed * Time.deltaTime);
}
}
7. Place Ghosts in your scene
Now you’re ready to bring your haunted environment to life by populating it with moving enemies. In this step, you’ll duplicate the Ghost GameObject and place enemies throughout the level. Each Ghost GameObject should be positioned in a way that creates unique challenges for the player, such as patrolling corridors, guarding important areas, or restricting movement near exits. These Ghost GameObjects will form the foundation for your enemy patrol behavior.
1. Decide how many Ghost GameObjects you want:
Before you add any Ghost GameObjects, take a moment to analyze your level and think about how each ghost can enhance gameplay and increase difficulty. Here are a few suggestions:
- Place a one to patrol near the player’s starting room
- Add one in a long corridor to encourage the player to use side rooms for cover
- Use another to circle around a central object, like a dining room table
- Have one patrol near the level’s exit to create tension during the final stretch.
Once you have an idea of where ghosts would be most effective, move on to the next steps.
2. Duplicate the Ghost GameObject:
- In the Hierarchy window, select the original Ghost GameObject.
- Press Ctrl+D (macOS: Cmd+D) to duplicate it.
- Repeat this process to create as many ghosts as you want in the scene.
3. Position each ghost in the level:
- Use the Move/Rotate Tools to place each Ghost GameObject in its designated area.
Remember, try to place the ghosts where their patrols will naturally block or complicate the player’s movement; this will make for a more interesting challenge!
4. Organize ghosts inside the Enemies GameObject:
- In the Hierarchy window, locate the Enemies parent GameObject you created in the last tutorial.
- Hold the Shift key, select all the Ghost GameObjects you created, and drag them inside the Enemies GameObject to set them as child GameObjects.
With all ghosts placed and enemies organized, your level now has enemies that increase tension and gameplay complexity. In the next step, you’ll create and assign waypoints so that each ghost can move around its patrol path.
8. Create and position the ghost waypoints
To guide each ghost along a patrol path, you’ll use empty GameObjects as waypoints. Since you only need their Transform positions, there’s no need for visual components.
In this step, you’ll create and organize all the waypoints first, then assign two or more to each Ghost GameObject based on where you placed them in the previous step. This modular approach allows each ghost to follow a unique path, increasing variety in enemy behavior.
1. Create a patrol path for the first ghost:
Before you place any waypoints, take a moment to decide how you want this ghost to patrol.
- Will it move between two points, like a short hallway patrol?
- Will it follow a more complex route, like circling a room or weaving through multiple spaces?
Once you have a path in mind:
- Right-click in the Hierarchy window and select Create Empty.
- Rename the empty GameObject “Waypoint_1_Start”.
Note: For better visibility, switch to a top-down view by clicking the Y axis on the Scene Gizmo, located at the top-right corner of the Scene view.
- Position the Waypoint_1_Start GameObject at the beginning of the first ghost’s patrol path.
- Press Ctrl+D (macOS: Cmd+D) to duplicate and create more waypoints.
- After each duplication, rename each waypoint sequentially (Waypoint_1_End, Waypoint_1_Mid, etc.) and move them to follow your chosen path.
There’s no required number of waypoints, just add enough to define the route you want. Each waypoint represents a step in the ghost’s journey.
2. Make waypoints visible in the Scene view:
By default, empty GameObjects can be hard to see. To make them easier to spot:
- Select all the Waypoint GameObjects you just created in the last step.
- In the Inspector window, select the cube icon in the upper-left corner.
- Choose a colored icon from the dropdown menu (for example, a red or blue label).
This will help you visualize paths clearly in the Scene view as you build your level.
3. Assign waypoints to the first Ghost GameObject:
- Select the first Ghost GameObject in the Hierarchy window.
- In the Inspector window, select the Add Component button.
- Search for and add the Waypoint Patrol component.
- Select the Add (+) button to add as many Waypoint slots as you defined.
- Click and drag the corresponding waypoints (like Waypoint_1_Start, Waypoint_1_End) into each box.
4. Create more paths for the other ghosts in your scene:
- Repeat Steps 1–3 for the remaining Ghost GameObjects.
Give each one its own waypoints and path, adjusting the number and layout to fit your level’s design.
5. Organize waypoints inside a parent GameObject:
- Hold Shift and select all the waypoints, then right-click on them and choose Create Empty Parent.
- Rename the new parent GameObject “Waypoints”.
- Reset its Transform position to (X = 0, Y = 0, and Z = 0).
6. Test the patrol paths:
- Enter Play mode and observe the ghosts in action.
- Watch their movement to verify they follow the correct path.
Each Ghost GameObject now has its own patrol path! With that, you've set up all the core gameplay functionality. In the next tutorial, you’ll add a UI to give players visual feedback when they get caught or escape, and you’ll also complete the game-ending loop.
9. Review the WayPointPatrol script
This step is completely optional, but if you’re curious about how the WayPointPatrol script works, here are a few parts of the code you might find especially interesting.
1. Main patrol logic:
void FixedUpdate ()
{
Transform currentWaypoint = waypoints[m_CurrentWaypointIndex];
Vector3 currentToTarget = currentWaypoint.position - m_RigidBody.position;- Calculates the direction vector from the current position to the target waypoint.
- Similarly, when the player character exits the trigger area, it sets the flag back to false.
2. Waypoint switching:
if (currentToTarget.magnitude < 0.1f)
{
m_CurrentWaypointIndex = (m_CurrentWaypointIndex + 1) % waypoints.Length;
}
- If the GameObject is close enough to the current waypoint, it switches to the next waypoint in the list.
- Uses modulo (%) to loop back to the first waypoint after the last one.
3. Moving toward waypoint:
Quaternion forwardRotation = Quaternion.LookRotation(currentToTarget);
m_RigidBody.MoveRotation(forwardRotation);
m_RigidBody.MovePosition(m_RigidBody.position + currentToTarget.normalized * moveSpeed * Time.deltaTime);- Instantly rotates the GameObject to face the direction of the next waypoint.
- Moves the GameObject in the direction of the waypoint at a constant speed, frame-rate independent.
If you’d like to dive deeper into programming, we recommend checking out the Junior Programmer Pathway, where you’ll learn the fundamentals of coding in Unity.
10. Next steps
With these steps completed, you’ve implemented a dynamic enemy that can catch the player character while patrolling an area.
In the next tutorial, you’ll define the game ending conditions, set up a simple UI, and create a basic game loop for the game.