DevLog68: Introduce Status to our game – how to handle respawn

DevLog68: Introduce Status to our game – how to handle respawn

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.

adding status system to the game
Respawn screen popped up, character play death animation and status widget added

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.

Creating status component

As usual, the parent class for these components is the Connected Component Base which mainly just deals with comms with the server.

Connected Component Base as parent class

Here it is now added to my actor BPs.

Status component is added to my actor BPs

Let’s see what functions we’ll be looking at:

Functions part of status component

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.

Fetch status on begin play

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.

Actor Status Struct
Status struct definition

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.

Process Status Update when message is received

My ConnectedComponentBase exposes the process message function where I just need to filter for the status update type.

Process Status Update message by routing it to correct function

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:

Process actor status 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.

Status object to help with effects

Let’s first look into the StatusBase object – this will be the shared class for all Statuses in future.

Functions and variables for Status Base class

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.

Handle activate in Status Base class
Handle deactivate in base class

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

example of the buff icon for status

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:

Add status to action bar

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.

Remove status function

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.

Process remove status
Remove status from actionbar

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.

Death status object with Activate functionality

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.

Deactivate death status function

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.

Configure class defaults like icon in Death state

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:

Create structure for status Class

And using this structure, i created a data table:

Data table containing link from category to status class

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).

Add status flow in actor component

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

Remove status from actor

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.

Button to respawn player

As you can see, its using the Status Component to request this respawn.

JSON request to respawn player

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.

Handle process force update motion

I have a PlayerMotionSync component which will look to process this.

Update actor motion

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.

Event blueprint update animation
Track the 'isDead' parameter

I then added a new state for Dead in my state machine.

Create dead state in state machine

This state will simply play the death animation:

Play 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.

Enter the state when isDead is true

And to exit this state, the ‘isDead’ simply has to be false.

exit the state when isDead is false