Open the protagonist class, the code logic for generating the gun is in the game start function
So before generating, we need to judge whether the object is on the server side (server side perspective)
void ASCharacter::BeginPlay()
{
Super::BeginPlay();
DefaultsFOV = CameraComp->FieldOfView;
//判断是否在服务器端
if (Role == ROLE_Authority)
{
//设置生成参数,当生成的actor碰到了其他物体,也要生成
FActorSpawnParameters Parameters;
Parameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
//生成武器actor(类型、位置、方向、参数),并且其地址赋予到指针上
CurrentWeapen1 = GetWorld()->SpawnActor<ASWeapen>(StartWeapen, FVector::ZeroVector, FRotator::ZeroRotator, Parameters);
//设置武器的位置与骨骼的插槽中,并设置主人
if (CurrentWeapen1)
{
CurrentWeapen1->SetOwner(this);
CurrentWeapen1->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponAttachSoketName);
}
}
//受伤自定义事件绑定
HealthComp->OnHealthChanged.AddDynamic(this, &ASCharacter::OnHealthChanged);
}
Compile it and see what the effect is
We found that the one on the left had weapons and the one on the right had no weapons
So we're going to make weapons network-replicable
Open the weapon class and set it in the constructor
//设置可以进行网络复制
SetReplicates(true);
Compile, then open the blueprint of the gun and tick it
This time they both have guns
Although there is a gun now, the player on the left can shoot, but the character on the right cannot shoot
This is because the right character, the pointer to the weapon is null
So we need to make the weapon pointers synchronized so that they can be synchronized
//目前玩家手中的武器
UPROPERTY(Replicated)
class ASWeapen * CurrentWeapen1;
To achieve network synchronization, there must be a function, which is defined in the actor class
We copy it into the player class
//用于网络同步的函数
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
import header file
#include "Net/UnrealNetwork.h"
define this function
void ASCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//同步给所有的客户端和服务器
DOREPLIFETIME(ASCharacter, CurrentWeapen1);
}
Test, both characters can shoot guns, but they can't see each other.
Let's solve this problem.
==========================================
Add a member function serverfire() to the weapon class
UFUNCTION(Server, Reliable, WithValidation)
void ServerFire();
Then the way to implement this function is quite special
void ASWeapen::ServerFire_Implementation()
{
Fire();
}
bool ASWeapen::ServerFire_Validate()
{
return true;
}
Then we modify the Fire() function
void ASWeapen::Fire()
{
//如果不是服务器,就执行ServerFire()
if (Role < ROLE_Authority)
{
ServerFire();
return;
}
//创建一个撞击句柄,用来获取弹道相关信息
FHitResult Hit;
//弹道的起点,我们设置为角色眼睛的位置
AActor * MyOwner = GetOwner();
if (MyOwner)
{ //位置
FVector EyeLocation;
//方向
FRotator EyeRotator;
//得到眼睛的位置和角度
MyOwner->GetActorEyesViewPoint(EyeLocation, EyeRotator);
//弹道的终点就是起点+方向*10000
FVector TraceEnd = EyeLocation + (EyeRotator.Vector() * 1000);
//弹道特效的结束点
FVector TraceEndPoint = TraceEnd;
//设置碰撞通道为可见性通道
FCollisionQueryParams QueryParams;
//让射线忽略玩家和枪
QueryParams.AddIgnoredActor(MyOwner);
QueryParams.AddIgnoredActor(this);
//符合追踪设为true,可以让射击更加精准
QueryParams.bTraceComplex = true;
//返回命中目标的表面材质
QueryParams.bReturnPhysicalMaterial = true;
//在创建一个单轨迹线来计算弹道
//LineTraceSingleByChannel击中物体返回true
if (GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEnd, COLLISION_WEAPON, QueryParams))
{
//命中对象
AActor * HitActor = Hit.GetActor();
//实际的伤害
float ActualDamage = BaseDamage;
//得到命中物体表面材质
EPhysicalSurface SurfaceType = UPhysicalMaterial::DetermineSurfaceType(Hit.PhysMaterial.Get());
//如果命中的是头部表面材质,伤害变成四倍
if (SurfaceType == SURFACE_FLESHVULNERABLE)
{
ActualDamage *= 4;
}
//造成点伤害ApplyPointDamage
//参数分别为命中对象、基础伤害、射击方向、命中信息(命中句柄)、MyOwner->GetInstigatorController(暂时不了解)
//this(射击者) 和伤害类型
UGameplayStatics::ApplyPointDamage(HitActor, ActualDamage, EyeRotator.Vector(), Hit, MyOwner->GetInstigatorController(), this, DamageType);
//根据材质的不同,进行不同的处理
UParticleSystem * SelectedEffect = nullptr;
switch (SurfaceType)
{
//这两种情况是一个效果
case SURFACE_FLESHDEFAULT:
case SURFACE_FLESHVULNERABLE:
SelectedEffect = FleshImpactEffect;
break;
default:
SelectedEffect = DefaultImpactEffect;
break;
}
//生成特效在命中点
//ImpactEffect:特效 ImpactPoint:打击点 Rotation():打击方向
if (SelectedEffect)
{
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), SelectedEffect, Hit.ImpactPoint, Hit.ImpactNormal.Rotation());
}
//命中的时候,修改弹道特效的终点
TraceEndPoint = Hit.ImpactPoint;
}
//方便debug
//DrawDebugLine(GetWorld(), EyeLocation, TraceEnd, FColor::Red, false, 1, 0, 1);
//射击特效
PlayFireEffects(TraceEndPoint);
//最后开火的时间
FireLastTime = GetWorld()->TimeSeconds;
}
}
Compile, test, fire on the server side, but the client cannot see it (no matter which character is controlled, the fire is seen on the server side)
Modify the code, we remove the return
compile test
The server fires, but the client cannot see it; the client fires, but can see it at the same time
Add two nodes to the character blueprint
===========================================
at present
Operate the client, and you can see special effects at both ends
Operate the server side, but the client side cannot see the special effects
In the header file of the weapon class, create the structure
USTRUCT()
struct FHitScanTrace
{
GENERATED_BODY()
public:
//弹道的目的坐标
UPROPERTY()
FVector_NetQuantize TraceTo;
//子弹数目:为了让该结构体内容发生变化,结构体才被不断得被网络复制
UPROPERTY()
uint8 BrustCounter;
};
Create the structure member variables in this class, and the corresponding network copy function
//网络射击信息
UPROPERTY(ReplicatedUsing = OnRep_HitScanTrace)
FHitScanTrace HitScanTrace;
//网络复制函数
UFUNCTION()
void OnRep_HitScanTrace();
When the content of the structure changes, the network replication function will be called automatically
In the fire function, update the network shooting information
//更新网络射击信息
if (Role == ROLE_Authority)
{
//子弹数量++
HitScanTrace.BrustCounter++;
//更新弹道目的坐标
HitScanTrace.TraceTo = TraceEndPoint;
}
void ASWeapen::Fire()
{
//如果不是服务器,就执行ServerFire()
if (Role < ROLE_Authority)
{
ServerFire();
}
//创建一个撞击句柄,用来获取弹道相关信息
FHitResult Hit;
//弹道的起点,我们设置为角色眼睛的位置
AActor * MyOwner = GetOwner();
if (MyOwner)
{ //位置
FVector EyeLocation;
//方向
FRotator EyeRotator;
//得到眼睛的位置和角度
MyOwner->GetActorEyesViewPoint(EyeLocation, EyeRotator);
//弹道的终点就是起点+方向*10000
FVector TraceEnd = EyeLocation + (EyeRotator.Vector() * 1000);
//弹道特效的结束点
FVector TraceEndPoint = TraceEnd;
//设置碰撞通道为可见性通道
FCollisionQueryParams QueryParams;
//让射线忽略玩家和枪
QueryParams.AddIgnoredActor(MyOwner);
QueryParams.AddIgnoredActor(this);
//符合追踪设为true,可以让射击更加精准
QueryParams.bTraceComplex = true;
//返回命中目标的表面材质
QueryParams.bReturnPhysicalMaterial = true;
//在创建一个单轨迹线来计算弹道
//LineTraceSingleByChannel击中物体返回true
if (GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEnd, COLLISION_WEAPON, QueryParams))
{
//命中对象
AActor * HitActor = Hit.GetActor();
//实际的伤害
float ActualDamage = BaseDamage;
//得到命中物体表面材质
EPhysicalSurface SurfaceType = UPhysicalMaterial::DetermineSurfaceType(Hit.PhysMaterial.Get());
//如果命中的是头部表面材质,伤害变成四倍
if (SurfaceType == SURFACE_FLESHVULNERABLE)
{
ActualDamage *= 4;
}
//造成点伤害ApplyPointDamage
//参数分别为命中对象、基础伤害、射击方向、命中信息(命中句柄)、MyOwner->GetInstigatorController(暂时不了解)
//this(射击者) 和伤害类型
UGameplayStatics::ApplyPointDamage(HitActor, ActualDamage, EyeRotator.Vector(), Hit, MyOwner->GetInstigatorController(), this, DamageType);
//根据材质的不同,进行不同的处理
UParticleSystem * SelectedEffect = nullptr;
switch (SurfaceType)
{
//这两种情况是一个效果
case SURFACE_FLESHDEFAULT:
case SURFACE_FLESHVULNERABLE:
SelectedEffect = FleshImpactEffect;
break;
default:
SelectedEffect = DefaultImpactEffect;
break;
}
//生成特效在命中点
//ImpactEffect:特效 ImpactPoint:打击点 Rotation():打击方向
if (SelectedEffect)
{
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), SelectedEffect, Hit.ImpactPoint, Hit.ImpactNormal.Rotation());
}
//命中的时候,修改弹道特效的终点
TraceEndPoint = Hit.ImpactPoint;
}
//方便debug
//DrawDebugLine(GetWorld(), EyeLocation, TraceEnd, FColor::Red, false, 1, 0, 1);
//射击特效
PlayFireEffects(TraceEndPoint);
//更新网络射击信息
if (Role == ROLE_Authority)
{
//子弹数量++
HitScanTrace.BrustCounter++;
//更新弹道目的坐标
HitScanTrace.TraceTo = TraceEndPoint;
}
//最后开火的时间
FireLastTime = GetWorld()->TimeSeconds;
}
}
Define the network replication function and let it execute the shooting effect
void ASWeapen::OnRep_HitScanTrace()
{
//调用射击特效
PlayFireEffects(HitScanTrace.TraceTo);
}
At this time, the network shooting information structure variable has not yet realized the replication and sharing of the network, so we let it be shared.
Create a function that copies member variables
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
define this function
void ASWeapen::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//同步给所有的客户端和服务器(DOREPLIFETIME_CONDITION不用同步给自己)
DOREPLIFETIME_CONDITION(ASWeapen, HitScanTrace,COND_SkipOwner);
}
compile, test
When the server shoots, the client can see the special effect of the ballistic, but not the special effect of the hit
============================================
solve this problem now
Add surface material member variable for web shot structure
//表面材质
UPROPERTY()
TEnumAsByte<EPhysicalSurface> SurfaceType;
Assign a value to it in the fire function
//更新网络射击信息
if (Role == ROLE_Authority)
{
//子弹数量++
HitScanTrace.BrustCounter++;
//更新弹道目的坐标
HitScanTrace.TraceTo = TraceEndPoint;
//更新击中物体的材质
HitScanTrace.SurfaceType = SurfaceType;
}
We make a function for the generation of the hit effect
//击中特效
void PlayImpactEffects(EPhysicalSurface SurfaceType , FVector ImpactPoint);
define this function
void ASWeapen::PlayImpactEffects(EPhysicalSurface SurfaceType , FVector ImpactPoint)
{
//根据材质的不同,进行不同的处理
UParticleSystem * SelectedEffect = nullptr;
switch (SurfaceType)
{
//这两种情况是一个效果
case SURFACE_FLESHDEFAULT:
case SURFACE_FLESHVULNERABLE:
SelectedEffect = FleshImpactEffect;
break;
default:
SelectedEffect = DefaultImpactEffect;
break;
}
//生成特效在命中点
//ImpactEffect:特效 ImpactPoint:打击点 Rotation():打击方向
if (SelectedEffect)
{
//计算枪口位置
FVector MuzzleLocation = MeshComponent->GetSocketLocation("MuzzleSocket");
//射击方向向量 = 打击点-枪口
FVector ShotDirection = ImpactPoint - MuzzleLocation;
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), SelectedEffect, ImpactPoint, ShotDirection.Rotation());
}
}
Improve the copy function content function
void ASWeapen::OnRep_HitScanTrace()
{
//调用射击特效
PlayFireEffects(HitScanTrace.TraceTo);
//生成命中特效
PlayImpactEffects(HitScanTrace.SurfaceType, HitScanTrace.TraceTo);
}
Perfect fire function
void ASWeapen::Fire()
{
//如果不是服务器,就执行ServerFire(),服务器端就有响应
if (Role < ROLE_Authority)
{
ServerFire();
}
//创建一个撞击句柄,用来获取弹道相关信息
FHitResult Hit;
//弹道的起点,我们设置为角色眼睛的位置
AActor * MyOwner = GetOwner();
//击中物体的材质
EPhysicalSurface SurfaceType = EPhysicalSurface::SurfaceType_Default;
if (MyOwner)
{ //位置
FVector EyeLocation;
//方向
FRotator EyeRotator;
//得到眼睛的位置和角度
MyOwner->GetActorEyesViewPoint(EyeLocation, EyeRotator);
//弹道的终点就是起点+方向*10000
FVector TraceEnd = EyeLocation + (EyeRotator.Vector() * 1000);
//弹道特效的结束点
FVector TraceEndPoint = TraceEnd;
//设置碰撞通道为可见性通道
FCollisionQueryParams QueryParams;
//让射线忽略玩家和枪
QueryParams.AddIgnoredActor(MyOwner);
QueryParams.AddIgnoredActor(this);
//符合追踪设为true,可以让射击更加精准
QueryParams.bTraceComplex = true;
//返回命中目标的表面材质
QueryParams.bReturnPhysicalMaterial = true;
//在创建一个单轨迹线来计算弹道
//LineTraceSingleByChannel击中物体返回true
if (GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEnd, COLLISION_WEAPON, QueryParams))
{
//命中对象
AActor * HitActor = Hit.GetActor();
//实际的伤害
float ActualDamage = BaseDamage;
//得到命中物体表面材质
EPhysicalSurface SurfaceType = UPhysicalMaterial::DetermineSurfaceType(Hit.PhysMaterial.Get());
//如果命中的是头部表面材质,伤害变成四倍
if (SurfaceType == SURFACE_FLESHVULNERABLE)
{
ActualDamage *= 4;
}
//造成点伤害ApplyPointDamage
//参数分别为命中对象、基础伤害、射击方向、命中信息(命中句柄)、MyOwner->GetInstigatorController(暂时不了解)
//this(射击者) 和伤害类型
UGameplayStatics::ApplyPointDamage(HitActor, ActualDamage, EyeRotator.Vector(), Hit, MyOwner->GetInstigatorController(), this, DamageType);
//生成命中特效
PlayImpactEffects(SurfaceType, Hit.ImpactPoint);
//命中的时候,修改弹道特效的终点
TraceEndPoint = Hit.ImpactPoint;
}
//方便debug
//DrawDebugLine(GetWorld(), EyeLocation, TraceEnd, FColor::Red, false, 1, 0, 1);
//射击特效
PlayFireEffects(TraceEndPoint);
//更新网络射击信息
if (Role == ROLE_Authority)
{
//子弹数量++
HitScanTrace.BrustCounter++;
//更新弹道目的坐标
HitScanTrace.TraceTo = TraceEndPoint;
//更新击中物体的材质
HitScanTrace.SurfaceType = SurfaceType;
}
//最后开火的时间
FireLastTime = GetWorld()->TimeSeconds;
}
}
Compile, test, and the special effects on both sides are synchronized
============================
now death is out of sync
Open the health component
First, judge whether it is a server in the game start function, and if it is a server, it will be bound and injured
// Called when the game starts
void USHealthComponent::BeginPlay()
{
Super::BeginPlay();
//这就意味着客户端不会响应受伤事件
if (GetOwnerRole() == ROLE_Authority)
{
AActor * Owner = GetOwner();
//将该函数绑定在角色的受伤事件上
if (Owner)
{
Owner->OnTakeAnyDamage.AddDynamic(this, &USHealthComponent::HandleTakeAnyDamage);
}
}
Health = DefaultHealth;
}
Make network replicable in the constructor
USHealthComponent::USHealthComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
DefaultHealth = 100;
//网络复制
SetIsReplicated(true);
}
Declare the health member variable in it as network copyable
//当前生命值
UPROPERTY(Replicated, BlueprintReadOnly, Category = "HealthComponent")
float Health;
And define and declare related synchronization functions
//用于网络同步的函数
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
#include "Net/UnrealNetwork.h"
void USHealthComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//同步给所有的客户端和服务器
DOREPLIFETIME(USHealthComponent, Health);
}
Open the role class and set the variable to network copyable
//是否死亡
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Player")
bool bDied;
In the network synchronization function, also perform synchronization
void ASCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//同步给所有的客户端和服务器
DOREPLIFETIME(ASCharacter, CurrentWeapen1);
DOREPLIFETIME(ASCharacter, bDied);
}
test
fall to the ground at the same time