Lost Pages is a VR sandbox-puzzler placed in a world made entirely out of paper. To find the missing scrap sprites the player must cut, glue and draw their way through a thin world.
During their adventures the player will not travel alone though. They will be accompanied by all the scraps that have already been found. They will hold the player's tools, make sure nothing of value gets lost and are all around a lively bunch.
Development time: 3 months
Roles: Programming, level layout, lighting, audio coordination
The glue and the eraser were the easiest of the tools. These two also were the features that differed the most from their initial sketch (although it was still pretty close).
The glue applies small glue blobs to surfaces which connect two actors together when they come in contact with each other. This is done using a simple physics constraint between the two. When glued together actors do not collide with each other anymore. This is to reduce jittering when flailing around two glued actors with your hands. We encountered no downsides to this method so I just stuck with it.
Also every glue connection beyond the first one does not actually constrain the two actors. We had many problems when trying to glue two actors together multiple times and ultimately it was for the better that I only made the first connection valid. We have only a handful of concave objects where this could theoretically become apparent but so far no one even noticed it and it 'solved' a big problem rather smoothly.
The aspect where the final glue differed from its initial sketch was the way it is applied to surfaces. Initially we planned the stick to draw on surfaces much like the brush so that a line of glue would be formed on the mesh. For demonstration purposes I implemented a simpler apply mechanic which simply spawned the aforementioned blob not a glue line. By the time I finished the brush mechanic though both the team and our testers (which we swapped out regularly) grew so accustomed to the glue blob that we simply kept it in. A nice bonus of this method is that with this all tools differ at least a bit in their application. The brush has to be moved, the glue has to be tapped, the eraser needs to touch and the scissor needs to be let go of.
The eraser was similar to the brush in how it was used. You pressed it against a surface and moved it around. This was fine in theory and in our earlier prototype but later on it became apparent that it was actually hindering gameplay a lot. Players would crawl on the floor slowly erasing each stroke they drew when all they wanted to do was to erase everything at once. Similar to TiltBrush where the usage of the eraser removes the complete stroke I then implemented a much simpler mechanic where the mere touch of the eraser immediately removed the completely line. Erasing became much more user-friendly afterwards. Although the work put into the more precise eraser mechanic was wasted now it ultimately resulted in a better experience.
For the scissors it was not difficult to come up with a solution. What was difficult though was the eradication of bugs as the scissors was deeply connected to the paper framework and thus quite error-prone.
The basic process of using the scissors goes like this:
The Unreal Engine already has very nice base functionality for slicing a mesh but I still needed to add a lot of features like vertex color, more precise convex hull slicing and of course all the 'logical' slicing like keeping the glue connections on the correct half post-slicing or cutting the drawn strokes in two as well.
A feature not present during the semester presentation is multi-threaded cutting which took all the calculations and executed it on another thread. Depending on the complexity of the sliced mesh it is either cut directly on the game thread which results in immediate slicing or it is transfered to another thread which results in slicing after 3 to 4 frames. The second approach trades a bit of user feedback for a smoother framerate. We also drown the four frames with a bunch of sounds and a fading visual effect which hides the delay completely.
Then there was the problem of how to determine whether a sliced actor half should be made dynamic (i.e. physically simulated) or whether it should stay static. Two examples:
When cutting this railing one would expect nothing to happen. You just cut through the railing, nothing should fall off or become loose, right?
When cutting off this branch on the other hand one would expect it to become dynamic and fall to the ground.
The difference between the two actors is that the railing has its anchor points defined. Every paper actor can define points that connect it to some completely static object like the ground or the walls. When this actor is now cut in two the anchor points are split among the halves as well. If one half contains at least one anchor point it is marked as static otherwise it is made dynamic. The railing for example has its anchor points defined here:
Last but not least there was the question of collision and how complex we were going to make it. The inner hollow area of a cut tree trunk for example is so big that any player would expect it to be non-blocking. Many of the more experienced players actually tried to put something inside the trunk to see whether it was completely convex or not. Due to the short semester we were not able to build custom-tailored convex hulls which is why we resorted to the V-HACD library built into Unreal's mesh editor. For some of the more complex meshes like the mentioned trunk we went up to 30 simple convex volumes to approximate the trunk's shape as close as possible. The results of the convex hull decomposition were decent enough and it allowed us to focus more on actual asset production.
Next there were the randomly shuffled scraps. From a technical point of view they were nothing amazing. Pick random parts, assemble them, add a random voice and you got a randomized character. The difficult part was to export each set piece so that the arms, legs and head would stay connected to the torso. They all had to trigger their animations at the same time while being completely different components and they should never go out of sync so that they appear as one entity. I had to tackle this feature many, many times until everything finally worked. There are a total of 20 scrap pieces (two arms, one leg pair, one torso and one head) which all had to be included into the concurrent animation system. Setting a random voice was nothing more than sending a parameter to WWise which played events such as screaming using said parameter.
The scrap swarm AI was quite straightforward but debugging it took a lot of standing up and down. The scraps avoid the player camera on a horizontal base.
Once the player comes to near to a scrap it moves the distance between the two in direction from the player to the scrap. Movement takes place using a simple
distance-dependent interpolation function. If the player camera is moving too far away from a scrap it gets tethered to the player and will move a minimum
distance towards them.
When a scrap is trying to evade the player camera but would hit a wall on the way away from the player it instead moves to the other side of the player therefore preventing getting stuck between the wall and the player. The AI had to be simple as we needed to support a lot of scraps at a time but it does a good job. Most of the players treat the scraps like some sort of floating dog and throw away their tools over and over again.
Lastly some words about the sound. Since we worked with the same sound designers as with Stockfall we already knew our way around WWise and its intricacies. What was new though was the addition of binaural sound. We used the Auro Headphone plugin. It required some modifications of the Unreal WWise plugin that is shipped with the WWise launcher that were not as intuitive as they could have been. There is noticeably less support for issues related to Unreal and WWise compared to, say, Unity and Wwise.
Beyond that WWise was the root of some issues when packaging the game as its declaration of a few UFUNCTIONs causes the Unreal packaging tool to crash. Other teams during the semester had similar problems with the software and even ditched it altogether in some cases. If my team and me did not have the experience from our previous project we probably would have joined the others.
WWise is a great tool and it offers a lot of fine control over sounds and their interaction with the environment but its integration into Unreal and Perforce workflows is okay at best.