Part of our game, we introduced a lot of functionality now, including combat system and character stats. The post on character stats can be found here.
This post will focus on the Unreal Engine implementation. The custom server implementation was added here.
The status system is not too far from stats, but still quite different. What does it entail?
Status effects also belong to actors and their effects can vary significantly. Some of the status effects could be buffs and some are debuffs.
The various effects they can have can include:
- stunned (can’t move, can’t cast, etc)
- silenced (can move, can’t cast)
- damage effects (e.g. burning, bleeding)
- enhancements or debuffs (such as move speed increase, attack/cast time reductions, etc)
- and perhaps many more
In this particular case, I will add a death status – this should prevent the character from moving and will also spawn a widget on screen to allow player to respawn.
Let’s get started.
Status Component
Status is an attribute for my actors, as such I will create a separate actor component to handle these.
As usual, the parent class for these components is the Connected Component Base which mainly just deals with comms with the server.
Here it is now added to my actor BPs.
Let’s see what functions we’ll be looking at:
Some of the important ones are:
- Fetch Status
- Process Message
- Process Status Update Message
- Process Actor Status
- Process Respawn
Fetch status
Fetch status is one of the first thing that we’ll do on any actor associated with this component.
This is a fairly simple call via websocket to my server to fetch the actor status.
That’s literally it 😀 quite simple right?
Actor Status Structure
Before checking the structures in UE, let’s first check how its defined in our Java server, which is the same definition.
public class ActorStatus {
String actorId;
Set<Status> actorStatuses;
boolean add;
Set<String> statusEffects;
...
}
Actor status is associated to a given actorId. We then have a set of all the actor Statuses that the actor actually has.
The next boolean add – this is something I added to make integration between server and client easier. I will be sending this structure on each update of the status. This means if I added some statuses, I will send this update with the add boolean true, otherwise it will be false to indicate that these are being removed.
I’ve now added statusEffects which will represent the aggregated set of status effects, such as whether player can or can’t move, cast, etc.
Now let’s check the Status class.
public class Status {
String id;
Map<String, Double> derivedEffects;
Set<String> statusEffects;
Instant added;
Instant expiration;
Boolean canStack;
String origin;
String category;
}
- ID will be the status instance id, so that I can add and remove the status instances from the actors.
- dervicedEffects will contain any modifications to the stats system – so for instance modifications on max MP/HP, attack speed, cast speed, etc.
- statusEffects will contain set of statuses, such as can’t cast or move, etc.
- added/expiration these are the timestamp of when the status is added and when its due to expire
- canStack – potentially should change to stack limit, will refer to how many of the same effects can stack
- origin – will refer to actor ID which caused this status
- category – this is identifier for the status
Now let’s reference their UE structs.
As you can see, they are the same as what’s defined in Java.
Process Status response
Now that we know what’s the actor status structure, we’re able to receive the response and process it.
My ConnectedComponentBase exposes the process message function where I just need to filter for the status update type.
This actor component can sit on any actor that’s in game, so first thing I want to do is filter for that particular actor.
If the message is intended for this actor, then I push the data to the function ‘Process Actor Status’.
The Process Actor Status function has this definition:
Don’t worry, we’ll go through the flow and zoom into relevant functions below.
The first thing to note is that the functionality branches on Add boolean.
I’ve made the server make add boolean true when the status is being added to the actor and it will be false if the statuses are being removed from the actor. This simplifies the logic flow for me.
Before I go into handling the add or remove of the statuses, let’s first introduce the new Object I created to represent my Statuses and help me handle the status effects.
Status Object to help handle effects
We introduced the status structure earlier which encapsulates the data we need. We may need to make some gameplay effects based on statuses too though. For that, I create an object to help deal with it.
Let’s first look into the StatusBase object – this will be the shared class for all Statuses in future.
For now, I added two functions:
- activate
- deactivate
And several variables:
- Status (Status struct we introduced earlier)
- Status Actor (the actor to which we’re applying status to)
- icon (icon to represent status in action bar)
- iconColor (because my icons are transparent by default, I apply color manually)
- playerCanvas (because some statuses will want to affect widgets on screen)
Activate and Deactivate base functions
The sub classes will handle most of the logic of applying status effects, this may be spawning actor classes and applying visuals/sounds etc.
The common thing that I expect them to require is to add widgets on the screen to represent the statuses. For now, I only have the action bar widget, so that’s where I will add them. This widget is for the player only.
Note that the Status base is a pure object and therefore did not have access to game instance or an ability to search for widgets. So I have to provide the player canvas in the constructor.
Let’s also check how we add the status widget on the action bar
Handle add and remove status widget in action bar
The little widgets will be displayed like this in the actionbar
This action bar is for the player and represents the players active statuses, which is why there’s filter for player before adding this widget.
For add status:
For add status you can see that I am taking the status object and extracting the icon and icon color to use for the Status widget. Later I will perhaps add other details, like status information too.
I also add it to Tracked Statuses variable, so that I can do the lookup and remove it faster, which we’ll see below.
You can see that I just have to find the status widget by ID and remove it from action bar.
Remove status
As we saw in the Add status, we added the status to the action bar widget and they are being tracked in the TrackedStatuses variable.
Now we can check about removing those statuses.
The first function is called from the Status component and the second function is from the actionbar widget.
As you can see, it becomes fairly trivial to remove the status widget when we track it in the variable.
Death Status object
Each status effect will have its own class/UE object defining how it will affect the player.
For now, I only created the Death status object, but you will see how you can create others too.
Notice a few things here; First is that when we create this blueprint, we make the Status Base the parent object.
With the Status Base as the parent, we can override the activate and deactivate functions, whilst calling the parent too.
Now with the function that we can see displayed, activate: we want to get the actor blueprint and set the is Dead (custom variable) to true. This will link to our animation blueprint.
Next, we want to disable the character movement, by setting the movement mode to none.
Finally, if this is a player actor, I also want to display the respawn widget, which is part of my player canvas.
When the death status is removed, we basically want to undo all the activated features. i.e. we want to change the is dead back to false, we want to enable movement and if its a player we want to hide the respawn widget.
Also, in the Class Defaults, I want to configure any base parameters, such as the icon and color to use when displaying this status.
Finding the correct status object
I will be receiving a message from server to add a status to an actor, this information will be in the form of ID or category for me. The way I link it to this object is via a data table.
I created a simple structure containing a class for status Class:
And using this structure, i created a data table:
I will search this data table using the category, i.e. dead state. As you can see, I added an entry to map it to the Dead State object.
Add Status flow in actor component
Ok now we saw the raw object and how its added to widget, let’s go back to the status component and zoom into the Add Status flow. This is from the “Process Actor Status” function we introduced earlier, (ctrl + F to find it above again).
When the add boolean is true, we add each status – this will construct the status object based on the category. It finds the correct object using the data table we introduced above.
Here we also add all relevant variables into the constructor, such as the status structure, the actor in question and the player canvas widget reference.
I add it to actor statuses variable for quick lookup and I call the activate function. This function will deal with all necessary features of activating that said status on the actor.
Remove status
Now when the add is false, it means we need to remove the status from the actor. The way we achieve this is by simply finding the status object and calling deactivate on it. We also stop tracking it by removing it from our actor statuses map.
Handling Player Respawn
I added a simple new widget which will pop up, as seen above, when the player receives a status update containing dead state.
Their blueprints are quite straightforward. They’re just button events that send request to server to respawn the player.
As you can see, its using the Status Component to request this respawn.
I added a piece of custom data to specify the respawn point.
The rest is handled by the custom server itself. It looks up the players current location and map and identifies the respawn point which is closest to it.
Next, it will send a message to the client to force update the location. It will also reset the statuses and reset the HP/MP.
Force update motion on respawn
As mentioned above, the server will process the respawn request and will send a force update motion message to the client.
I have a PlayerMotionSync component which will look to process this.
This function will check that the player motion update is for the intended actor. It then obtains the owner of this actor component and updates both, target locations and the actual actor location.
The target location variable is used by my custom smoother component.
I will be able to use this functionality in near future if the player is trying to make motion which will be rejected by motion validator.
Bonus: Link death animation
In my animation blueprint, I extended the Event Blueprint Update Animation to start tracking the is dead variable.
I then added a new state for Dead in my state machine.
This state will simply play the death animation:
In order to enter the state, which is currently possible from either idle or channeling, we just need to have the ‘isDead’ boolean to be true.
And to exit this state, the ‘isDead’ simply has to be false.