Table of Contents
Let’s continue our journey in creating a 2D roguelike in Unity. In the first part, we created the project, set up the graphics, and implemented the player movement. Now it’s time to bring the dungeon to life: we’ll add enemies, program a basic AI to chase the player, and implement a simple combat system.
Remember, all the source code is available in the official 2DRogueTest GitHub repository, which we strongly recommend checking to ensure you're viewing the latest version of the code!
-
- <li">
1. Adding Enemies
The project already includes the base enemy prefab: /Prefabs/Enemy.prefab
. This GameObject includes:
- a
SpriteRenderer
with theenemy.png
sprite - a
Rigidbody2D
(Body Type: Dynamic) - a
BoxCollider2D
- the
EnemyController.cs
script found in/Scripts/EnemyController.cs
The enemy prefab can be instantiated manually in the scene or via script.
2. Moving Toward the Player (Basic AI)
The EnemyController.cs
script contains a simple yet effective logic to chase the player:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class EnemyController : MonoBehaviour { public float speed = 3.0f; private Rigidbody2D rb; private Transform player; void Start() { rb = GetComponent<Rigidbody2D>(); player = GameObject.FindGameObjectWithTag("Player").transform; } void FixedUpdate() { if (player != null) { Vector2 direction = (player.position - transform.position).normalized; rb.MovePosition(rb.position + direction * speed * Time.fixedDeltaTime); } } } |
This simple AI moves each enemy toward the player at a constant speed. Make sure the player GameObject has the Player
tag set.
3. Combat System
To add a health and death system, each entity (player and enemies) must have hit points. The project includes a reusable script called EntityStats.cs
in /Scripts/EntityStats.cs
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class EntityStats : MonoBehaviour { public int maxHP = 3; public int currentHP; void Awake() { currentHP = maxHP; } public void TakeDamage(int amount) { currentHP -= amount; if (currentHP <= 0) { Die(); } } protected virtual void Die() { Destroy(gameObject); } } |
The PlayerController.cs
script has been updated to allow attacking enemies when the Space key is pressed. In the Update()
method, you’ll find:
1 2 3 4 |
if (Input.GetKeyDown(KeyCode.Space)) { TryAttack(); } |
And the TryAttack()
method is defined as follows:
1 2 3 4 5 6 7 8 9 10 11 |
void TryAttack() { Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, 0.5f); foreach (var hit in hits) { if (hit.CompareTag("Enemy")) { hit.GetComponent<EntityStats>()?.TakeDamage(1); } } } |
This basic area attack hits all enemies within a 0.5 unit radius.
Note:
- Enemies must have the
Enemy
tag. - The
EntityStats
script must be added to both the player and the enemies.
4. Damage System for the Player
Enemies can deal damage too. In the EnemyController.cs
script, you can add this method:
1 2 3 4 5 6 7 |
void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.CompareTag("Player")) { collision.gameObject.GetComponent<EntityStats>()?.TakeDamage(1); } } |
This way, every time an enemy touches the player, it deals 1 damage.
You can enhance this system with cooldowns between attacks, animations, and visual feedback.
5. Visual Feedback and Polish
The project includes:
- animations for enemies and player in
/Animations
- placeholder sound effects
- basic logic to flash enemies when they take damage
All of these are optional but enhance the player experience.
Conclusion
In this second part, we’ve brought the dungeon to life: enemies now chase the player, attack, and can be defeated. Everything is handled with simple and modular scripts, all included in the 2DRogueTest repository.
In the third and final part, we’ll see how to automatically generate rooms, link them together, and make every run unique.