Skip to content

【Replicated】

告诉引擎哪些成员变量需要在网络上进行复制和同步,以便在多个客户端之间保持游戏状态的一致性。

同步条件

CONDITION_None:表示不使用任何条件,即无条件复制属性。这是默认的条件。

CONDITION_InitialOnly:表示仅在初始复制时才进行属性复制,初始复制是指在对象被复制到新的客户端时进行的第一次复制。这可以减少网络负载并提高性能。

CONDITION_OwnerOnly:表示仅在属性的所有者(Owner)是本地玩家控制的角色时才进行复制。这在存在大量角色时有助于减少网络带宽的使用。

CONDITION_SkipOwner:表示在属性的所有者(Owner)是本地玩家控制的角色时不进行复制,但在其他情况下进行复制。这对于只有远程玩家之间需要同步的属性是有用的。

CONDITION_SimulatedOnly:表示仅在属性的所有者(Owner)是模拟代理(Simulated Proxy)时才进行复制。模拟代理是指非本地玩家控制的角色的代理。

使用示例

下面是一个角色碰撞武器时的,需要对应角色显示“按 E 装备”的一个示例。

AWeapon.h

cpp
#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

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

cpp
#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

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);
    }
}

MIT Licensed