Leveraging UE4's Gameplay Framework for our Multiplayer game

Rohan Mayya
Project Asura

--

Unreal Engine is great, but the one thing we all collectively dislike about it is its docs.
I mean, just search try wrapping your head around Networking for Unreal (and subsequently its Gameplay Framework).

Thank god for Cedric Neukirchen’s Networking compendium, otherwise, we’d all be in trouble today.

This article is not intended to be a guide. What I intend to detail over the rest of it is how we’ve (successfully) manage to wrangle the Gameplay Framework to our needs, where we struggled, and scope for improvement.

Credit to Cedric Neukirchen for this golden image.

I’ll be covering these topics in detail with the game we’re building as our context:
Pawn
PlayerController
GameMode
GameState
PlayerState
GameInstance

Our Requirements

We’re building something similar to Among Us. So we have all the usual stuff. Voting, running around, and being able to kill other people. Multiple intermediary loading screens to be shown, and a lot of states to track. Plus roles.

We also have 2 levels that players keep transitioning between, so we need to juggle important state between these 2 levels.

Off the top of my head, we need to:

  • Assign vampire(s) (impostor, as you might call it),
  • Keep track of who’s dead
  • Figure out managing states BETWEEN levels (The main level and a voting level)

Our intention is to map specific problems to specific parts of the Gameplay Framework so that you, the reader, get a better idea of how to use these components, and for what purpose.

Before I begin, I’d like to say that semanticity matters. It makes sense to use a component to do exactly what it was designed for.

eg: Writing logic that controls the movement for a player in a Level Blueprint does not make sense at all.

You get the idea.

Let Us Begin

First off, we need to find a way to store the player’s username throughout the session of being in-game.

Game Instance

The Game Instance class happens to be perfect for this. There are 3 important things to know about Game Instance:

  • Every single device connected to the game has its own local copy of Game Instance.
  • Game Instance is not replicated. In other words, only YOU know about your own GameInstance.
  • Data stored in the Game Instance lasts all throughout the lifecycle of the game. This means, from the time you open your game app till the time it closes, the data persists.

This is perfect for storing data that is relevant all throughout, from opening the application right to closing it.

This leads to an important observation: Game Instance should ideally be avoided when you’re storing stuff inside a running instance of a game.

Examples of where we’re using Game Instance (now and in the future):

  • Storing the player’s username,
  • Storing choices made in the main menu (such as skins selection, lobby size, map choice, etc).

Everything In-Game

Next, we need to worry about everything in-game.
Let’s break this down:

  • Player details (Am I dead? Am I a vampire? Have I voted during the voting round? How much of my tasks have I completed?)
  • Details about the current state of the game (How much progress left till everyone collectively finishes all their tasks? How much time has passed in the game currently? Is it daytime? Is it nighttime?)
  • Details about win conditions. (What to do when all the vampires are dead? Or when all the normal players are dead? What about edge cases, like when 1 vampire and 1 civilian are alive?)
  • Widgets and HUD (How do I show my cooldowns and abilities? What about the visual interface for all the buttons I click on to kill or vote for someone?)
  • Inputs (How do I tie in the visual buttons to controls that do something? Should I do that it in the Player Controller or the Player Pawn?)

Remember, the purpose of this article is to map these questions to suitable components of the Gameplay Framework.
Very specific implementations and edge cases are something we’ll talk about in future articles.

But, to keep it simple:

PlayerState

We’ve used PlayerState for all relevant player details. PlayerState is replicated over the network. This means the server is aware of my player state, and so are all the other connected devices (From now, we’ll call them clients).

It’s important to understand the distinction between something that’s on the PlayerState and having something only locally (on the client).

To illustrate, think about a detail like storing information about being dead.

Ask yourself one question: Does every connected player have to know whether you’re dead or alive? If yes, then store it in PlayerState.

Now ask yourself another question: Does every connected player need to know whether you have the scoreboard open on your own screen? If no, then don’t store it in PlayerState.

More specifically, for the second question, you can consider using PlayerController or something that’s not necessarily networked, to toggle opening and closing the scoreboard. (I’m speaking about the logic here, not the visual HUD elements.)

GameState

When considering where to use GameState, ask yourself one important question:

Does a variable that is continuously changing contribute to ending the game? If yes, it belongs to GameState. This is a bit of an oversimplified explanation, but it gets the job done in most cases.

Let’s do examples again. They’ll make things pretty clear.

Ask yourself this: Does every player need to have a synchronized time clock? If yes, it probably goes in GameState.

The reason? GameState belongs only to the server, but it is replicated to every client. Hence, it makes semantic sense to store a variable here that is going to affect every client.

Great examples of this are current time, number of days that have passed, the progress of all tasks put together, etc.

To illustrate the first example a little further:
Current time changes every second, and on reaching 0:00 seconds, the level ends.
So it’s considered to be a variable that contributes to the game actually ending.

GameMode

This class is most often associated with win conditions. It also is the right place to define the rules of a game.

When working with this class, ask yourself this:
Has some condition been reached, or some variable changed, such that we now have to transition to a new level or end the game?

Let’s take our previous example of the current time being stored in the GameState.

For an easy understanding, let’s assume our game clock starts at 5:00, and when the clock reaches 0:00, we want the game to end.

This rule is what we would describe in GameMode. We’d have a variable, say, “EndTime”, (set to 0:00), and then we say, if the current time in GameState is equal to the EndTime, then we’d like to end the game in the GameMode.

Another example is the number of days passed. If 4 days have passed (which we’re keeping track of in the GameState), then end the game here (after doing a check).

PlayerController

We’re using this for Splash Screens and all our controls.

Reason? Other clients don’t care about you actually pressing the W key. They only care to see if your character has moved one step forward. Your character pawn takes care of displaying that.

This makes it incredibly useful as a space to define all your controls and everything that’s only local to your own device.

Examples of controls are Movement, Calling for an emergency meeting, Camera rotation.

Pawn

We’ve actually decided to keep some of the controls here, such as killing and transforming into a vampire. And also the role-specific abilities.

The reason? We want to use a single PlayerController class to define all the common controls, such as movement and opening scoreboard.

However, since the roles will have different abilities, it makes more sense to bind that control in the Pawn (in this case, our Character class. Remember, Character inherits from Pawn).

Conclusion

There are some parts I still haven’t touched, such as retaining state when transitioning between levels, etc. But I definitely plan on covering them sometime in the future. I hope you enjoyed reading this article as much as I did writing it!

--

--

Rohan Mayya
Project Asura

Strong proponent of building impactful software. Usually write about Web and Game Development.