Add an enemy character
Tutorial
·
Beginner
·
+0XP
·
25 mins
·
Unity Technologies

In this tutorial, you’ll build on your previous work to create moving enemies for the player character.
By the end of this tutorial, you’ll write a new script to control enemy movement and behavior using concepts that you have already learned in this course.
1. Overview
In the Health System unit, you created static hazards in the form of damage zones. Now you’ll build on that work to create dynamic hazards for the player to deal with: enemies.
Whatever asset pack you’re working with, the enemies for your 2D Beginner game are malfunctioning machinery: a robot, a vending machine, or a boiler. These enemy characters will move around and attack the player character until the player fixes them using a projectile mechanic that you’ll implement later in this unit.

By the end of this tutorial, you’ll write a new script to control enemy movement and behavior using concepts that you have already learned in this course.
Working on your own project?
This tutorial is part of Beginner 2D: Adventure Game and refers to the game project for that course, but this information may be helpful for any beginner creator who wants to create enemy characters in their own game.
Note: For the purposes of this tutorial, we chose to use the Ruby’s Adventure asset set, and the file paths used in instructions will reflect this. If you chose another asset set, the file names will be the same, but in your corresponding theme folder.
2. Create an enemy GameObject
You’ve previously set up a DamageZone GameObject, now you’re going to repeat that process for an enemy character. Try to set up the GameObject using the guidance below but without following the detailed instructions at the bottom of this step.
Use the following guidance to help you as you create your enemy:
- You can find an Enemy sprite in the Sprites folder for your chosen asset pack.
- Give the Enemy GameObject the components and configuration that it needs to move and collide with the player character and environment.
- Set the pivot for the Enemy GameObject as you did for the player character.
- Create a new script called “EnemyController”and attach it to the GameObject.
- You might want to add more than one enemy to the game, so remember to turn the GameObject into a prefab.
Check your enemy configuration
Review these high-level instructions to confirm that you have set up the enemy correctly:
1. Navigate to Assets > Art > [Your Chosen Project] > Characters and select the Enemy sprite.
2. In the Sprite Editor window, set the sprite’s Custom Pivot to the following values:
- X = 0.5
- Y = 0
3. Create a new GameObject using the Enemy sprite.
4. In the Sprite Renderer component, set the Sprite Sort Point to Pivot.
5. Add a Box Collider 2D component, resize the sprite, and enable the Is Trigger setting.
6. Add a Rigidbody 2D component. Set the Gravity Scale to 0, then use the foldout (triangle) to expand the Constraints section and enable Freeze Rotation.
7. Create a new script called “EnemyController” and attach it to the Enemy GameObject.
8. Turn the GameObject into a prefab.
3. Write a script to move the enemy
The enemy character for your game is going to move in a predictable patrol pattern, rather than in more unexpected ways. Before we guide you through writing instructions for the patrol, take some time to apply what you’ve learned already to make the Enemy GameObject start to move horizontally when the game launches.
You’ll need to do the following:
- Declare a public variable to control the enemy speed.
- Create a variable of type Rigidbody2D and then use GetComponent to store a reference to the Rigidbody2D component on the Enemy GameObject in it.
- Adapt the movement calculation that you used for the player character before you based it on user input.
- Apply the updated position information in the position variable to the actual Rigidbody 2D component on the Enemy GameObject using Rigidbody2D.MovePosition, as you did when you fixed the player character’s movement jittering.
Tip: Remember to write your movement code in the correct function for physics calculations to avoid glitches.
Review your movement code
To check through the work that you have done in the script, use the following guidance:
- There should be no changes to the using statements at the top of the script. The enemy character’s movement is not going to be controlled by player input, so the script does not need input system access.
- At the beginning of the EnemyController class, above the Start function, you need two new variables:
- A public float variable to control the enemy speed.
- A Rigidbody2D variable to contain the reference to the Enemy GameObject’s Rigidbody 2D component.
public float speed;
Rigidbody2D rigidbody2d;- In the Start function, you need to get the reference to the Rigidbody2D component for the Enemy GameObject and store it in the variable that you created, as you’ve done previously in the PlayerController script:
void Start()
{
rigidbody2d = GetComponent<Rigidbody2D>();
}- Physics calculations need to be made in the FixedUpdate function rather than the Update function. You need three instructions in this function, based on the code you wrote to test player character movement initially before you set up player input:
{
Vector2 position = rigidbody2d.position;
position.x = position.x + speed * Time.deltaTime;
rigidbody2d.MovePosition(position);
}Here’s an explanation of this code:
- The first instruction stores the Enemy GameObject’s Rigidbody 2D component’s position in a Vector2 variable called position.
- The second instruction increments the component’s x-axis position by the speed variable multiplied by Time.deltaTime (the time that it takes each frame to render), and spurs it in the position variable.
- The third instruction uses MovePosition to apply the updated position information in the position variable to the actual Rigidbody 2D component on the Enemy GameObject.
Check your script, then save and return to the Unity Editor.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyController : MonoBehaviour
{
public float speed;
Rigidbody2D rigidbody2d;
// Start is called before the first frame update
void Start()
{
rigidbody2d = GetComponent<Rigidbody2D>();
}
// FixedUpdate has the same call rate as the physics system
void FixedUpdate()
{
Vector2 position = rigidbody2d.position;
position.x = position.x + speed * Time.deltaTime;
rigidbody2d.MovePosition(position);
}
}
Test the enemy’s movement
Back in the Unity Editor, set a value for the exposed Speed property and then check the enemy movement in Play mode.
When you’re finished, return to your IDE.
4. Add vertical movement for the enemy
Although the enemy characters aren’t going to have sophisticated movement patterns, it would be more realistic and engaging for players if the patrol movement could be horizontal or vertical in the game environment.
To adjust your script to add vertical movement, follow these instructions:
1. At the top of the EnemyController class, declare a new public bool variable called “vertical”:
public bool vertical;Remember, bool type variables contain Boolean values, which can be true or false. You can use this variable to determine whether the enemy will move along the x-axis (if vertical is false), or the y-axis (if vertical is true).
2. Now you’re going to edit the movement code you’ve already written. At the top of the FixedUpdate function, beneath the Vector2 variable declaration, add the following two conditional statements:
if ()
{
}
else
{
}Here’s an explanation of this code:
- You’ve encountered if statements before; when the conditions in the parentheses are met, Unity will execute the code in the statement.
- The else statement addition here contains code that will be executed when the condition for the if statement is not met, so it doesn’t need its own condition.
Before you continue to the next instruction, try to apply what you have learned so far and use the instruction that sets the enemy's position to complete the if and else statements.
3. Check your statements against the code below:
if (vertical)
{
position.y = position.y + speed * Time.deltaTime;
}
else
{
position.x = position.x + speed * Time.deltaTime;
}Here’s an explanation of this code:
- The if statement takes your variable vertical as its condition; when that Boolean is true, y-axis movement is applied to the enemy.
- The else statement does not take its own condition because it is linked to the if statement’s condition. When the vertical Boolean is false, x-axis movement is applied to the enemy.
4. Make any adjustments that you need to and check that the FixedUpdate function contains the following code:
void FixedUpdate()
{
Vector2 position = rigidbody2d.position;
if (vertical)
{
position.y = position.y + speed * Time.deltaTime;
}
else
{
position.x = position.x + speed * Time.deltaTime;
}
rigidbody2d.MovePosition(position);
}5. Save your changes.
5. Challenge: Add a timer for patrol movement
The enemy needs to move forward and back on whichever axis it is moving along, or it will move outside the bounds of the gameplay area. You can achieve this by using a timer for the movement, just as you did when you implemented a cooldown period after the player character takes damage.
Try to apply what you have learned so far to create a timer that moves the enemy in one direction until the timer count reaches zero, and then reverses the direction and resets the timer.
Use the following guidance to help you as you script the timer:
- You will need to create variables to store the timer for movement before the direction is reversed, the current value of the timer, and the enemy’s current direction on the axis for movement.
- You will need to set an initial value to the timer and then update it every frame.
- Use a conditional statement to change the enemy’s direction.
Review your patrol code
To check through the work that you have done in on the script, use the following guidance:
- At the beginning of the enemy controller class, you need one new public variable so you can adjust the timer for movement in the Inspector:
public float changeTime = 3.0f;changeTime is a float variable, which is a good fit for countdowns.
- You also need two private variables:
float timer;
int direction = 1;Here’s an explanation of your change:
- The float variable timer will store the current value of the movement countdown; the initial changeTime value and then each subsequent negative increment before the movement is reversed.
- The direction variable stores the enemy’s direction of movement along your chosen axis — a positive value (1) forward movement on the axis or a negative value (-1) for backward movement.
- The movement direction will only ever be set to 1 or -1, so the integer variable type is a good fit for this situation.
In the Start function, you need an instruction that sets the timer variable’s initial value to changeTime (the full countdown value before the movement direction changes):
timer = changeTime;At the top of the Update function, you need an instruction that subtracts Time.deltaTime from the movement timer:
timer -= Time.deltaTime;You previously used the subtract operator (-=) in exactly the same way for the damage cooldown timer.
Still in the Update function, you need to add an if statement to control what happens when the timer count reaches zero:
if (timer < 0)
{
direction = -direction;
timer = changeTime;
}Here’s an explanation of this code:
- This if statement is executed if the timer value is less than (<) zero,
- The direction variable is set to minus the current value, reversing the current direction (as two negatives cancel each other to make the value positive).
- The timer is reset to the full changeTime value.
- Finally, in the Fixed Update function, you need to revise the two instructions that apply movement to the enemy to multiply the speed by the correct direction on the movement axis:
if (vertical)
{
position.y = position.y + speed * direction * Time.deltaTime;
}
else
{
position.x = position.x + speed * direction * Time.deltaTime;
}Check your script in full, then save and return to the Unity Editor.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyController : MonoBehaviour
{
// Public variables
public float speed;
public bool vertical;
public float changeTime = 3.0f;
// Private variables
Rigidbody2D rigidbody2d;
float timer;
int direction = 1;
// Start is called before the first frame update
void Start()
{
rigidbody2d = GetComponent<Rigidbody2D>();
timer = changeTime;
}
// Update is called every frame
void Update()
{
timer -= Time.deltaTime;
if (timer < 0)
{
direction = -direction;
timer = changeTime;
}
}
// FixedUpdate has the same call rate as the physics system
void FixedUpdate()
{
Vector2 position = rigidbody2d.position;
if (vertical)
{
position.y = position.y + speed * direction * Time.deltaTime;
}
else
{
position.x = position.x + speed * direction * Time.deltaTime;
}
rigidbody2d.MovePosition(position);
}
}
Test the enemy’s update patrol movement
Set initial values in the Enemy Controller (Script) component and then test your changes in Play mode. The following setting configuration is a good starting point for testing:
- Speed to 1
- Change Time to 3
- Vertical to True
If the enemy spends too long outside of the camera bounds, you can adjust its initial position or change the patrol timer and speed.
6. Add damage for enemy collisions
You’ve now created a moving enemy — now it’s time to apply damage when it collides with the player character. Although this code will be similar to what you’ve previously written for the damage zones, this time you’re going to use a different Unity function: OnTriggerEnter2D.
Triggers are, as their name suggests, like triggering an alert. Although triggers use collision functionality, they might not have anything to do with a collision between in-game objects — for example, you could create an invisible trigger when a player goes through a particular doorway.
To set up damage on collision, follow these instructions:
1. Return to the EnemyController script in your IDE.
2. Below the FixedUpdate function, add a new OnTriggerEnter2D function:
void OnTriggerEnter2D(Collider2D other)
{
}Here’s an explanation of this code:
- The type of the parameter for this function (other) is Collider2D.
- The Collider2D type contains a lot of data about the collision, including the other GameObject involved in any collisions with the enemy.
3. Add the following instruction inside the function:
PlayerController player = other.gameObject.GetComponent<PlayerController>();Here’s an explanation of this code:
- This instruction creates a variable that contains the reference to the Player Controller (Script) component on GameObject that collides with the collectible.
- You’ve used this code previously for health collectibles and damage zones.
4. Underneath the instruction, add an if statement with code to increment the player character health by -1 on collision:
if (player != null)
{
player.ChangeHealth(-1);
}Here’s an explanation of this code:
- Unity will execute this code if a GameObject with the PlayerCharacter script attached collides with the enemy — again, you’ve used this code previously for collectibles and damage zones.
5. Save your changes and return to the Unity Editor to test the damage functionality.
7. More things to try
If you want to further develop your skills, explore new concepts, or improve your project, check out some of the optional activities below. Each one is tagged as either Easy, Medium, or Difficult, so you can choose the level of challenge.
These activities are entirely optional, so if you’re not interested, no problem — just skip this step. We do recommend attempting at least one of them in order to get the most out of this learning experience. Good luck!
Easy: Create fast and slow enemy variants
To craft the right challenge for your players, try to create fast and slow enemy variants. Remember to carefully consider the enemies’ placement and patrol patterns.
Hard: Configure the enemy to switch between vertical and horizontal movement
Currently the enemies in your game only move horizontally or vertically, based on what axis you set them to move. To make the enemies less predictable, create script functionality that will allow the enemies to randomly change what axis they are moving along while the game is playing.
8. Check your script
If you’re completing the 2D Beginner course, take a moment to check that your script is correct before continuing.
Important: If you completed any extension work in your script, this will not be reflected in the reference script below.
EnemyController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyController : MonoBehaviour
{
// Public variables
public float speed;
public bool vertical;
public float changeTime = 3.0f;
// Private variables
Rigidbody2D rigidbody2d;
float timer;
int direction = 1;
// Start is called before the first frame update
void Start()
{
rigidbody2d = GetComponent<Rigidbody2D>();
timer = changeTime;
}
// Update is called every frame
void Update()
{
timer-= Time.deltaTime;
if (timer < 0)
{
direction = -direction;
timer = changeTime;
}
}
// FixedUpdate has the same call rate as the physics system
void FixedUpdate()
{
Vector2 position = rigidbody2d.position;
if (vertical)
{
position.y = position.y + speed * direction * Time.deltaTime;
}
else
{
position.x = position.x + speed * direction * Time.deltaTime;
}
rigidbody2d.MovePosition(position);
}
void OnTriggerEnter2D(Collider2D other)
{
PlayerController player = other.gameObject.GetComponent<PlayerController>();
if (player != null)
{
player.ChangeHealth(-1);
}
}
}9. Next steps
In this tutorial, you’ve set up an enemy to challenge the player. Next you’ll add movement animations to make that enemy (and the player character) feel more realistic.