Building a designer-friendly gameplay framework with replication in mind
Unreal Engine C++ Multiplayer
Unreal Engine 🖤 C++
After studying various parts of Unreal Engine, it was time to dive deep into C++ and my favourite part of any game I personally play: multiplayer. Over the course of the next 8 months, I managed to get a good grasp of the engine's workings, replication and the gameplay framework.
FAQs
How long did you work on this project for?
I started working on this project in March of 2024 and finished the last touches in late October of the same year. All in all, 8 months.
What made you do this?
Sticking to my plan of learning Unreal Engine, this was the obvious step after studying most other aspects of the engine to some level. I wanted to get a deeper understanding of how the gameplay framework, their lifecycles, and other systems worked. Plus, I wanted to know if I could do multiplayer. :)
Was this entirely self-taught?
Not entirely. I enrolled in Tom Looman's "Professional Game Development in C++ and Unreal Engine" course, as it was best oriented for people proficient in programming and already familiar with the engine. Although I put in at least the same amount of time into individual learning as I did with the coursework.
What parts are you most proud of?
Usually I mention a feature or two in these sections, but this time, I'll have to say architecture and code quality. Even though the coursework provided great guidelines, the quality of the end product often felt lacking, so I put a substantial amount of work into bringing the quality onto a level I was truly proud of. I loved this.
What about challenges?
It has to be multiplayer, without a question. Replication was such a different way of thinking and writing code, that it was truly mind-boggling at first, and even the simplest tasks seemed daunting in a server-authorative model. I think the replicated gameplay class instances chart will forever be etched into my whiteboard now. :)
Did you manage to develop all the features you wanted?
I think so. There’s always something to add, but I’m happy with my feature list.
Can I see the code and check it out myself?
Yeah! You can find the project on GitHub, and should be able to build it with UE 5.2 and above.
Features
-
Characters and other elements (such as the Pause Menu) handle their own parts of the Enchanced Input implementation to decrease the amount of tight coupling.
-
All features are supported and tested for 2-player cooperative gameplay in the intended server-authoritative model.
-
Implemented a GAS-like action system from scratch. Behavior is admittedly simpler than Epic’s GAS but includes everything this project required.
Low performance overhead.
Replication support with prediction and server-side authentication.
Utilises GameplayTags throughout.
Support for Actions (one-time) and Effects (over-time).
Blueprint Action & Effect support for easy iteration for designers.
Actions include elements such as: Projectile attack, teleport projectile, blackhole projectile with phsyics support.
Effects include elements such as: Burning damage-over-time, parrying, reflective “thorns” buffs.
-
Health: is representative of the Character’s life force.
Rage: is given to the player upon receiving damage, depending on the damage received.
Replication support with server-side authentication.
Handles changes of the relevant values it is responsible for, runs validity checks, broadcasts to observers.
Values are customizable in Blueprints on a per-usage basis.
-
AI behavior support a “ranged enemy” type behavior.
AI with Behavior Trees, with support for various tasks.
BT Tasks include the following: roaming, sensing (seeing), chasing, attacking, fleeing, healing.
BT services and decorators include: checking attack range validity, checking self-health, ability cooldowns.
EQS queries to support the AI’s various positioning-related tasks.
-
14 different delegates are used instead of ticks throughout. In shipped code, no tick functions are used, anywhere.
-
GameMode responsibilites are separated into Components.
GameMode EQS queries upon gameplay start to spawn a set of pickups in random places.
GameMode reads enemy data, spawn chance weights and such from DataAssets and DataTables.
GameMode spawn enemies over time, with weighted randomized chances, based on a difficulty curve.
GameMode rewards players with credits upon destroying enemies.
-
Various elements in the world can be locked, preventing interaction with them, unless a keycard is present.
Keycards can be given to Characters with which they can bypass the locks.
Replicated in a server-authoritative model.
-
Treasure Chests can be opened, giving credits to the player.
Pickups can be collected for credits, health, or even to enable new abilities.
Levers can be toggled to give the player key-cards that can enable the opening of closed items (such as chests, doors, etc.).
World interactors also respond with relevant localized state descriptions to queriers.
-
The SaveGame system saves various world-state elements.
Not currently replicated as that would've required EOS/Steam integration for UUIDs which was outside of the project scope.
-
Loads the DataAssets and their DataTables asynchrounously.
Also includes async loading of other assets elsewhere in the project.
-
Main menu, pause menu, gameplay HUD, all with animations, states, and standardized behavior.
Dynamic behavior hooks into delegates to change the UI in various places.
-
All GameplayTags are defined in C++.
Console commands include elements such as: Health/Damage multipliers, flags to prevent/enable spawning of enemies and drawing of debug elements in world.
Developer settings include a project-level setting to specify save-game file names without needing to launch the game from a shipped build.