About
A Game of Cheese is a exploration/puzzle game in which you play as a rat who lives in the basement of methamphetamine addicts.
The game is about scouring a basement and figuring out how to collect all of the cheese hiding down there. You can also consume methamphetamine for some interesting bonus stats and effects.
Project Overview
Language
C++
Engine
Unreal
Platform
PC
Year
2021
Development Time
7 weeks
Team Size
9
My contributions
Movement state machine system usable in both C++ and Blueprint at the same time for easier cooperation between programmers and designers.
Various movement states and mechanics with detailed and organized options panel for easier tuning for designers
Climbing mechanic
Animated popup menus and circle gauge shader
Development
The development of this game was in many ways very ironic. One of the things we agreed on early on was that most of us had over-scoped projects in the past and that this time we wanted to try something making something easily adaptable.
Because of this, the game ended up being very character-centric with a lot of different movement mechanics - this would make it easy to add and remove features, we thought. Instead, that approach quickly become a problem once we realized that 3 designers and 2 programmers had to work in the same Blueprint file (the one for the character), that was inheriting from the same C++ file. Merging of Blueprint files (in version control) isn't supported. Even using composition, just having a seperate file/class for movement doesn't really help when most of the game mechanics are different types of movement.
When your entire team tries to check out the same file in Perforce every 10 minutes
The cheese they're trying to get is the file called BP_RCharacter
A movement state pattern for both C++ and Blueprint
The project desperately needed structure and modularization. While I was familiar with the concept of components and had worked that way before, integrating them into a workflow that would work well in Unreal with both Blueprints and C++ was another challenge and a pretty hard one at that. I wanted to find a way to use the same state pattern for BOTH C++ and Blueprint, not just either-or. As programmers, we didn't want to be locked out of using C++, but we also didn't want to lock the designers out of using Blueprint.
To do stuff like this, one needs to really understand how Blueprint and C++ interact with each other in Unreal, and you kind of have to delve into some engine code to read through a bunch of comments that may or may not lead you on the right path. This was quite hard in the beginning.
Eventually though, I found out about the so-called _Implementation methods in Unreal Engine. For a lot of Unreal's built-in methods that can be marked as BlueprintNative or BlueprintCallable, Unreal also implements a C++ method with the same name, appended with _Implementation. This implementation function works as a base default function that gets called if the event function isn't implemented in Blueprint.
You can override it in C++, but it won't be called if you implement the event in Blueprint.
But in Blueprint you can actually make a call to the parent function before proceeding. Then you can use both implementations! The C++ function will simply run before you continue with the Blueprint function.
After having finally figured out Unreal's awful syntax for creating interfaces, I found that making a state pattern like this actually isn't that complicated.
First, make an interface with both UFUNCTION(BlueprintNativeEvent, ..)-marked methods and virtual _Implementation methods.
In C++, your new states simply need to inherit from the interface and override the virtual functions
In the child Blueprint, the functions will now be available like you could previously see in the image above.
To initalize the state machine, you can first create a UDataAsset to be able to easily reference the Blueprint child classes:
And then initialize them on the object that keeps the state:
Changing states is now as easy as calling ChangeState(...), and it can be done through Blueprint as well if you prefer:
Thanks to the implementation of this pattern, it was now possible to work on several different movement states in Blueprint, as well as C++.
The reason there is both a VaultingComp and a VaultStateComp is that Hugo, another programmer in my team was working on this component while I was working on the state system.
Instead of wasting time rewriting and rewiring a ton of code, Hugo implemented VaultStateComp with hooks into VaultingComp. It worked out great!
Credits
Art
Emmet Mörk
Gustav Nilsson
Johan Alström
Programming
Benjamin Fager
Henrik Nilsson
Hugo Lindroth
Design
Emile Sterner Jonsson
Mateusz Sieradzinski
Pussel Widegren
Copyright © 2024 Henrik Nilsson