Today I thought I’d explain how the journal system in Monstrum is implemented. The journal is our equivalent of a quest/mission log in many other games, and has a few noteworthy features:
It is diegetic, which means it exists in the game world itself, instead of, for example, being rendered on a 2D plane in front of everything else. We chose a diegetic interface as it is generally more immersive, and also works better for the Oculus Rift.
The information in the journal is rendered on the page itself, following the curve of the paper. This makes the journal feel more natural than, say, having text float above the page.
In order to create the effect of having the UI rendered on to the page, the journal model is first split in to two – the cover and the page. This is so the two meshes can be UV mapped separately. The cover is just a standard model, but the page is UV mapped with (0,0) in one corner to (1,1) in the opposite. A separate camera is pointed at a custom UI system and draws to a RenderTexture that is displayed on the page. At this point, we can see the journal info on the page. The trouble is, we can’t interact with it!
To solve this second problem, we have to start with the player. The player starts an interaction with any given object by looking at it. Therefore, we raycast along the camera forward, looking for the collision mesh of the journal page. If we miss, the player isn’t looking at it. If we hit, how do we know where on the page they are looking? Well, Unity provides the texture coordinates of a hit from a raycast. These will be mapped between (0,0) and (1,1), as defined earlier by the UV map. All we need to do now is use the Camera.ViewportPointToRay function, and cast the returned ray, masking for our journal UI elements. At this point we can do whatever we like with the returned hit data (button clicks, mouseovers, positioning the journal cursor). Hooray!
I hope this technique may be of use to someone!