In the previous post we’ve looked at synchronizing the nearby players motion on our client.
This means that we could see the nearby players move about as we expect, however their appearance was not synchronized between the clients – this is what we’ll focus on achieving in this post.
This is actually not too difficult to achieve because we will be leveraging a lot of pre-existing functionality.
The main things that we have to do is:
- have a custom event that’s called every x seconds (will set to 0.5 seconds by default)
- send GET API call to obtain the appearance information for our characters using VaRest plugin
- apply the appearance information to the proxy characters on screen
Graph for synchronising appearance
First thing – I added a new graph for synchronizing the nearby players appearance information and renamed the other to specifically cover only motion.
As the name suggests, this graph will be responsible for synchronizing the nearby proxy character appearance data.
First let’s add an entry point for this graph.
This function will run asynchronously – we want to keep the logic separate from others to avoid making any single graph too complicated.
We also make it async to avoid any single call becoming ‘too heavy’. For instance, the motion needs to be synchronized almost real-time, however appearance information doesn’t change as often, so its ok to make it happen less often. We can reduce load quite significantly by splitting this up (though bear in mind any additional HTTP call also has a relatively high overhead).
Next, let’s see what we’re actually trying to get every 0.5 seconds.
Here we see we make a GET request to: http://localhost:8081/player/characters?names=character1,character2
The request has url params for names
, which will contain comma separated name values for characters we’re interested to get information of.
The response contains accountCharacters
which includes the appearance information that we care about.
Let’s see how we make this call in UE:
We can see that we’re using the VaRest plugin in order to create a Json GET request here.
The URL is generated via a helper method – it’s time we start using refactored function calls to generate them.
The helper method is:
The input is an array of strings, in our case it’s an array of keys from NearbyPlayersData
where the keys are the character names.
We can use the join string array
function in order to nearly put those names together in CSV format.
The host
is also refactored to make it easier to change in future, when we will push this to production.
After getting the result from the API call, we want to process it.
Let’s cover what we’re doing here:
- Get the account character array information from response
- Convert to
AccountCharacter
struct that we can use in our BP - Update the
NearbyPlayersData
with the appearance information - Apply the appearance data onto the proxy characters
1 – We saw that the response contained an array called accountCharacters
– so we pull it from the payload.
For extra visibility, here’s the expected JSON payload again, notice the accountCharacters
object, as well as the appearanceInfo
that we ultimately want.
2 – We already covered the ConvertToAccountCharacterResponse
function before (part of create/select character widget screens), but here are the helper methods just in case:
3 – Updating the nearby players data struct.
Remember that the variable contains a map of player name as key and Character data as value structure.
The definition of CharacterData
is:
which includes AccountCharacter
object, which is exactly what we’re pulling.
So once we’ve pulled the AccountCharacter
data – we update the definition inside the NearbyPlayersData
variable, using the name
that’s inside the AccountCharacter
as the key.
It can sound confusing so here’s breakdown:
First, get the name from account character and use that to find the nearby player data relevant to this account character.
Next, set the members of CharacterData
– here we’re specifically updating the AccountCharacter
that we’ve just obtained.
Finally, ADD it back into the map, using the name as the key.
This is because when we updated the value, we updated a copy of the value – if there’s a way to update reference of the value, we would not need to add it back.
4 – applying this appearance information onto the proxies.
Now that we’ve updated the Nearby Players Data
struct, we can iterate over each one, and update the proxy characters appearance information and tell each one to Resolve All Appearance
.
The resolver was covered part of create character screen post.
Final step is to add an entry point
I added mine to the SynchroniseMotion
part of setting up websocket.
It can exist in constructor instead or something like ‘event begin play’.
And that’s it!
Testing
Now, simply start the game, select two characters and enter the game.
You will find that we will re-sync character appearance every 0.5 seconds!
Further improvements?
Many as usual, one big one for instance is to make the resolve character appearance
more efficient. Specifically, only start resolving when there’s a noticed change in appearance. Currently it will try applying all appearance information even if nothing’s changed.
Question – how are you planning on loading the map data – useful for things like preventing players from walking through walls, adding trigger zones, etc
hey!
Simple answer is that I am not.
Unless you have a larger team, I don’t think this aspect is worth it, if you will try synchronise your movement with the server, not only will it add a lot of additional processing power requirements, but if not done well, it will make player experience a lot worse.
However, for things like combat with mobs/AI, the idea is to have UE server(s) loaded and playing with the custom server (which are map aware).
In that way, the UE server is almost like an acting player of the mobs. Furthermore, for the combat, you could do synchronisation with server, so the client will not be responsible for evaluating damage etc, but rather would ‘request’ to attack, or ‘request’ to use skill, and the server will actually validate and process it.
interesting, but if the UE server is to be loaded anyway, what do we gain by having this secondary system around? seems like we could skip all the character synchronization in that case and just use this system for server selection/character selection
basically UE servers are not very scalable.
If your game (or map/zone in a game) will have 100 players or less, you can simply use UE replication – this is definitely recommended.
However when you’re trying to synchronize thousands of players in a single map/zone, it will be too much for UE servers to handle.
for character selection/creation, you could use something even simpler – you don’t need a ‘server’ per se, there are serverless solutions, like Amazon Game lift (https://aws.amazon.com/gamelift/getting-started)
or PlayFab (https://learn.microsoft.com/en-us/gaming/playfab/sdks/unreal/quickstart)
there’s more abstract solutions too, like Xsolla game store: https://www.unrealengine.com/marketplace/en-US/product/xsolla-store-sdk
this should (if I remember correctly) have login options, which will effectively have custom fields for characters + much more.