Intro
So we are 5 days from the rating deadline so I'm confident I can write this spoiler-ridden post about my LD54 game Corporate Courier Deluxe. For those who haven't played it, it's a 20-ish minute long game about delivering parcels inside a corporate building. Your character cannot move, only look around and interact. They are confined to a small utility elevator navigating through the floors. Each floor will present you with one or two parcels to deliver to another floor. The game quickly becomes a mess of managing a limited space, using your memory and own organization methods.
SPOILERS FROM NOW ON
We wanted to create a game that would look like a typical progressive, job simulator, but underneath would have some sort of hidden narrative/story. After a certain number of delivered parcels, you will get one that looks like any other, but it has a back note, only visible if you actually throw the package around or inspect it. You can actually miss the entire game plot if you don't look carefully and this was a deliberate design choice.
The challenge
The story is told through back-notes written on the shadow parcels where, to progress in the story, you should deliver the parcel to the shadow destination instead of the standard one. This means that further story events would require specific events and progression. So we have, scripted events with triggering prerequisites and triggered flags. We also wanted, from the first draft, to have special quest items that would help you progress, from a design point, these items give the player a sense of progression and novelty. So we also have to consider how to add these items to our system in a modular or scalable fashion.
Solving the Event System
In my architecture, there are a couple of central pieces, a game/narrative flow script that counts delivered parcels, stages endgames and in general controls the logic on how the game progresses and what should happen next. The other central piece is the script that actually places items in the counter once you arrive to a floor.
So our Parcel Manager spawns the parcels, and our Game Flow knows what's up with the narrative right now, the interesting part is the relationship.
In my implementation, the Parcel Manager asks the Game Flow if its time for a scripted event and stages the next scripted event.
This means that the gameflow should know when a scripted event is due, and as I mentioned before, these events have prerequisites, all of these events are defined in a scriptable object such as this:
In this case, it is the first event of the game. It will be staged as soon as the player delivers 5 normal parcels and it will create a single shadow parcel with that back note that upon shadow delivery will stage the flag firstshadowdelivery to the game flow state, triggering more events. Also this event won't spawn any quest items.
Next events will require increasing shadow deliveries and certain actions to be completed.
With this, I had a reliable system of scripted events with written exposition that I could scale up from the editor without having to write a lot of specific if-clauses in the code.
Quest items and callback functions galore
The addition of quest items that you would grab and use to interact with special items was something that had a couple of interesting challenges. First, I extracted the "grabbable" interface from parcel to accommodate the quest items as well.
With that out of the way, it was time to attack the narrative aspect of these items. The spatula is used to remove a ceiling panel which uncovers a one-of-a-kind parcel. The screwdriver allows you to open the elevator panel and unlock floor 133. This means that not only I had to add the items, but also, script the interaction.
IMPORTANT
Writing reliable, maintainable and scalable code is all about finding the behavior patterns. I had to code two screws and a ceiling panel, but where's the pattern??? Well... to interact with these items you have to be holding the correct tool and click on the target interactable item, pattern discovered, we now have an Interactable Item. (No screw, no panel)
But does this means that the screw has to behave like a panel or a panel like a screw? None, by using callback functions you can make a script trigger different things on runtime. This means that the ceiling panel will trigger the special package spawn when its activated, and the screw will trigger the elevator panel opening when activated.
This script works pretty much for any interactable thing that requires a quest item
Screw initialization
Ceiling panel initialization
For all the design patterns nerds out there.... yes this is Dependency Injection applied to Unity :smile:
Conclusion
Narrative systems are complex because when someone tells a story they are not thinking about the technical implementation those verbs require. This is why we have to abstract the behavioral pattern of the verbs and try to cheat our way around the story telling. This is pretty much why every other RPG out there can be summarized in a fetch quest and go kill some monster loop, we cannot accommodate unlimited verbs into a precise scripting implementation.
- Avoid doing hardcoded events in your code
- Try to find the abstract pattern of your verbs and build an easy to build and read event line.
- Building reliable systems is better than building long stories
- Use data structures and OOP to your benefit. Relegate the responsibility of prerequisites to the events, and not to the event manager
If you managed to get this far, thank you very much! I wanted to share this experience mainly because it took me under 2 hours to implement all of this, so it has a lot of value for game jams. Hope you found this post useful or at least interesting and would love to discuss your style of implementing scripted narrative.