Author: | Jan Eberle, Felix Kosian, Felix Stieglbauer, Nikola Tomic |
---|---|
Supervisor: | Prof. Gudrun Klinker |
Advisor: | Daniel Dyrda |
Submission Date: | [Date] |
Abstract
This internship dealt with the level engineering process and focused on designing, assembling, and polishing a single level of a game. The goal was to develop a snippet of a game concept that focused heavily on vertical-level design and intuitive wayfinding. While the style was lowpoly, the final level was to have the functionality and auditory and visual appearance of a finished game. To achieve this, the conceptual design of the entire game was completed before beginning the implementation of a selected portion of the game. This included a story world, design pillars, and a theme for the game. This allowed the entire development process to be planned and properly documented with participants, from concept design to feature implementation to game evaluation. Special emphasis was placed on learning how to create a quality-level design, as well as other components to create a valuable concept.
A trailer video.
Results/Implementation/Project Description
Assets / Visual Effects
Great attention was paid to the assets and visual effects. These consist of light settings, post-processing, shaders for grass and trees, as well as particle systems and fog systems.
Assets:
Great emphasis was placed specifically on the assets. For this purpose, a mood board was created at an early stage, which centers various assets next to each other, also with different styles. It was now possible to establish key assets, which serve as guiding objects. Subsequent assets, which were selected later, were oriented to these key assets. After the blockout was created, the assets were inserted into the scene. Different components for the buildings and vegetation were selected and placed. The assets were used again and again and rotated, mirrored, and distorted to bring in more variation. During this process, it was important that the blockout was not changed anymore and was finished, because the asset placement required a lot of work.
Lightsettings und Postprocessing:
The lighting mood of the scene was carefully adjusted to fit the mood, as well as increasing the light function of the enemies and the player's ability to hide. Various post-processing filters were applied. These consist of Color Adjustments, Vignette, Bloom, Ambient Occlusion, Film Grain and Depth of Field. In the following figures, the individual filters can be seen. It is always only the named filter, which is then all added together in the final result. It should also be noted that the lighting in the pictures was not turned on, so that you can see the filter and the difference better.
Before all Filters: | ||
Color Adjustment: | Vignette: | Depth of Field: |
Bloom: | Ambient Occlusion: | Film Grain: |
All Filters applied: |
Shader:
The movements of grasses and trees were created using Unity's graphical shader function. Both shader graphs work in the same way. Here the mesh is displaced over time to simulate a wind movement. To prevent the grass from moving back and forth on the ground, the displacement of the mesh depends on the y-axis of the UV map.
Particle systems:
To get more movement into the scene there are a total of 4 different particle systems. One for the glowing mushrooms, which should represent the spores. One for the torches, which was an imported asset pack. One for the candles, which consists of the same asset, but was changed into a more calm-looking particle system. And finally, a particle system, which enhances the fog and makes it appear to move. The base therefor was a standard asset from Unity. This consists of an image which is emitted in the wind direction of the fog.
Fog system:
By using the HDRP, especially the light feature of the game should be supported. The Fogsystem of the HDRP was used here. This allows to set a locales volume in specific areas to which different light settings can be applied.
Light-Baking:
For performance reasons, the light was baked into lightmaps. For this, all non-moving objects were set to static and all light sources that are not moved were set to "baked". Lights that remained on "dynamic" are mainly those of the enemies. Care must be taken here, as for large scenes light baking can take several hours if not days, depending on the hardware used. If possible, you should switch the settings to the GPU, as this can reduce the runtime considerably.
Enemy system:
Design: stationary or on predefined paths walking NPC. The player must stay out of their view. If the player is in their view, they track the player but don’t follow it. The player fails and respawns if they stay inside the enemy view too long.
Enemies are placed at positions where the player should not pass and other places where they present a puzzle for the player to not get seen by them.
Enemy - Patrols:
Defining enemy walk and look destinations is done by placing Game Objects as markers in the world. The timing is defined in a timeline, one for each enemy. Timelines can then be nested to create timed behaviours between enemies.
To simplify the workflow, the timeline with default clips is created by editor scripts, detecting all look and walk destinations automatically and linking them to the clip references.
The timeline has two custom tracks with custom clips. The length of the clips has to be set manually but can be changed while at runtime. (The reason for this is that the navigation calculation is not possible while in editor, so the walk clip lengths have to be tested and set by hand)
Enemy - Player detection:
The decision if the player is visible to an enemy is made in 3 steps:
- Is the player in the view prism? This is done by simple trigger checks
- Is the player in line of sight? This is done by raycasting
- Is the light level on the player high enough to be detected?
If all are true, the player is detected and has up to 6 seconds (depending on the distance to the enemy) to hide again.
Light level on the player calculation:
The player has a capsule only visible to 4 cameras (which render into a 4x128 each) and a volume with the exposure mode set to “fixed”.
By Async GPU Readback Requests, the render textures are copied to a buffer to then compute the average light level. (This results in a few frames delay but the async approach is worth the performance gains. Another optimization would be to compute the light level on the GPU directly)
This light level is then compared to a threshold to determine if the player is visible.
Character Controller
Reactive Implementation
For our player interaction, we were using a basic reactive first-person character controller developed with UniRX. Based on it, we adapted the implementation to our scenario and tailored it to the possible interactions. For example, the character can interact with virtually any object which implements the InteractiveObject interface by looking at it and (if necessary) pressing the interaction key (F-key).
Source: Daniel Dyrda
Ladders
To give the level additional verticality, we added a climbing functionality to the character controller. Even though it is in itself adaptable, it is currently only implemented for ladders. These ladders are distributed over various locations in the level and adjustable in terms of length, number of rungs and exit direction. The player can then interactively move up and down the ladder, and exits the ladder automatically when reaching either of the ends. The ladder's properties can in theory be easily adapted by the level designers without any knowledge of the code by placing the gizmo transforms accordingly. However, since the character is still controlled by Unity's CharacterController component, its collider is still active. This can cause issues, as it could cause nearby external colliders to block the player's movement along the ladder, therefore blocking progress, or, even worse, the ladder's exit. To avoid fine tuning and testing every single ladder several times, this had to be solved via code. The eventual workaround was to simply reposition the character controller's collider far above the level while the player interacted with the ladder. This, of course, can cause additional unwanted behavior, however solved the issue at that moment. To get rid of the problem once and for all, the ladder interaction would have to be reworked by not executing the movement via the player's internal movement update (which is executed over the character controller) but rather via its own movement method, during which the character controller is deactivated.
The above image shows a ladder prefab added into the scene. The arrows at the top and bottom define the respective exit position and direction. When leaving the ladder (by reaching one of its ends) the player is animated towards the exit position and its viewing direction progressively restricted to the target exit direction. This makes the player automatically face into the correct direction for the next part of the level and gives a clear signal to the player when the ladder interaction is ended. The white line between both arrows, parallel to the ladder defines the two points between which the player moves. This makes it easy to replace the ladder model and adjust the climbing angle / length / offset.
Jump Function
In order to create a familiar experience for players with prior experience in first-person games, we decided to include a jump functionality. Even though this feature is rather cosmetic than fundamental for the gameplay - resulting in a very low jump height - we tried to improve the jump experience. In order to achieve this, we implemented a custom function, controlling the jump's height over the curse of its duration. This allowed the player to remain longer at a high altitude, giving more time to control the jump action and reduced the time between the highest point and landing. This jump function was defined by combining two functions:
Level Design
First Iteration:
During the first iteration the village was planned to be drawn out over a large area. The player would have to find the way to the castle using a tower from of it as a wayfinding cue.
This was deemed unsuitable as most of the playing time would be used on traveling to and from different locations.
Jan proposed a different concept with a much smaller playing area. With this concept more verticallity could be added to buildings and in turn more "mini games" where the player has to figure out the path to the goal.
(The castle is missing in this Image)
This is the blockout with the new concept. In the right image the path is colored in with yellow paint. The player starts in the upper right corner and makes it´s way onto the buildings.
Possible enemy locations are marked with red cubes. The guards in the middle are there to block of the direct path to the castle.
We widened the area for the graveyard (upper right corner) to be able to make this area into a minigame where the player has to sneak around some enemys.
After this blockout was tested we divided the map into multiple prefabs to be able to work on different parts of the map at the same time.
We moved the start of the path out of the village into a forest and the player has to walk trough a small sewer like tunnel to get inside.
The climbup onto the buildings changed as the player doesnt go to the front of the buildings but instead walks between the buildings to get to the front where a ladder is placed. A guard walks around here and the player has to be careful not to be seen.
After that the path is fairly straight forward across the buildings, a fake path leads to the left while the right path that goes up and around the enemy tower.
The player walks through a tunnel with different light sources from vegetation. After that the player walks down on the side of the building to get into a tower at the front gate of the village.
In the tower a stair path leads to the top where the player has to walk around the tower and across a wooden plank path. At the end a ladder is placed so the player can climb up and walk across a beam above the gate to get to the tower on the other side. An enemy is wating the beam from behind the player.
After the player gets down on the other side a short path leads to the graveyard. Here a lot of enemys are placed in risky positions so the player has to sneak around them to get to the church where a path leads upwards on the side of the building. Behind the church are two paths again. The path to the right leads around an enemy tower where the player can look at the bridge going to the castle. The path going straight leads to a ladder on the side of the building where the player can get to ground level again.
insert images.
In the last part of the level the player has to walk to the left side of the bridge where a path leads down. A path of wodden planks that hangs on the underside of the bridge leads around one of the pilars to a secret path in the mountain where our level ends.
The whole village is enclosed by mountains so the player cant escape.
Checkpoint System
The checkpoint system works by saving a position of the player at critical points in the level. A CheckpointManager saves the position of the player when it senses a collision with the player. If the player dies the last position is requested from the CheckpointManger to reset it. For additional debug purposes the user can cycle through the checkpoints with F1 and F2.
(example collider)
Conclusion
[ PDF (optional) ]
[ Slides Kickoff/Final (optional)]