Project Zero
Introduction
Project Zero has been my most ambitious, challenging and rewarding undertaking.
In previous solo projects I've made, it has either been a level whitebox with conceptual gameplay or isolated gameplay systems without a proper level to use them in. With Project Zero I wanted to create a holistic project where many systemic design elements would work together to create a grand, epic experience.
I took on the challenge to create my own stealth game, taking inspiration from some of my favorite games in the genre but having my own take on the design and the technical implementation of features.
I created all of the systems from scratch and designed them to be scalable and iterable, meaning I could start small and extend their functionality over time.
Since it's such a big project, I've split it into different sections with sub-categories. I hope it's easy for you to find what you want to know more about.
Project details
-
Made in Unreal Engine 4.24 over 14 weeks
-
Heavily inspired by MGSV: Ground Zeroes
-
Models made in Maya
-
UI assets made in Photoshop
-
Audio cut and mixed in Audacity
What did I not create?
-
Textures & Shaders - Quixel Megascans
-
Weapon Models - Deadghost Interactive
-
Environment Models - Dekogon Studios & Quixel
-
Particle Systems - Various UE4 Asset Packs
-
Sound Effects - GDC Audio Bundles
-
Music - Fesliyan Studios
-
Animations - UE4 Animation Starter Pack
Stealth Design
General philosophy
What makes a great stealth experience?
I asked myself this question when getting started on Project Zero. Spy stories have always intrigued me and the feeling of navigating unnoticed behind enemy lines always makes me feel like a boss. But why?
To delve into this, I did extensive research on some of my favorite stealth game series and found some common ideas.
Being on the edge of detection in a stealth game should feel thrilling and the outcome of every tense situation should give a feeling of relief and accomplishment.
Enemy behavior should be fairly predictable, letting the player outsmart them by planning and executing their approach in the most effective and creative way they can think of. I wanted a stealth experience where the player feels like they are in total control of their actions, and that punishing consequences occur because of their mistakes.
This is called player agency and was one of the most important core design elements weaved into my design.
Player Cover System
The player cover system is one of the core features that could make or break the experience.
My goal was to let the player get behind virtually any object in the game and be covered from both bullets and detection, and therefore cater to both a combat and stealth approach. Utilizing covers reduces the fatigue of the constant threat of detection, and offers a high prospect and refuge in contrast to the otherwise open and exposed areas.
What I wanted to achieve with the cover system:
-
An opportunity for the player to take a second, breathe, and plan their next move
-
Protection from exposure in both combat and stealth scenarios
-
The player can easily transition between covers, keeping flow in focus
The result
First iteration of the cover system
The first iteration was inspired by Deus Ex: Mankind Divided, which has a tight cover system that locks the player into a "lane" along the cover surface and lets the player exit or transition between covers by pressing an action button.
The benefit with this style of cover system was that it would allow players to feel safe knowing they couldn't accidentally step out of cover by overshooting it.
The downside was that it crippled the fluid movement I was aiming for, and would probably be better suited for a level design with tighter cover placement.
It behaved nicely, but didn't feel quite right.
On the technical side, boiled down, it works like this:
-
A collision sphere around the player checks for cover objects
-
Traces on each side of the player check if the surface is flat and wide enough
-
Locomotion changes and movement now follows the surface normal
-
A sphere trace checks for corners to trigger the peek animation
-
The same trace keeps the player covered while passing small gaps
The final iteration of the cover system is more dynamic in the sense that it lets the player move around freely, gently nudging them into the cover's lane when close enough to a cover surface.
This made the system feel more fluid and more intimately connected with the player's movement.
Final iteration of the cover system
While in cover, the player is safe from detection
within a specified angle.
When aiming a weapon from a corner or low cover, the
player pawn will automatically peek out.
Cover Placement
When placing cover assets in the world, I encountered some issues. Some shapes were more complex than others and would return false info about the surface to the cover handler. A small bump in a surface could say "hey, this is a corner" and result in unpredictable behavior.
To counter this problem, I created a dedicated cover actor and scripted it to get the bounds of any chosen mesh and create an additional custom collision box on a separate channel that only reacts with the player's cover checks.
This way, projectiles and visibility checks would still react with the actual surface of the object, while the player pawn would move along the custom collision box.
Using a "Low Cover Threshold" value, the script automatically measures the height of the object and flags it as low cover when appropriate.
Placing covers in a scene
Covers in a level
Distracting enemies
While playtesting, some players got anxious or frustrated with always having to engage certain enemies in order to get past them. I took this into consideration and added two different ways to distract an enemy:
Whistling
If close enough, whistling will alert an enemy and cause them to investigate the player's location. This is a good way to either make them leave their post, or separate them from other enemies' sight lines and silently take them out.
Throwing Magazines
Empty Magazines can be picked up after reloading or found in the world, and can be thrown from a distance to lure enemies away.
Empty magazines are dropped when reloading
Throwing a magazine to lure an enemy away
Whistling to draw an enemy in
Interrogation
When sneaking up on an enemy, the player can catch them by surprise and hold them at gunpoint. The player will then interrogate the enemy who can reveal information such as level hints or key codes to secret areas.
If left unguarded, the enemy will try to reach for their weapon and and trigger an alarm.
Lone enemies can be caught off guard
Information is assigned to enemies through volumes in the world. Each volume contains a set of information and lets enemies inside the volume know what information to reveal. Once a piece has been revealed, it's removed from the volume.
Information is stored in data tables.
Non-lethal Takedowns & Hiding Bodies
A non-lethal approach to engaging enemies is to knock them out by rolling into them. This will put the enemy in an 'unconscious' state and incapacitate them for a while.
A few points about how it works:
-
If the enemy hasn't seen you, they will be knocked out instantly. They will not trigger an alarm.
-
If the enemy has seen you, they require two hits to be knocked out. They will trigger an alarm when waking up.
-
If the enemy is in combat, they can still be knocked over, but cannot be knocked out.
Once an enemy is unconscious or dead, other enemies can detect their body and trigger an alarm. To avoid this, the player can grab a limb and hide the body.
This is a feature I didn't get to spend enough time to polish. Since the physics can be unpredictable, bodies can sometimes get stuck or become unresponsive.
If at first you don't succeed, try again
Bodies can be hidden behind objects or in bushes
Stealth Feedback & Reflex Mode
To let the player know when they are about to be detected, there are a few indications to look out for. There are three layers of feedback given to the player before they are fully detected and combat is initiated:
1. When an enemy has a line of sight towards the player but hasn't seen them yet, a radial indicator pops up on the HUD showing the direction of the enemy. This lets the player quickly hide before the enemy reacts.
2. When an enemy is alerted because they see or hear something they react with a voice line and turn on their flashlight, letting the player know that they need to move to another location to avoid being detected.
3. When an enemy has a clear sight of the player and is about to enter combat, time is slowed down for a moment, giving the player a final chance to take them out before they trigger an alarm and combat starts.
Reflex mode in my reference game
Reflex mode in Project Zero
Binoculars & Markers
Part of the game loop is to scout an area and plan your approach. To that end, I added binoculars that are always in the player's inventory and can be used to spot enemies or landmarks, as well as place markers.
When looking straight at an enemy or a level objective, a marker is automatically placed and will stay attached to them for a few minutes.
Markers can also be placed manually by clicking, letting players create their own objective in the level.
Placing markers in the level
Combat Design
General Philosophy
Weapon and combat design was a fun challenge for this project. Since the stealth design became so prominent, I wanted combat to balance out and give variation to the gameplay to suit different play styles.
I did a lot of research on real-life weapon mechanics and took inspiration from some of my favorite shooters. I realized early on that I wanted weapons to feel realistic and powerful, and that engaging in combat should be intense and unforgiving.
I wanted each shot to feel satisfying, and each hit from an enemy projectile to feel lethal.
A big chunk of my time went into designing feedback elements and balancing the combat to make sure it was challenging, fun and rewarding.
Weapons & Ballistics
I wanted to add different weapon types that are useful in different situations. There are four types of weapons in the game: Pistols, shotguns, rifles and grenades.
Pistols deal the least damage, but aren't as loud as other weapons. It's a starter weapon that can be equipped with a silencer.
The stealthy choice.
Shotguns are very loud and slow, but kill with one shot in close range combat. It can only be equipped with close-range sights.
The adrenaline-filled choice.
Rifles are varied in their use.
The AR-4 is a tactical rifle that has a high fire rate and can be equipped with most attachments.
The KA-47 has a slower fire rate and more aggressive recoil but deals more damage. It cannot be equipped with attachments.
Shooting Range
I set up a shooting range to test and balance weapons and ballistics.
In the range I could test everything weapon-related such as:
-
Weapon Accuracy
-
Projectile speed
-
Projectile drop rates (effective distance)
-
Recoil and spread patterns
-
Fire and hit feedback
I wanted to experiment with how realistic I could make the weapons behave without sacrificing fun gameplay. I used physics-based projectiles and designed the weapon properties based on their real-life references.
Since I used projectiles, I had to account for things like the weapon's muzzle velocity and the weight of the bullet to get the drop rate just right.
After the properties were set, to make sure the bullet landed where intended I had to adjust the different sights to a specific range.
This process is called 'sighting in' or 'zeroing' and worked much like it would in real life, adjusting the sight until it had an accurate projection to that range.
Zeroing weapon sights required a lot of animation tweaking
Details matter, it's worth the time to get them right
A lot of effort went into making the weapons feel good when interacting with them.
Small details such as actually grabbing a magazine and putting it into the magazine slot to reload the weapon, or bullet casings being kicked out of the ejection port, gave the weapon feel another layer of realism.
Different weapons have different weapon handling.
Both recoil and bullet spread are factors that change how a weapon handles, and will add to the weapon's unique feeling when firing it.
I planned the varying recoil to make room for potential recoil-reducing attachments such as grips further on.
Testing recoil patterns in the shooting range
Attachments
Since I wanted each weapon to have a different feel, I also wanted some way to make them more unique and give even more player agency in the sense that players would have to choose more carefully what weapon they wanted to use, and when.
To that end, I created an attachment system. Players can pick up weapon attachments in the level and use those to customize their weapons.
This was a system I made pretty early on, and throughout the process I made sure that each attachment had a real impact on gameplay, but unfortunately I didn't have time to balance them out so in the end some of them were objectively more useful than others.
Weapons can be customized on the fly
With a keypress the player enters the inspection mode, where they can customize the weapon they are currently holding.
Different weapons have different attachment slots and some weapons allow attachments that other weapons don't.
What attachments a weapon can have equipped is defined in the weapon properties, and the slots are assigned as sockets in their skeleton.
Just like with the weapons, I built the attachment system to be scalable and easy to expand upon.
I made 4 types of attachments:
-
Muzzle
-
Sight
-
Grip
-
Light
Each attachment has an impact on gameplay, and some have both pros and cons to balance out their value.
For example, the suppressor makes you silent but has limited durability and the flashlight will light up dark areas but can be detected by enemies.
Attachment types
Camera
When I first started this project, I made it in a true first person perspective, where the player had a fully animated body but the camera was attached to the head. Later on I switched over to a third person camera, but wanted to keep the FPP as an option.
This was an exciting challenge for me, because when designing weapons, camera and the Ievel I had to keep both camera perspectives in mind.
As for the camera mechanics, my goal was to use the camera as a tool to make the player feel certain ways depending on what they were doing, without making it feel intrusive.
To achieve that, I wanted to make a very dynamic camera with a combination of subtle shifts in movement, dynamic field of view, post process effects and camera shake.
Camera moves and zooms dynamically
The camera is attached to the player on a spring arm that has a very slight camera lag, making it smoothly adjust its position by "lagging" after the player depending on how they move.
Likewise, the spring arm smoothly retracts and extends to different lengths in combination with field of view depending on what the player is doing.
I used the two together to enhance the game feel while playing.
Combat Feedback
Feedback is what gives the combat that extra juice. To make the second-to-second loop of combat really fun, I added layers of feedback elements and started polishing them pretty early on.
My main priority was to let the player know when they have landed a shot and make the hit feel good, and that taking too much damage should feel intimidating.
Camera and sound play a big part in damage feedback
When the player takes damage, the damage feedback grows exponentially the lower health they have left:
-
Blood vignettes are added and grow larger
-
A heartbeat sound loops that increases in volume and rate
-
All other sound effects have a low-pass filter added, making them sound more and more muffled
-
Chromatic aberration and blur effects increase
When shooting, a few different feedback elements are given to the player.
-
Unique hit sounds effects, particles and bullet hole decals on different surfaces
-
Blood splatter decals behind enemies when hit
-
Crosshair is animated and gives hit markers
-
Damage amount is shown on each hit
Each bullet hit should make the player feel its impact
Locomotion
Both the player character and enemies have a lot of animations. To make them more manageable, I added scripted locomotions where animations transitioned smoothly based on the character's state.
I used Unreal's basic animation kit to get started, and found a nice workflow where I could create representative poses out of existing animations and blend them in different ways to achieve the result that I wanted.
When adding new features that required unique animations, I duplicated existing animation assets and manipulated bones in the skeleton to create a pose.
The pose was then added to the locomotion and could be blended with other animations for different results.
For instance, all running animations share the same base animation, but have a different upper body animation depending on what weapon is equipped.
A rifle pose is blended with a base run animation
Idle state blended by which weapon is equipped
Existing assets were used to create new animations
AI
AI Behavior Tree
I wanted the AI to be able to make intelligent decisions based on their situation and environment, while still being fairly predictable to let players outsmart them.
I split the AI framework into different behaviors and levels of intelligence using a state machine, where each state would change the way they behave and react depending on different conditions.
Idle
Enemy's passive behavior. They walk slowly, typically following patrol paths. The most predictable state.
Alerted
Enemy noticed something. They move faster and investigate the source of stimuli. Somewhat predictable movement.
Searching
Enemy is aware of the player, moves fast and uses unpredictable search patterns.
Combat
Enemy has spotted player and makes unpredictable decisions where to move, prioritizing cover and trying to flank the player.
Behavior Tree Breakdown
Perception & Sight Forgiveness
I used UE4's built-in perception system as a base to get started with. After setting it up, the AI could detect the player through either sight, hearing or taking damage.
My main focus with the enemy's perception was to make sure their reactions were believable, logical and readable.
My goal:
-
Enemies can differentiate sounds and react accordingly
-
Enemies take longer to react to different impulses depending on the situation
-
Enemies will trigger an alarm upon spotting the player
To give the enemies a bit of a natural reaction time, I introduced two dynamic timers that I call 'sight forgiveness' and 'detection forgiveness'. The first is a slight reaction time before being alerted when first spotting the player. The latter is the reaction time after being alerted, before confirming it's the player and entering combat.
Testing bone detection
One of the drawbacks with UE4's built-in perception system is that the sight traces will only target an object's center point, which for the a character is around the stomach. As long as that point is covered, the rest of the body is invisible.
What I had in mind was that the player should have different levels of exposure depending on how much of their body is visible.
With the help of a few articles on the subject and a little bit of C++, I modified the player character class to return certain bones in its skeleton upon detection.
Each bone specified has a strength value that's returned to the AI actor. That value, combined with the distance, is then used to reduce or increase an enemy's reaction time.
This means that if the whole player is visible an enemy can react in a split second, while if only the head is sticking out it will take them a bit longer.
Sight 'strength' per bone
Testing cover metrics with the new AI perception
Test gym for cover metrics
Generated cover positions
For all condition-based decisions I used the Unreal's experimental Environment Query System, or 'EQS'.
Environment queries allow the AI to run a series of tests to find the best suited location for a certain result.
By using EQS I could generate random search patterns, cover locations and flanking directions.
This made the AI very dynamic in the way they behaved in any given situation.
AI Cover System
When entering combat, the enemy will try to take cover from the player.
In the first iteration, I placed dedicated cover points in the world with a scripted 'booking system' that allowed the enemies to occupy a spot that had the player in sight and range.
This worked fine for the most part, but it was quite tedious to manually place cover points, and very difficult to make it work in every situation. Unless I placed a ton of these points, the enemies moved in a more predictable pattern, which felt pretty contrived.
Like with my other systems, I wanted the AI cover logic to be fluid and immersive.
EQS tests to find a cover position
Some enemies stay in front of the player, some flank around
Patrol paths
Most enemies typically move along designed patrol paths. These are pretty straightforward; a script is placed in the world and consists of a list of locations in a closed loop. The path is visualized in the editor, showing how much ground they cover.
In the enemy properties a patrol path can then be assigned to them. Initially, they will go to the nearest point in the path and then they will loop through the locations defined in the script.
Patrol paths give the enemies a predictable movement pattern that the player can observe. The paths are versatile in the way that their locations can be manipulated during runtime, and enemies can switch paths depending on in-game events.
Enemies use pathfinding to move between points.
Enemies take a break between patrol points and sometimes
play a random idle animation.
Level Design
General Philosophy
To support my game loop, I wanted somewhat short and compact levels where the player could roam around and find their way towards a single objective. Since I went for a multi-path approach, I didn't want to overwhelm the player when introducing a new area.
I use a strong landmark to make it obvious the player where they should end up, and then set them free to make their own path by either utilizing placed covers or blasting their way through.
I didn't have as much time as I wanted to work on the level layout, but I did my best to get the general idea across.
Game loop
Basic overview
Examples of covers and patrol paths in the camp
A strong landmark that the player can navigate towards
Kitbashing & Metrics
The environment assets consist of a kit put together from different marketplace packs, Quixel assets and my own models. I created all of the structures and a modular building kit while the landscape assets, props and cover assets were from other content creators.
When putting the kit together, my first priority was getting the metrics right. Some assets were made with different scales in mind, so I had to unify them to make them both look right and serve a gameplay purpose with my scale and metrics.
I made an asset gym to keep track of my assets and see how they looked relative to each other.
I also used this scene to test lighting, material properties and weather effects.
Asset gym
Cover metrics
When selecting assets for my kit, I tested them with different gameplay mechanics. I measured how high a cover needs to be when crounching and standing, as well as how well the AI would utilize them as cover objects.
I defined a buffer zone to give a clear message to the player what is and what is not a cover object. Any hard-surface objects within this range would deny affordance and could confuse the player when navigating the level.
Pickups & Doors
The player can interact with different objects in the level. I wanted to encourage both exploration and problem solving, and did so by adding loot stashes and locked doors.
There are two armories in the level that are protected by doors with keypads. These are tough to get into, because the player first needs to acquire the access code by interrogating enemies, and then get to the door which is guarded by patrolling enemies.
Secret stashes can be found scattered throughout the level and may contain weapons, ammo or weapon attachments.
The quality and quantity of the loot depends on how easy it is to find, and how many enemies are patrolling the area.
Secret stashes are usually found in dark corners
Some doors can be lockpicked
Others need an access code
Triggered Events
There are triggers that activate events in the level when the player enters them. I wanted to have a few scripted events to
add immersion, and to help the player learn the controls with tutorial triggers.
I set up trigger volumes that could be placed anywhere in the level and be configured to play any sequence with different settings such as locking player input or triggering another script when done playing.
Context based tutorials are shown when appropriate
Vista event that introduces the area
Dynamic Weather
This started off as a small side-project where I wanted to try making a somewhat advanced weather locomotion that affected the world in different ways. I wanted a robust and scalable system that could seamlessly change the atmosphere of a level without changing the environment assets.
I realized that I could use this to support events in my game by, for instance, revealing a new location after a thick snow storm.
Weather can be used to conceal or reveal parts of the level
A weather manager keeps track of current weather properties and handles a scheduling system where triggered weather changes are applied in a queue.
When adding the weather manager to a level, I could define a pool of what types of weather that level supports. From this pool, the manager can randomize which weather it should transition to next if nothing else has been added to the queue by a trigger volume.
When a weather change is triggered, a few different values are changed over time:
-
Wind strength and direction (affects cloth)
-
Environment light intensity
-
Fog density and color
-
Weather effect particle count and direction
-
Foliage sway intensity
Variables are also sent to a global material parameter collection, that defines whether environment surfaces should look wet, or be covered with a layer of snow.
With these changes, not only does the weather effects change, but the world changes with it.
Dynamic weather changes the environment
Final Thoughts
I had a blast working on Project Zero. By developing a project of this scope I explored, experimented and learned so much about tackling problems that I wouldn't have to deal with in a smaller piece.
On the technical side I had to plan a structure and set up a robust base that would be able to support all the systems I wanted to make. From a design perspective, I was able to take a number of features from concept to implementation and support/re-balance them when adding other elements to the mix.
The downside was that my scope went through the roof and above. There were so many features and technical challenges that I wanted to take on that I didn't have as much time as I would have wanted to actually level design the experience (even if level design wasn't the primary focus in this portfolio piece.)
I hope you didn't get too bored scrolling through the content. If there's anything specific you would like to know more about or just want to have a chat, just throw me a message!