18uec++ multiplayer game [the server fires guns for two characters, and can shoot online]

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

 

 

Guess you like

Origin blog.csdn.net/zhang2362167998/article/details/127861914