# 【Replicated】
告诉引擎哪些成员变量需要在网络上进行复制和同步,以便在多个客户端之间保持游戏状态的一致性。
# 同步条件
CONDITION_None:表示不使用任何条件,即无条件复制属性。这是默认的条件。
CONDITION_InitialOnly:表示仅在初始复制时才进行属性复制,初始复制是指在对象被复制到新的客户端时进行的第一次复制。这可以减少网络负载并提高性能。
CONDITION_OwnerOnly:表示仅在属性的所有者(Owner)是本地玩家控制的角色时才进行复制。这在存在大量角色时有助于减少网络带宽的使用。
CONDITION_SkipOwner:表示在属性的所有者(Owner)是本地玩家控制的角色时不进行复制,但在其他情况下进行复制。这对于只有远程玩家之间需要同步的属性是有用的。
CONDITION_SimulatedOnly:表示仅在属性的所有者(Owner)是模拟代理(Simulated Proxy)时才进行复制。模拟代理是指非本地玩家控制的角色的代理。
# 使用示例
下面是一个角色碰撞武器时的,需要对应角色显示“按 E 装备”的一个示例。
AWeapon.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "WeaponWidgetComponent.h"
#include "Weapon.generated.h"
UENUM(BlueprintType)
enum class EWeaponState : uint8
{
EWS_Initial UMETA(DisplayName = "Initial State"),
EWS_Equipped UMETA(DisplayName = "Equipped"),
EWS_Dropped UMETA(DisplayName = "Dropped"),
EWS_MAX UMETA(DisplayName = "DefaultMAX")
};
UCLASS()
class BLASTER_API AWeapon : public AActor
{
GENERATED_BODY()
public:
AWeapon();
virtual void Tick(float DeltaTime) override;
void ShowPickupWidget(bool bShowWidget);
protected:
virtual void BeginPlay() override;
UFUNCTION()
virtual void OnSphereOverlap(
UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult);
UFUNCTION()
virtual void OnSphereEndOverlap(
UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex);
private:
UPROPERTY(VisibleAnywhere,Category = "Weapon Properties")
class USkeletalMeshComponent* WeaponMesh;
UPROPERTY(VisibleAnywhere, Category = "Weapon Properties")
class USphereComponent* AreaSphere;
UPROPERTY(VisibleAnywhere, Category = "Weapon Properties")
class UWeaponWidgetComponent* PickupWidget;
UPROPERTY(VisibleAnywhere,Category = "Weapon Properties")
EWeaponState WeaponState;
};
AWeapon.cpp
#include "Weapon.h"
#include "Components/SphereComponent.h"
#include "WeaponWidgetComponent.h"
#include "Blaster/Character/BlasterCharacter.h"
AWeapon::AWeapon()
{
PrimaryActorTick.bCanEverTick = false;
bReplicates = true;
WeaponMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("WeaponMesh"));
SetRootComponent(WeaponMesh);
WeaponMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Block);
WeaponMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Ignore);
WeaponMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
AreaSphere = CreateDefaultSubobject<USphereComponent>(TEXT("AreaSphere"));
AreaSphere->SetupAttachment(RootComponent);
AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
PickupWidget = CreateDefaultSubobject<UWeaponWidgetComponent>(TEXT("PickupWidget"));
PickupWidget->SetupAttachment(RootComponent);
}
void AWeapon::BeginPlay()
{
Super::BeginPlay();
if(PickupWidget)
{
PickupWidget->SetVisibility(false);
}
// 确保只有权威端才检查碰撞,并不是每个端都检查碰撞
if (HasAuthority())//if (GetLocalRole() == ENetRole::ROLE_Authority)
{
AreaSphere->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
AreaSphere->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
AreaSphere->OnComponentBeginOverlap.AddDynamic(this,&AWeapon::OnSphereOverlap);
AreaSphere->OnComponentEndOverlap.AddDynamic(this,&AWeapon::OnSphereEndOverlap);
}
}
void AWeapon::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// 开始感应到重叠,设置角色与可拾取武器进行重叠
void AWeapon::OnSphereOverlap(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult)
{
ABlasterCharacter* BlasterCharacter = Cast<ABlasterCharacter>(OtherActor);
if(BlasterCharacter)
{
//PickupWidget->SetVisibility(true);
BlasterCharacter->SetOverlappingWeapon(this);
}
}
// 感应重叠结束,置空设置重叠
void AWeapon::OnSphereEndOverlap(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex)
{
ABlasterCharacter* BlasterCharacter = Cast<ABlasterCharacter>(OtherActor);
if(BlasterCharacter)
{
BlasterCharacter->SetOverlappingWeapon(nullptr);
}
}
void AWeapon::ShowPickupWidget(bool bShowWidget)
{
if(PickupWidget)
{
PickupWidget->SetVisibility(bShowWidget);
}
}
BlasterCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "BlasterCharacter.generated.h"
UCLASS()
class BLASTER_API ABlasterCharacter : public ACharacter
{
GENERATED_BODY()
public:
ABlasterCharacter();
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
protected:
virtual void BeginPlay() override;
void MoveForward(float Value);
void MoveRight(float Value);
void Turn(float Value);
void LookUp(float Value);
private:
UPROPERTY(VisibleAnywhere,Category = Camera)
class USpringArmComponent* CameraBoom;
UPROPERTY(VisibleAnywhere, Category = Camera)
class UCameraComponent* FollowCamera;
UPROPERTY(ReplicatedUsing = OnRep_OverlappingWeapon)
class AWeapon* OverlappingWeapon;
UFUNCTION()
void OnRep_OverlappingWeapon(AWeapon* LastWeapon);
public:
void SetOverlappingWeapon(AWeapon* Weapon);// { OverlappingWeapon = Weapon; }
};
在实现文件实现 GetLifetimeReplicatedProps 函数,并在该函数使用 DOREPLIFETIME 或者 DOREPLIFETIME_CONDITION 函数定义需要哦同步的变量
DOREPLIFETIME:定义对象无条件全玩家同步
DOREPLIFETIME_CONDITION:按照指定条件进同步,参考上面的同步条件值。
BlasterCharacter.cpp
#include "BlasterCharacter.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Net/UnrealNetwork.h"
#include "Blaster/Weapon/Weapon.h"
ABlasterCharacter::ABlasterCharacter()
{
PrimaryActorTick.bCanEverTick = true;
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(GetMesh());
CameraBoom->TargetArmLength = 600.f;
CameraBoom->bUsePawnControlRotation = true;
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
FollowCamera->bUsePawnControlRotation = false;
bUseControllerRotationYaw = false;
GetCharacterMovement()->bOrientRotationToMovement = true;
}
void ABlasterCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//DOREPLIFETIME(ABlasterCharacter, OverlappingWeapon);
// 只有Actor的拥有者才会同步,比如角色拥有碰撞的武器,则Actor的对应玩家会收到同步信息
DOREPLIFETIME_CONDITION(ABlasterCharacter, OverlappingWeapon, COND_OwnerOnly);
}
void ABlasterCharacter::BeginPlay(){Super::BeginPlay();}
void ABlasterCharacter::Tick(float DeltaTime){Super::Tick(DeltaTime);}
void ABlasterCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAxis("MoveForward", this, &ThisClass::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &ThisClass::MoveRight);
PlayerInputComponent->BindAxis("Turn", this, &ThisClass::Turn);
PlayerInputComponent->BindAxis("LookUp", this, &ThisClass::LookUp);
}
void ABlasterCharacter::MoveForward(float Value){/** 不是重点忽略 */}
void ABlasterCharacter::MoveRight(float Value){/** 不是重点忽略 */}
void ABlasterCharacter::Turn(float Value){/** 不是重点忽略 */}
void ABlasterCharacter::LookUp(float Value){/** 不是重点忽略 */}
/** 只会在服务端调用 */
void ABlasterCharacter::SetOverlappingWeapon(AWeapon *Weapon)
{
if(IsLocallyControlled())
{
if(OverlappingWeapon)
{
OverlappingWeapon->ShowPickupWidget(false);
}
}
OverlappingWeapon = Weapon;
if(IsLocallyControlled())
{
if(OverlappingWeapon)
{
OverlappingWeapon->ShowPickupWidget(true);
}
}
}
void ABlasterCharacter::OnRep_OverlappingWeapon(AWeapon* LastWeapon)
{
if(OverlappingWeapon)
{
OverlappingWeapon->ShowPickupWidget(true);
}
if(LastWeapon)
{
LastWeapon->ShowPickupWidget(false);
}
}