Back to Blog

Damned Veil: How I Automated Enemy Spawning Using PCG

Introduction

Let me tell you about Damned Veil. It’s a 2D horror, strategy, and shooter game I’ve been working on. The core mechanic is simple but tricky: you have to defeat all the enemies in an arena using a single projectile that bounces off the walls. You have to find the absolute perfect shooting angle so the projectile ricochets and hits multiple targets in one go.

Sounds fun for the player, right? Well, for the developer (me), it was an absolute nightmare to design.

The Problem: Manual Placement is a Trap

When I started developing the levels, I placed the enemies manually.

Here is what the workflow looked like: Place an enemy -> guess the bounce angle -> playtest -> realize the angle is off by 2 degrees and the enemy is unreachable -> adjust -> playtest again -> cry a little -> repeat.

Manual placement was incredibly inefficient. It took way too long to ensure a level was actually solvable, and the resulting levels felt repetitive. (Seriously, I was losing my mind doing trial-and-error physics simulations in my head).

I couldn’t just use pure random placement either, because spawning an enemy in a spot that the projectile mathematically cannot reach would completely break the game. I needed an automated system that was random enough to be fresh, but smart enough to guarantee the level was playable.

The Solution: Procedural Content Generation (PCG)

To fix this, I decided to build a Procedural Content Generation (PCG) system in Unity using C#. But not just any random spawner, I combined two specific algorithmic approaches: Specular Path and Constraint Satisfaction Problem (CSP).

Here is how the magic works:

  • Specular Path: Before spawning anyone, the algorithm calculates the exact trajectory of the projectile based on the player’s position and the arena’s walls. It essentially draws the invisible “bounce lines” across the map. These lines become our candidate spawning spots.
  • Constraint Satisfaction Problem (CSP): This acts as the strict bouncer/validator. It takes those candidate spots and filters them through strict game rules. Are the enemies too close to each other? Are they spawning inside the player’s safe zone? Are they actually on the bounce path? If a position passes the CSP checks, an enemy is spawned!
  • why Specular Path? Because in a game strictly governed by ricochet physics, the path of the bullet is the only thing that matters. You have to design the level around the bullet, not the bullet around the level.
  • why CSP? Because pure randomness is chaotic. CSP ensures the game remains balanced, fair, and mathematically solvable (most of PCG use constraint algorithm, so do i).

The Results & Performance

I wasn’t just going to guess if this system was good, I tested it across 15 different maps (5 small, 5 medium, and 5 large arenas) with a total of 149 trials.

Here are the numbers:

  • Speed: The algorithm took an average of 7.616 ms to execute. It generates the entire enemy layout without much delay.
  • Reliability: It achieved a 92% reliability rate (137 out of 149 configurations were mathematically valid and playable).
  • Expressivity: Out of those 137 valid layouts, 135 were completely unique. That’s a 99% expressivity rate, meaning the game basically never gets boring or repetitive.

Did the Players Actually Like It?

Metrics are cool, but player experience is what actually matters. I conducted a beta test and evaluated it using the Player Experience Inventory (PXI).

The results were incredibly positive. The Functional Consequences (how clear and playable the game feels) scored a 5.773, and the Psychosocial Consequences (how fun, challenging, and engaging it feels) scored a 5.762. Both of these fall into the “High” category!

This proved that using procedural generation didn’t ruin the handcrafted feel of the game. It maintained player curiosity, strategic freedom, and the perfect level of challenge.

What I Learned & What’s Next

Building this PCG system taught me a massive lesson: don’t brute-force game design if math can do it better.

While the system works beautifully, it currently has some limitations. It only handles 2D static players and static enemies.


Thanks for reading!