打开主角类,生成枪的代码逻辑在游戏开始函数里
所以在生成之前,我们需要判断该对象是否在服务器端(服务器端视角)
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);
}
编译一下,看看是什么效果
我们发现,左边的有武器,右边的没有武器
所以我们要让武器可以进行网络复制
打开武器类,在构造函数中进行设置
//设置可以进行网络复制
SetReplicates(true);
编译,然后打开枪的蓝图,打上勾
这一次他们都有枪了
现在枪虽然是有了,左边的玩家可以开枪,但是右边的角色不可以开枪
这是因为右边角色,指向武器的指针是空的
所以我们要让武器指针可以同步,这样就可以同步了
//目前玩家手中的武器
UPROPERTY(Replicated)
class ASWeapen * CurrentWeapen1;
要实现网络同步,必须还要有一个函数,这个函数定义在actor类里面
我们将其复制到玩家类里面
//用于网络同步的函数
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
导入头文件
#include "Net/UnrealNetwork.h"
定义这个函数
void ASCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//同步给所有的客户端和服务器
DOREPLIFETIME(ASCharacter, CurrentWeapen1);
}
测试,两个角色都可以打枪了,但是互相看不见。
下面解决这个问题。
==========================================
给武器类添加成员函数serverfire()
UFUNCTION(Server, Reliable, WithValidation)
void ServerFire();
然后实现该函数的方式比较特殊
void ASWeapen::ServerFire_Implementation()
{
Fire();
}
bool ASWeapen::ServerFire_Validate()
{
return true;
}
然后我们修改一下Fire()函数
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;
}
}
编译,测试,服务器端开火,客户端看不到(不管控制哪个角色,都是在服务器端看到开火)
修改一下代码,我们把return去掉
编译测试
服务器端开火,客户端看不到;客户端开火,同时可以看到
给角色蓝图添加两个节点
===========================================
目前
操作客户端,两端都能看到特效
操作服务器端,客户端看不到特效
在武器类的头文件中,创建结构体
USTRUCT()
struct FHitScanTrace
{
GENERATED_BODY()
public:
//弹道的目的坐标
UPROPERTY()
FVector_NetQuantize TraceTo;
//子弹数目:为了让该结构体内容发生变化,结构体才被不断得被网络复制
UPROPERTY()
uint8 BrustCounter;
};
创建该类中的结构体成员变量,和对应的网络复制函数
//网络射击信息
UPROPERTY(ReplicatedUsing = OnRep_HitScanTrace)
FHitScanTrace HitScanTrace;
//网络复制函数
UFUNCTION()
void OnRep_HitScanTrace();
当结构体内容发生改变的时候,就会自动调用网络复制函数
在fire函数里面,更新网络射击信息
//更新网络射击信息
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;
}
}
定义网络复制函数,让其执行射击特效
void ASWeapen::OnRep_HitScanTrace()
{
//调用射击特效
PlayFireEffects(HitScanTrace.TraceTo);
}
此时,网络射击信息结构体变量还没有实现网络的复制共享,我们让其共享。
创建复制成员变量的函数
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
定义这个函数
void ASWeapen::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//同步给所有的客户端和服务器(DOREPLIFETIME_CONDITION不用同步给自己)
DOREPLIFETIME_CONDITION(ASWeapen, HitScanTrace,COND_SkipOwner);
}
编译,测试
发现服务器端射击的时候,客户端可以看到弹道的特效,但看不到击中特效
============================================
现在解决一下这个问题
为网络射击结构体添加表面材质成员变量
//表面材质
UPROPERTY()
TEnumAsByte<EPhysicalSurface> SurfaceType;
在fire函数里面为其赋值
//更新网络射击信息
if (Role == ROLE_Authority)
{
//子弹数量++
HitScanTrace.BrustCounter++;
//更新弹道目的坐标
HitScanTrace.TraceTo = TraceEndPoint;
//更新击中物体的材质
HitScanTrace.SurfaceType = SurfaceType;
}
我们为击中特效的生成做成一个函数
//击中特效
void PlayImpactEffects(EPhysicalSurface SurfaceType , FVector ImpactPoint);
定义这个函数
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());
}
}
完善一下复制函数内容函数
void ASWeapen::OnRep_HitScanTrace()
{
//调用射击特效
PlayFireEffects(HitScanTrace.TraceTo);
//生成命中特效
PlayImpactEffects(HitScanTrace.SurfaceType, HitScanTrace.TraceTo);
}
完善fire函数
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;
}
}
编译,测试,两边特效同步了
============================
现在是死亡不同步
打开生命值组件
首先在游戏开始函数里判断当前是否是服务器,如果是服务器,才绑定受伤
// 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;
}
在构造函数中设为可网路复制
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);
}
将其中的生命值成员变量声明为网路可复制
//当前生命值
UPROPERTY(Replicated, BlueprintReadOnly, Category = "HealthComponent")
float Health;
并定义和声明相关同步函数
//用于网络同步的函数
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
#include "Net/UnrealNetwork.h"
void USHealthComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//同步给所有的客户端和服务器
DOREPLIFETIME(USHealthComponent, Health);
}
打开角色类,将该变量设为网络可复制
//是否死亡
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Player")
bool bDied;
网络同步函数中,也进行同步一下
void ASCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//同步给所有的客户端和服务器
DOREPLIFETIME(ASCharacter, CurrentWeapen1);
DOREPLIFETIME(ASCharacter, bDied);
}
测试
同时倒地
文章来源:https://www.toymoban.com/news/detail-408816.html
文章来源地址https://www.toymoban.com/news/detail-408816.html
到了这里,关于18uec++多人游戏【服务器为两个角色发枪,并能在线开枪】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!