Vestige is a first-person puzzle game which takes place in an alternate history, beneath the capital of a renewed and oppresive Roman empire. Cast into an abandoned subway, the player must traverse the dangerously repurposed tunnels, using a decoy device to thwart the traps and security systems that lie within.
As level designer and programmer for a core team of nine, it was my responsibility to make sure that everything in the game worked and worked well. In addition to implementing all necessary functionality in UnrealScript, I blocked out the level, implemented each puzzle and event, and coordinated level checkout so that other members of the team could cooperatively and seamlessly work on lighting and decoration.
Vestige received first prize at the 2011 SCAD Entelechy exhibition for Best Digital Game Prototype and Best Environment Art.
For download links and more information about the project and the team, see the game's official site.
Vestige was a group project executed over the course of 20 weeks.
My primary responsibilities were:
The following areas were handled by other parts of the team:
The core mechanic of Vestige is a decoy device, and the idea behind it is that the environment is constantly shifting and moving in order to thwart the player. This sort of experience is within the scope of UDK's existing functionality, but in order for the level to respond to the player robustly and to fully involve the decoy mechanic, we needed a specialized system. After we had settled on the idea of a decoy, I quickly set to work devising a way to implement this mechanic as a simple set of level design tools. My solution was a position-sensitive trigger: a volume to track the player's progress through a particular area and use that information to drive events in the level.
I tried to design the system in such a way that it would be extensible, to allow the progress of the tracked player to be used in more ways than those initially foreseen. In order to aid this future expansion, and for general organization, the system is clearly decomposed. The trigger object handles acquiring an appropriate target and tracking its progress. Its responsibility ends each frame when it notifies relevant actors of the tracked progress as a value between 0.0 and 1.0.
A number of properties allow the designer to control how the target's position is interpreted. Each volume is concerned with a single axis, which can be changed via the TrackingAxis property. StartTracking and StopTracking properties move the minimum and maximum edges inward, effectively creating a buffer zone beyond which the trigger always registers a value of 0.0 (minimum) or 1.0 (maximum). Somewhat similarly, the ExitBehavior properties specify how the trigger should respond when the target exits the volume.
All of these modifications to the tracked progress of the target take place in as simple and self-contained a way as possible, with the volume always reporting a value between 0.0 and 1.0. In the example above, this value is used to drive the position of simple interpolated actors which automatically move toward a specified offset based on reported progress. However, the system is designed in such a way that new classes can easily be created and made to respond to this tracking data.
The following sequence shows a puzzle in which the player must drop two batteries simultaneously into two different battery sockets. Each socket is covered by a layer of floating floor tiles which become active in the presence of the player (or the decoy). The player can drop a battery remotely by triggering the floor tiles with a decoy, placing a decoy on the tiles, then retreating and recalling the decoy to deactivate the tiles, dropping the battery to the socket below. All of this functionality is inherent in the custom classes created for the floor tiles, the battery, and the socket.
Thanks to an interface which exposes events from these objects, Kismet can be used to implement the puzzle in a fairly streamlined way. Each battery event is attached to a floor socket. When batteries are added or removed from a socket, a counter is modified and the appropriate events are called. When the first battery is added, a timer begins to count down. If the timer finishes without a second battery being inserted, the first battery is moved offscreen, where it is added to a list of inactive batteries. When the player no longer has enough batteries to complete the puzzle, these discarded batteries can be moved back into the room as if spawned anew.
This puzzle went through a great many revisions, and this is one case where the involvement of additional narrative events significantly improved the way that the accompanying puzzle played. In the process of revising the script, the writers weaved in an encounter with the protagonist's mentor, from whom she is separated at the beginning of the level. In addition to helping the player establish a rapport with this important character, his presence improved the puzzle in a number of ways. For one, it allowed us to justify the fact that batteries were being infinitely respawned from nowhere by establishing through the script that the character was in an adjacent room which was filled with batteries. The active participation of both characters also allowed us to incorporate hints into the dialogue, easing the player's understanding of what might have otherwise been a difficult puzzle to understand.