STRATEGY
Next Steps:
Add a final boss (e.g., Necromancer) guarding the dungeon exit.
Introduce environmental hazards (spike traps, collapsing floors).
Escape the dungeon or perish trying.
You awaken in a dark, labyrinthine dungeon filled with hostile creatures. Your goal is to navigate procedurally generated levels, defeat enemies, and unlock gates using keys found in chests. Each level becomes progressively harder, culminating in a final escape.
Combat & Exploration: Fight skeletons (melee sword attacks) and spiders (ranged web projectiles) while scavenging chests for keys (to unlock gates) and gold (to upgrade weapons at hidden blacksmith stations).
Progression: Unlock new areas by finding keys, and use gold to enhance weapon damage, fire rate, or unlock special abilities (e.g., explosive rounds).
Hidden Secrets: Some chests contain rare items like health potions, temporary shields, or maps revealing hidden paths.
Skeleton Soldiers: Slow but deadly in close combat. They patrol corridors and swarm the player.
Spiders: Agile, ranged foes that shoot webs to slow your movement. Often cling to ceilings or hide in shadows.
Start
Pro Builder
Using ProBuilder in Unity, I began making the layout of the world in my game. It being a dungeon and wanting to make it simple, I made cubes throughout the map and inverted them and joined them together.
I added chests and gates across the map, The player would have to interact with these chests to open the gates. The chest also has ammo and health.
The chest opens with a chest script, which works with a box collider set around the vicinity of the chest. The script will use the collider to check whether a component with the tag “Player” has entered the collider using void onTriggerEnter.
It also checks on trigger exit as a fail-safe to avoid the chest opening without the player being inside the collider
In these scripts, we can see how the code is connected to all create a seamless function where the player opens the chest, then receives a key which enables them to open a gate
Each chest has a different key type, which can be assigned to the inspector. These keys would be updated in the player’s inventory.
The chest and gate also play an audio clip when it is interacted with, as well as an animation.
In the start of the game your inventory is empty so all the UI icons are hidden from the screen each chest would have its specific key for specific gate in order to progress.
I done this by creraitng an Dictionary which held the values for all the keys, and when the item is collected it adds the quatity of the item to the list which displays on the UI.
It would also remove items from the dictioanary when used and not display it on the UI
The PlayerHealth script oversees the player’s health. The script keeps track of health as a numerical value ranging from 0 (indicating death) to maxHealth (set at a default of 100). When the player suffers damage, the health value decreases.
This script employs two overlapping UI bars (front and back) to visually represent health. As health diminishes, the front bar updates immediately while the back bar gradually “catches up” through a Lerp animation just like the stamina bars. This design produces a refined effect, ensuring the UI appears both responsive and smooth.
This is the stamina script for the player. The script works by keeping variables in floats. These variables are assigned to the player’s movement so that they can control whether the player is running or walking. This is done by determining whether key inputs are pressed and deducting from the variable floats which cause an outcome.
In my script, we have maxStamina and minStamina. When the player presses and holds the shift key while pressing the W, A, S, D, and stamina is more than > 0, they start sprinting. When stamina ≤ 0, they walk.
I also made an AI follow a patrol path.
The script creates a waypoint list and stores the transform position.
It draws lines using Gizmos, which make it so that I can always see the path the enemy takes and amend it through the editor.
This path is then called and followed in the enemy’s patrol state.
The script begins with public variables that define the firing range of the gun, the capacity of the magazine, the remaining bullets, and the current bullet count. This allows for effective tracking of ammunition and contributes to a more realistic firearm mechanic.
The Shoot Mode function allows the gun to switch between automatic and semi-automatic firing modes. This flexibility enables the inclusion of various weapon types within a single code framework. For instance, if a player is equipped with a semi-automatic pistol, which fires one bullet at a time, this enum can be activated for that specific object.
In this game, the player utilises a shotgun, making this mechanic particularly suitable. The Gun script also has a bullet effect where it plays a particle when fired and when it hits an object all these where done to create relism.
This is the gun script that the Player uses. This is an FPS game, so the gun needs to respond like it.
In the script, the game utilises a raycast that creates a straight line like a laser at the center of the screen. Using this rayCast, I become an object that can be used to detect what the line hits. For example, if the line hits a door, the line can detect it.
I utilised this so that when the rayCast hit an object with the tag enemy, it would do damage.
Patrolling Between Waypoints
The enemy follows a predetermined route outlined by the patrolPath.waypoints. Upon reaching one of these waypoints, it pauses for 5 seconds (managed by waitTimer), stops all movement, and then randomly selects a new waypoint to move towards. This cycle repeats indefinitely unless the player is detected.
Player Detection
While patrolling, the enemy constantly monitors for the player’s presence through the CanSeePlayer() function. If the player is spotted, it stops its patrol, shifts into AttackState, and begins to chase the player, akin to how pressing Shift in the stamina script triggers sprinting.
Movement and Rotation
To navigate smoothly between waypoints, the enemy utilizes NavMeshAgent. It gradually rotates to align with the next target using Quaternion.Slerp, which helps make the turns appear fluid and natural.
The enemy begins its behavior by tracking the states. In the Attack state, upon detecting the player, it initiates a chase and emits a walking sound.
Once the player enters the Attack Range, the enemy halts its pursuit and performs an attack animation while ensuring it is facing the player.
If the player exits the range, the enemy continues to pursue the player, adjusting its path as needed.
Should the enemy fail to detect the player for 8 seconds, it reverts to the patrol state, resuming its designated path.
This system controls an enemy’s basic actions—patrolling, detecting players, and attacking—using a state machine model. Similar to a stamina script that tracks running and walking, this framework adjusts patrol and attack modes based on player visibility.
Key Components:
1.Navigation & Movement
1.NavMeshAgent: Handles pathfinding, much like the stamina script adjusts speed.
2.PatrolPath: A set of waypoints the enemy follows when not attacking.
2.Player Detection Three criteria must be met for the enemy to “detect” a player:
1.Distance Check: Is the player within 20 units?
2.Field of View: Is the player within the 85-degree angle in front?
3.Line of Sight: Is there a clear line to the player?
4.Debug Rays: Red indicates detection failure; green signifies a player has been detected and the enemy shifts to AttackState.
3.State Machine
1.PatrolState: Default state moving between waypoints with walking sounds.
2.AttackState: Activated upon detecting a player, leading to pursuit and attack.