Distributed Programming  
Lecture 05 - Multiplayer and Network  
Communication in Unreal Engine  
Edirlei Soares de Lima  
<edirlei.lima@universidadeeuropeia.pt>  
Multiplayer in Unreal Engine  
The Unreal Engine is built with multiplayer gaming in mind.  
Is very easy to extend a single player experience to multiplayer.  
Even single-player games, use the networking architecture.  
The engine is based on a client-server model.  
The server is authoritative and makes sure all connected clients are  
continually updated.  
Actors are the main element that the server uses to keep  
clients up to date.  
The server sends information about the actors to clients;  
Clients have an approximation of each actor and the server maintains  
the authoritative version.  
Multiplayer in Unreal Engine  
The server is in charge of driving the flow of gameplay.  
It handles network connections,  
notifies clients when gameplay  
starts and ends, when is time to  
travel to a new map, along with  
actor replication updates.  
Only the server contains a valid  
copy of the GameMode actor.  
Clients contain only an approximate  
copy of the actors, and can use it as  
a reference to know the general  
state of the game.  
Multiplayer in Unreal Engine  
Network Modes:  
Standalone: the server runs on a local machine and not accept clients  
from remote machines. Is used for single-player games.  
Dedicated Server: the server has no local players and can run more  
efficiently by discarding sound, graphics, user input, and other player-  
oriented features. Is used for multiplayer games hosted on a trusted  
and reliable server where high-performing are needed.  
ListenServer: is a server that hosts a local player, but is open to  
connections from remote players as well. Is good for games where users  
can set up and play their own games without a third-party server.  
Client: the machine is a client that can connect to a dedicated or listen  
server, and therefore will not run server-side logic.  
Multiplayer in Unreal Engine  
The core element of the networking process in Unreal Engine  
is Actor Replication.  
The server maintains a list of actors and updates the client periodically  
so that the client can have a close approximation of each actor (that is  
marked to be replicated).  
Actors are updated in two main ways:  
Property updates;  
RPCs (Remote Procedure Calls).  
Properties are replicated automatically (any time they change)  
and RPCs are only replicated when executed.  
Property Replication  
Example of property to be replicated: actor's health.  
Each actor maintains a list of properties that can be marked  
for replication to clients.  
Whenever the value of the variable changes on the server side, the  
server sends the client the updated value.  
Property updates only come from the server (i.e.: the client will never  
send property updates to the server).  
Some properties replicate by default (e.g.: Location, Rotation, etc.).  
Actor property replication is reliable.  
Property Replication  
Replicate a property:  
1. Set the replicated keyword:  
UPROPERTY(replicated)  
float health;  
2. Implement the GetLifetimeReplicatedProps function:  
void MyClass::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>&  
OutLifetimeProps) const{  
Super::GetLifetimeReplicatedProps(OutLifetimeProps);  
DOREPLIFETIME(MyClass, health);  
Default replication rule:  
}
replicates to all clients  
3. Enable replication in the constructor method:  
SetReplicates(true);  
Remote Procedure Calls (RPCs)  
Example of RPC: a function to spawn an explosion to each  
client at a certain location.  
RPCs are functions that are called locally, but executed  
remotely on another machine.  
Primary use: to do unreliable gameplay events that are temporary or  
cosmetic in nature.  
E.g.: play sounds, spawn particles, or do other temporary effects that  
are not crucial to the Actor functioning.  
By default, RPCs are unreliable. To be reliable, a especial  
keyword (Reliable) must be used in the definition of the RPC.  
Remote Procedure Calls (RPCs)  
Defining a RPC:  
To declare a function as an RPC that will be called on the server, but  
executed on the client:  
UFUNCTION(Client)  
void ClientRPCFunction();  
To declare a function as an RPC that will be called on the client, but  
executed on the server:  
UFUNCTION(Server)  
void ServerRPCFunction();  
To declare a function as an RPC that will be called from the server, and  
then executed on the server and on all connected clients:  
UFUNCTION(NetMulticast)  
void MulticastRPCFunction();  
RPC Validation  
The validation function for an RPC allows the detection of bad  
parameters or cheating:  
It can notify the system to disconnect the client who initiated the call.  
Example:  
UFUNCTION(Server, WithValidation)  
void SomeRPCFunction(int32 AddHealth);  
bool SomeRPCFunction_Validate(int32 AddHealth){  
if (AddHealth > MAX_ADD_HEALTH){  
return false;  
}
return true;  
}
void SomeRPCFunction_Implementation(int32 AddHealth){  
Health += AddHealth;  
}
Back to the First Game  
Can we transform our first game into a multiplayer game?  
Yes! We just need to fix some small issues.  
Test the game in a multiplayer setup:  
Two players  
Back to the First Game  
What works in multiplayer:  
1
2
3
. Player movement;  
. Zombie movement;  
. Coins;  
What doesn’t work in multiplayer?  
1. No game mode in the client and no total number of collected coins;  
2. End level area and the game over messages;  
3. Enemy animation and walk speed;  
Back to the First Game  
First issue: there is no game mode in clients.  
Solution:  
Logic change: now multiple players can collect coins cooperatively, so its  
more appropriate to show the number of remaining coins in the level;  
Move the function that computes the number of coins in the level to  
the character class (will be locally computed);  
Change the blueprint that access the game mode to show the number  
collected coins;  
Back to the First Game  
Second issue: end level area and the game over messages.  
Problem 1: disable the player controller on all clients/server  
Issue: the CompleteLevel function runs only on the server, because it is implemented  
in the Game Mode class.  
Solution:  
Create a multicast function in a Game State class;  
Back to the First Game  
Second issue: end level area and the game over messages.  
Problem 1: disable the player controller on all clients/server  
Issue: the CompleteLevel function runs only on the server, because it is implemented  
in the Game Mode class.  
Solution:  
Create a multicast function in a Game State class;  
public:  
UFUNCTION(NetMulticast, Reliable)  
MyGameStateBase.h  
void MulticastOnLevelComplete(APawn* character, bool succeeded);  
void AMyGameStateBase::MulticastOnLevelComplete_Implementation(  
APawn* character, bool succeeded){  
for (FConstPawnIterator it = GetWorld()->GetPawnIterator(); it; it++){  
APawn* pawn = it->Get();  
if (pawn && pawn->IsLocallyControlled()){  
pawn->DisableInput(nullptr);  
}
MyGameStateBase.cpp  
}
Back to the First Game  
Second issue: end level area and the game over messages.  
Problem 1: disable the player controller on all clients/server  
Issue: the CompleteLevel function runs only on the server, because it is implemented  
in the Game Mode class.  
Solution:  
Set the new Game State class in the blueprint instance of the Game  
Mode:  
Back to the First Game  
Second issue: end level area and the game over messages.  
Problem 1: disable the player controller on all clients/server  
Issue: the CompleteLevel function runs only on the server, because it is implemented  
in the Game Mode class.  
Solution:  
Call the multicast function in CompleteLevel function;  
...  
MyFirstGameGameMode.cpp  
AMyGameStateBase *gameState = GetGameState<AMyGameStateBase>();  
if (gameState)  
{
gameState->MulticastOnLevelComplete(character, succeeded);  
}
...  
Back to the First Game  
Second issue: end level area and the game over messages.  
Problem 2: show the “Level Completed Message” for all clients/server.  
Issue: we need a class to create the widget that exist only once on the clients.  
Solution:  
Create the widget in a new Game Controller class;  
Back to the First Game  
Second issue: end level area and the game over messages.  
Problem 2: show the “Level Completed Message” for all clients/server.  
Issue: we need a class to create the widget that exist only once on the clients.  
Solution:  
Expose the OnLevelCompleted to blueprint implementation:  
MyPlayerController.h  
public:  
UFUNCTION(BlueprintImplementableEvent, Category = "Gameplay Events")  
void OnLevelCompleted(APawn* charact, bool succeeded);  
Create a blueprint for the new player controller and implement the event:  
Back to the First Game  
Second issue: end level area and the game over messages.  
Problem 2: show the “Level Completed Message” for all clients/server.  
Issue: we need a class to create the widget that exist only once on the clients.  
Solution:  
Call the OnLevelCompleted function in the Multicast function:  
void AMyGameStateBase::MulticastOnLevelComplete_Implementation(  
APawn* character, bool succeeded){  
...  
for (FConstPlayerControllerIterator it =  
GetWorld()->GetPlayerControllerIterator(); it; it++){  
AMyPlayerController* pController = Cast<AMyPlayerController>  
(it->Get());  
if ((pController) && (pController->IsLocalController())){  
pController->OnLevelCompleted(character, succeeded);  
}
}
}
MyGameStateBase.cpp  
Back to the First Game  
Second issue: end level area and the game over messages.  
Problem 2: show the “Level Completed Message” for all clients/server.  
Issue: we need a class to create the widget that exist only once on the clients.  
Solution:  
Set the new Player Controller class in the blueprint instance of the Game  
Mode:  
Back to the First Game  
Second issue: end level area and the game over messages.  
Problem 3: move the camera to spectator viewpoint in all clients/server.  
Solution:  
Call the SetViewTargetWithBlend function for all player controllers:  
void AMyFirstGameGameMode::CompleteLevel(APawn* character,  
bool succeeded){  
...  
for (FConstPlayerControllerIterator it =  
GetWorld()->GetPlayerControllerIterator(); it; it++){  
APlayerController* playerController = it->Get();  
if (playerController){  
playerController->SetViewTargetWithBlend(foundViewpoints[0],0.8f);  
}
}
...  
MyFirstGameGameMode.cpp  
}
Back to the First Game  
Third issue: enemy animation and walk speed.  
Problem: synchronize the animation and walk speed with all  
clients/server.  
Solution:  
Create a variable to represent a chasing state and replicate it to all clients:  
protected:  
...  
EnemyCharacter.h  
UPROPERTY(Replicated)  
bool isChasing;  
void AEnemyCharacter::GetLifetimeReplicatedProps(Tarray  
EnemyCharacter.cpp  
<FLifetimeProperty>& OutLifetimeProps) const  
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);  
DOREPLIFETIME(AEnemyCharacter, isChasing);  
EnemyCharacter.cpp  
}
Back to the First Game  
AEnemyCharacter::AEnemyCharacter(){  
...  
EnemyCharacter.cpp  
isChasing = false;  
SetReplicates(true);  
}
void AEnemyCharacter::EnemyComponentHit(UPrimitiveComponent*  
HitComponent, AActor* OtherActor, UPrimitiveComponent*  
OtherComp, FVector NormalImpulse, const FHitResult& Hit){  
if (Role == ROLE_Authority){  
SeePlayer(Cast<APawn>(OtherActor));  
}
}
void AEnemyCharacter::SeePlayer(APawn *pawn){  
if ((pawn) && (AIController) && (!target)){  
...  
isChasing = true;  
}
}
Back to the First Game  
void AEnemyCharacter::Tick(float DeltaTime){  
Super::Tick(DeltaTime);  
EnemyCharacter.cpp  
if ((target)&& (Role == ROLE_Authority)){  
if (FVector::Dist(GetActorLocation(),  
target->GetActorLocation()) > SensingComponent->SightRadius){  
..  
isChasing = false;  
.
}
}
if ((isChasing)&& (this->GetMesh()->GlobalAnimRateScale != 2.5f)){  
this->GetMesh()->GlobalAnimRateScale = 2.5f;  
this->GetCharacterMovement()->MaxWalkSpeed = 150.0f;  
}
else if (this->GetMesh()->GlobalAnimRateScale != 1.0f){  
this->GetMesh()->GlobalAnimRateScale = 1.0f;  
this->GetCharacterMovement()->MaxWalkSpeed = 50.0f;  
}
}
Back to the First Game  
Fourth issue: runtime error message.  
Error: Blueprint Runtime Error: "Accessed None trying to read property  
CallFunc_Create_ReturnValue".  
Solution:  
A small change in the ThirdPersonCharacter blueprint solves the issue:  
New Multiplayer Game  
Concept: 3rd person shooter multiplayer game.  
Gameplay elements:  
Player Character: can walk, jump, crunch,  
and shoot;  
Weapon: used by the player character to  
shoot other players;  
Player Health: when hit by a shot, the  
player character losses a specific amount  
of health points;  
Death: after losing all health points, the  
player character dies;  
Multiplayer Game  
Create a new C++ empty project:  
Multiplayer Game  
Create a new character class:  
Multiplayer Game  
Implement the character movement:  
protected:  
void MoveForward(float value);  
void MoveRight(float value);  
MyCharacter.h  
void AMyCharacter::MoveForward(float value){  
AddMovementInput(GetActorForwardVector(), value);  
MyCharacter.cpp  
}
void AMyCharacter::MoveRight(float value){  
AddMovementInput(GetActorRightVector(), value);  
}
void AMyCharacter::SetupPlayerInputComponent(UInputComponent*  
PlayerInputComponent){  
Super::SetupPlayerInputComponent(PlayerInputComponent);  
PlayerInputComponent->BindAxis("MoveForward", this,  
&
AMyCharacter::MoveForward);  
PlayerInputComponent->BindAxis("MoveRight", this,  
AMyCharacter::MoveRight);  
&
}
Multiplayer Game  
Setup the axis keys in the project settings:  
Multiplayer Game  
Create a blueprint for the character class and test the player  
movement in the level:  
Multiplayer Game  
Implement the camera movement and setup the axis keys:  
MyCharacter.cpp  
void AMyCharacter::SetupPlayerInputComponent(UInputComponent*  
PlayerInputComponent){  
...  
PlayerInputComponent->BindAxis("LookUp", this,  
&AMyCharacter::AddControllerPitchInput);  
PlayerInputComponent->BindAxis("Turn", this,  
&AMyCharacter::AddControllerYawInput);  
}
Multiplayer Game  
Create a 3rd person camera and a spring arm components in  
the character class:  
MyCharacter.h  
protected:  
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")  
class UCameraComponent * CameraComponent;  
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")  
class USpringArmComponent * SpringArmComponent;  
AMyCharacter::AMyCharacter(){  
PrimaryActorTick.bCanEverTick = true;  
MyCharacter.cpp  
SpringArmComponent = CreateDefaultSubobject<USpringArmComponent>  
"SpringArm Component");  
(
SpringArmComponent->bUsePawnControlRotation = true;  
SpringArmComponent->SetupAttachment(RootComponent);  
CameraComponent = CreateDefaultSubobject<UCameraComponent>  
("Camera Component");  
CameraComponent->SetupAttachment(SpringArmComponent);  
}
Multiplayer Game  
Download and import the Animation Starter Pack:  
https://www.unrealengine.com/marketplace/animation-starter-pack  
Multiplayer Game  
Add and setup the model mesh in the character blueprint:  
Multiplayer Game  
Clear the animation blueprint (UE4ASP_HeroTPP_AnimBlueprint)  
and setup the animation in the character blueprint.  
Multiplayer Game  
Implement the crouch action:  
protected:  
...  
MyCharacter.h  
void BeginCrouch();  
void EndCrouch();  
void AMyCharacter::BeginCrouch(){  
Crouch();  
}
MyCharacter.cpp  
void AMyCharacter::EndCrouch(){  
UnCrouch();  
}
AMyCharacter::AMyCharacter(){  
...  
GetMovementComponent()->GetNavAgentPropertiesRef().bCanCrouch = true;  
}
Multiplayer Game  
Implement the crouch action:  
MyCharacter.cpp  
void AMyCharacter::SetupPlayerInputComponent(UInputComponent*  
PlayerInputComponent){  
...  
PlayerInputComponent->BindAction("Crouch", IE_Pressed, this,  
AMyCharacter::BeginCrouch);  
PlayerInputComponent->BindAction("Crouch", IE_Released, this,  
AMyCharacter::EndCrouch);  
&
&
}
Multiplayer Game  
Set the crouch variable in the animation blueprint:  
Multiplayer Game  
Implement the jump action:  
MyCharacter.cpp  
void AMyCharacter::SetupPlayerInputComponent(UInputComponent*  
PlayerInputComponent){  
...  
PlayerInputComponent->BindAction("Jump", IE_Pressed, this,  
&AMyCharacter::Jump);  
}
Multiplayer Game  
Implement the jump action:  
Multiplayer Game  
Setup the game to be played in multiplayer:  
1
. Delete the character from the map and add two  
or more “Player Start” actors to the map.  
2
. Create a new Game Mode blueprint and set our  
character blueprint as default pawn class.  
3
. Set the new Game Mode as the Game Mode for  
the map in the World Settings.  
Multiplayer Game  
Add a weapon: import the weapon mesh  
http://www.inf.puc-rio.br/~elima/dp/weapon.zip  
Multiplayer Game  
Create a C++ class for the weapon:  
Multiplayer Game  
Implement the weapon:  
protected:  
MyWeapon.h  
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")  
class USkeletalMeshComponent* MeshComponent;  
AMyWeapon::AMyWeapon()  
{
MyWeapon.cpp  
PrimaryActorTick.bCanEverTick = true;  
MeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>  
("Mesh Component");  
RootComponent = MeshComponent;  
}
Multiplayer Game  
Create a blueprint for the weapon class and select the  
weapon mesh:  
Multiplayer Game  
Spawn the weapon and attach it to the character hand in the  
BeginPlay event of the character blueprint:  
protected:  
class AMyWeapon* CurrentWeapon;  
MyCharacter.h  
UPROPERTY(EditDefaultsOnly, Category = "Weapon")  
TSubclassOf<AMyWeapon> WeaponClass;  
Multiplayer Game  
Spawn the weapon and attach it to the character hand in the  
BeginPlay event of the character blueprint:  
void AMyCharacter::BeginPlay(){  
Super::BeginPlay();  
MyCharacter.cpp  
if (WeaponClass){  
FActorSpawnParameters parameters;  
parameters.SpawnCollisionHandlingOverride =  
ESpawnActorCollisionHandlingMethod::AlwaysSpawn;  
CurrentWeapon = GetWorld()->SpawnActor<AMyWeapon>(WeaponClass,  
FVector::ZeroVector, FRotator::ZeroRotator,  
parameters);  
if (CurrentWeapon){  
CurrentWeapon->AttachToComponent(GetMesh(),  
FAttachmentTransformRules::SnapToTargetNotIncludingScale,  
"WeaponSocket");  
CurrentWeapon->SetOwner(this);  
}
}
}
Multiplayer Game  
Create a socket (“WeaponSocket”) in the skeleton of the  
model to correctly attach the weapon to the character’s hand:  
Multiplayer Game  
Add the weapon as a “Preview Asset” into the socket and  
adjust its position to the character’s hand:  
Multiplayer Game  
Implement the code to shoot with the weapon:  
protected:  
MyWeapon.h  
...  
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")  
TSubclassOf<UDamageType> DamageType;  
.
..  
public:  
..  
void Fire();  
.
Multiplayer Game  
Implement the code to shoot with the weapon:  
void AMyWeapon::Fire()  
{
MyWeapon.cpp  
AActor* weaponOwner = GetOwner();  
if (weaponOwner)  
{
FVector eyeLocation;  
FRotator eyeRotation;  
weaponOwner->GetActorEyesViewPoint(eyeLocation, eyeRotation);  
FVector endPoint = eyeLocation + (eyeRotation.Vector() * 1000);  
FCollisionQueryParams cparams;  
cparams.AddIgnoredActor(weaponOwner);  
cparams.AddIgnoredActor(this);  
cparams.bTraceComplex = true;  
...  
Multiplayer Game  
Implement the code to shoot with the weapon:  
...  
MyWeapon.cpp  
FHitResult hit;  
if (GetWorld()->LineTraceSingleByChannel(hit, eyeLocation, endPoint,  
ECC_Visibility, cparams)){  
AActor* hitActor = hit.GetActor();  
UGameplayStatics::ApplyPointDamage(hitActor, 10.0f,  
eyeRotation.Vector(), hit,  
weaponOwner->GetInstigatorController(),  
this, DamageType);  
}
DrawDebugLine(GetWorld(), eyeLocation, endPoint, FColor::Red,  
false, 1.0f, 0, 1);  
}
}
Multiplayer Game  
Implement the code to shoot with the weapon:  
protected:  
..  
void Fire();  
..  
public:  
..  
virtual FVector GetPawnViewLocation() const override;  
MyCharacter.h  
.
.
.
Multiplayer Game  
Implement the code to shoot with the weapon:  
void AMyCharacter::Fire()  
{
MyCharacter.cpp  
if (CurrentWeapon)  
{
CurrentWeapon->Fire();  
}
}
void AMyCharacter::SetupPlayerInputComponent(UInputComponent*  
PlayerInputComponent){  
...  
PlayerInputComponent->BindAction("Fire", IE_Pressed, this,  
&AMyCharacter::Fire);  
}
Multiplayer Game  
Implement the code to shoot with the weapon:  
FVector AMyCharacter::GetPawnViewLocation() const  
{
MyCharacter.cpp  
if (CameraComponent)  
{
return CameraComponent->GetComponentLocation();  
}
return Super::GetPawnViewLocation();  
}
Multiplayer Game  
Add particle effects to the weapon: import the particle effects  
http://www.inf.puc-rio.br/~elima/dp/weapon_effects.zip  
Multiplayer Game  
Implement the code spawn the particle systems:  
protected:  
MyWeapon.h  
...  
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")  
UParticleSystem* FireEffect;  
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")  
UParticleSystem* ImpactEffect;  
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")  
UParticleSystem* TraceEffect;  
void PlayFireEffects(FVector endPoint);  
void PlayImpactEffects(FVector impactPoint);  
...  
Multiplayer Game  
Implement the code spawn the particle systems:  
void AMyWeapon::PlayFireEffects(FVector endPoint)  
{
MyWeapon.cpp  
if (FireEffect)  
{
UGameplayStatics::SpawnEmitterAttached(FireEffect, MeshComponent,  
"MuzzleFlashSocket");  
}
if (TraceEffect)  
{
UParticleSystemComponent* instTrace =  
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), TraceEffect,  
MeshComponent->GetSocketLocation("MuzzleFlashSocket"));  
instTrace->SetVectorParameter("Target", endPoint);  
}
}
Multiplayer Game  
Implement the code spawn the particle systems:  
void AMyWeapon::Fire(){  
..  
FHitResult hit;  
MyWeapon.cpp  
.
if (GetWorld()->LineTraceSingleByChannel(hit, eyeLocation, endPoint,  
ECC_Visibility, cparams)){  
...  
PlayImpactEffects(hit.ImpactPoint);  
endPoint = hit.ImpactPoint;  
}
PlayFireEffects(endPoint);  
}
Multiplayer Game  
Setup the weapon effects:  
Multiplayer Game  
Adjust the camera position and rotation in the character  
blueprint for a better view:  
Multiplayer Game  
Add a crosshair to the center of the screen: create a widget  
blueprint with a crosshair image at the center.  
Multiplayer Game  
Add a crosshair to the center of the screen: instantiate the  
widget in the BeginPlay event of the character.  
Multiplayer Game  
Setup the weapons for multiplayer: only the server should  
create the weapons, but currently the weapons are being  
created in the BeginPlay event, which also runs on clients.  
void AMyCharacter::BeginPlay(){  
Super::BeginPlay();  
MyCharacter.cpp  
if ((Role == ROLE_Authority)&&(WeaponClass)){  
FActorSpawnParameters parameters;  
parameters.SpawnCollisionHandlingOverride =  
ESpawnActorCollisionHandlingMethod::AlwaysSpawn;  
CurrentWeapon = GetWorld()->SpawnActor<AMyWeapon>(WeaponClass,  
FVector::ZeroVector, FRotator::ZeroRotator, parameters);  
if (CurrentWeapon){  
CurrentWeapon->AttachToComponent(GetMesh(),  
FAttachmentTransformRules::SnapToTargetNotIncludingScale,  
"WeaponSocket");  
CurrentWeapon->SetOwner(this);  
}
}
}
Multiplayer Game  
Setup the weapons for multiplayer:  
AMyWeapon::AMyWeapon()  
{
MyWeapon.cpp  
MyCharacter.h  
...  
SetReplicates(true);  
}
protected:  
..  
.
UPROPERTY(Replicated)  
class AMyWeapon* CurrentWeapon;  
...  
Multiplayer Game  
Setup the weapons for multiplayer:  
#include "Net/UnrealNetwork.h"  
MyCharacter.cpp  
...  
void AEnemyCharacter::GetLifetimeReplicatedProps(  
TArray<FLifetimeProperty>& OutLifetimeProps) const  
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);  
DOREPLIFETIME(AEnemyCharacter, isChasing);  
}
Multiplayer Game  
When a client fires the weapon, the Fire function must be  
called on the client and on the server:  
proteceted:  
UFUNCTION(Server, Reliable, WithValidation)  
void ServerFire();  
MyWeapon.h  
void AMyWeapon::ServerFire_Implementation()  
{
MyWeapon.cpp  
Fire();  
}
bool AMyWeapon::ServerFire_Validate()  
{
return true;  
}
Multiplayer Game  
When a client fires the weapon, the Fire function must be  
called on the client and on the server:  
void AMyWeapon::Fire()  
{
MyWeapon.cpp  
if (Role != ROLE_Authority)  
{
ServerFire();  
}
...  
Multiplayer Game  
The server must replicate the hit points to all clients (except  
to the one that fired):  
protected:  
MyWeapon.h  
...  
UPROPERTY(ReplicatedUsing=OnReplicateHitPoint)  
FVector_NetQuantize HitPoint;  
UFUNCTION()  
void OnReplicateHitPoint();  
...  
Multiplayer Game  
The server must replicate the hit points to all clients (except  
to the one that fired):  
MyWeapon.cpp  
void AMyWeapon::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>&  
OutLifetimeProps) const {  
Super::GetLifetimeReplicatedProps(OutLifetimeProps);  
DOREPLIFETIME_CONDITION(AMyWeapon, HitPoint, COND_SkipOwner);  
}
void AMyWeapon::OnReplicateHitPoint()  
{
PlayFireEffects(HitPoint);  
PlayImpactEffects(HitPoint);  
}
Multiplayer Game  
The server must replicate the hit points to all clients (except  
to the one that fired):  
void AMyWeapon::Fire(){  
if (Role != ROLE_Authority){  
ServerFire();  
MyWeapon.cpp  
}
AActor* weaponOwner = GetOwner();  
if (weaponOwner){  
FVector eyeLocation;  
FRotator eyeRotation;  
weaponOwner->GetActorEyesViewPoint(eyeLocation, eyeRotation);  
FVector endPoint = eyeLocation + (eyeRotation.Vector() * 1000);  
FCollisionQueryParams cparams;  
cparams.AddIgnoredActor(weaponOwner);  
cparams.AddIgnoredActor(this);  
cparams.bTraceComplex = true;  
...  
Multiplayer Game  
The server must replicate the hit points to all clients (except  
to the one that fired):  
.
FHitResult hit;  
..  
MyWeapon.cpp  
if (GetWorld()->LineTraceSingleByChannel(hit, eyeLocation, endPoint,  
ECC_Visibility, cparams)){  
AActor* hitActor = hit.GetActor();  
UGameplayStatics::ApplyPointDamage(hitActor, 10.0f, eyeRotation.  
Vector(), hit, weaponOwner->GetInstigatorController(),  
this, DamageType);  
PlayImpactEffects(hit.ImpactPoint);  
endPoint = hit.ImpactPoint;  
}
PlayFireEffects(endPoint);  
if (Role == ROLE_Authority){  
HitPoint = endPoint;  
}
}
}
Multiplayer Game  
Test the game:  
Multiplayer Game  
Create a new Component class (C++) to implement the Health  
Component:  
Multiplayer Game  
Implement the Health Component:  
MyHealthComponent.h  
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature,  
UMyHealthComponent*, HealthComponent, float, health, float,  
damage, const class UDamageType*, DamageType, class  
AController*, InstigatedBy, AActor*, DamageCauser);  
...  
protected:  
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Health")  
float Health;  
UFUNCTION()  
void OnTakeDamage(AActor* DamagedActor, float Damage, const class  
UDamageType* DamageType, class AController* InstigatedBy,  
AActor* DamageCauser);  
.
..  
public:  
UPROPERTY(BlueprintAssignable, Category = "Events")  
FOnHealthChangedSignature OnHealthChangedEvent;  
Multiplayer Game  
Implement the Health Component:  
UMyHealthComponent::UMyHealthComponent(){  
Health = 100;  
MyHealthComponent.cpp  
}
void UMyHealthComponent::BeginPlay(){  
Super::BeginPlay();  
AActor* myOwner = GetOwner();  
if (myOwner){  
myOwner->OnTakeAnyDamage.AddDynamic(this,  
&UMyHealthComponent::OnTakeDamage);  
}
}
void UMyHealthComponent::OnTakeDamage(AActor* DamagedActor, float  
Damage, const class UDamageType* DamageType, class  
AController* InstigatedBy, AActor* DamageCauser){  
Health = Health - Damage;  
if (Health < 0)  
Health = 0;  
OnHealthChangedEvent.Broadcast(this, Health, Damage, DamageType,  
InstigatedBy, DamageCauser);  
}
Multiplayer Game  
Add the Health Component to the character:  
...  
MyCharacter.h  
protected:  
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")  
bool IsDead;  
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")  
class UMyHealthComponent * HealthComponent;  
...  
UFUNCTION()  
void OnHealthChanged(class UMyHealthComponent* healthComp,  
float health, float damage, const class UDamageType*  
DamageType, class AController* InstigatedBy, AActor*  
DamageCauser);  
...  
Multiplayer Game  
Add the Health Component to the character:  
AMyCharacter::AMyCharacter()  
{
MyCharacter.cpp  
...  
HealthComponent = CreateDefaultSubobject<UMyHealthComponent>  
("Health Component");  
HealthComponent->OnHealthChangedEvent.AddDynamic(this,  
&AMyCharacter::OnHealthChanged);  
...  
}
void AMyCharacter::BeginPlay()  
{
Super::BeginPlay();  
IsDead = false;  
...  
}
Multiplayer Game  
Add the Health Component to the character:  
MyCharacter.cpp  
void AMyCharacter::OnHealthChanged(UMyHealthComponent* HealthComponent,  
float health, float damage, const class UDamageType*  
DamageType, class AController* InstigatedBy, AActor*  
DamageCauser){  
if ((health <= 0)&&(!IsDead))  
{
IsDead = true;  
GetMovementComponent()->StopMovementImmediately();  
GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::  
NoCollision);  
}
}
Multiplayer Game  
Implement the death animation logic:  
Multiplayer Game  
Implement the death animation logic:  
Multiplayer Game  
Replicate the health over the network:  
MyHealthComponent.h  
protected:  
UPROPERTY(Replicated, EditDefaultsOnly, BlueprintReadOnly,  
Category = "Health")  
float Health;  
MyHealthComponent.cpp  
UMyHealthComponent::UMyHealthComponent(){  
..  
SetIsReplicated(true);  
.
}
void UMyHealthComponent::BeginPlay(){  
Super::BeginPlay();  
if (GetOwnerRole() == ROLE_Authority) {  
AActor* myOwner = GetOwner();  
if (myOwner){  
myOwner->OnTakeAnyDamage.AddDynamic(this,  
&UMyHealthComponent::OnTakeDamage);  
}
}
}
Multiplayer Game  
Replicate the health over the network:  
MyHealthComponent.cpp  
void UMyHealthComponent::GetLifetimeReplicatedProps(Tarray  
<FLifetimeProperty>& OutLifetimeProps) const  
{
}
Super::GetLifetimeReplicatedProps(OutLifetimeProps);  
DOREPLIFETIME(UMyHealthComponent, Health);  
Multiplayer Game  
Replicate the death animation over the network:  
MyCharacter.h  
protected:  
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Player")  
bool IsDead;  
MyCharacter.cpp  
void AMyCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>&  
OutLifetimeProps) const {  
Super::GetLifetimeReplicatedProps(OutLifetimeProps);  
DOREPLIFETIME(AMyCharacter, CurrentWeapon);  
DOREPLIFETIME(AMyCharacter, IsDead);  
}
Multiplayer Game  
Test the results:  
Exercise 1  
1
) Continue the development of the multiplayer game and add  
the following features:  
Display the player health on the HUD;  
When the player is killed, display a “game over” message;  
When all other players are killed, display a “you win” message;  
Further Reading  
Carnall, B. (2016). Unreal Engine 4.X By Example. Packt  
Publishing. ISBN: 978-1785885532.  
Web Resources:  
Networking and Multiplayer in Unreal Engine -  
https://docs.unrealengine.com/en-us/Gameplay/Networking  
Network Guide -  
https://wiki.unrealengine.com/index.php?title=4_13%2B_Network_  
Guide