Detective Interns

A first-person online co-op horror game with a funny touch about solving a murder mystery while the killer is still at large

-----------> WIP! Planned to release on Steam (2025) <-----------

I am the project manager, game designer, and programmer on this duo project. We started designing this concept as we've always wanted to design a horror co-op game, and after some researching, this was the most evident solution. I was going to develop this using the Mirror framework for multiplayer, together with the Fizzy Steamworks relay to let players host their own lobby's on Steam. This is a good middleground between too much abstraction and barebones netcode, letting us prototype efficiently. To land on this concept, we created countless concepts, iterating on them, and discussing them with my 3D generalist, Willem Hollants . After we decided on a concept, I started prototyping in Unity (URP) . It is a great learning experience in multiplayer code architecture and designing co-op games where cooperation comes first: you need your friends to help you out with tasks, and distract the murderer.


The challenges presented in this project are mostly programming & design related. I'll showcase some difficulties I've encountered and how I solved them here.

Design Challenge: Balancing cooperation, puzzles, and scope.

After some brainstorming, we came across a glaring issue: the scope is larger when working on online multiplayer games. This means we have to develop an MVP, cutting out all of the fluff and finding the fun in the concept. One of the core features is real cooperation: lots of online co-op games in the horror theme lack a sense of real co-op - you just exist in the same world, with the same purpose. We wanted to design our game around the premise that you have to actually work together. Combining this with puzzles, while high replayability is expected, wasn't as easy as anticipated. You simply can't have infinite random puzzles, and keep them interesting and engaging for the players.

Solution:

We designed the puzzles to be more like riddles, requiring information which can only be achieved by solving tasks in the house. This makes randomisation way easier:  it allows for interesting / engaging tasks, while not compromising the novelty of it (the outcome will always be different, and the enemy can come chase you at any time). Every task solved will give you some information, which could help you eliminate a potential suspect. E.g. You and your friend stand on a pressure plate at the same time, unlocking a new room, where you find a note with a formula for the age of the murderer.  This formula contains lots of variables, which you can find doing tasks or playing detective. Once you find any clue or variable, players can take a picture of it and bring it back to the planning board, to piece clues together from everybody and by process of elimination and detective work solve the mystery.


Programming Challenge: Syncing real-time screenshots over the network.

To take pictures and post them to the planning board, you need to make sure that every client can see the same picture at the same time. One issue: standard picture formats in HD resolution consist of multiple megabytes. This cannot just be transferred every frame over the network, as it would take forever and take up too much bandwith.

Solution:

The script listens for input, not sending network traffic every frame, but only after validation of an input.
Before taking a screencap, wait until the end of the frame where P is pressed to ensure the frame is rendered

Take a screenshot in JPG (lower filesize than PNG, no transparency needed)
Convert the screenshot to Texture2D
Use a static class "TextureConverter" to convert the JPG into an array of bytes

Right click pressed --> Raycast to find position and surface normal to figure out where to place the picture
Sends a command to the server to spawn a picture object (pass in position, surface normal, bytestream)

Server sends an RPC to all clients to update the pos, rot, parent of it on each individual client (Server Authorative):
-Convert the bytestream back to a Texture2D using the class, and converting into a Sprite on each client
-Set image in a World Space Canvas to the sprite (in ClientRPC so synced for everyone)

I'm always looking for opportunities. Contact me here!

LinkedInGitHubLinkLinkEmail