Unreal Engine开发:虚拟摄像机开发_虚拟摄像机的网络同步与多人游戏

虚拟摄像机的网络同步与多人游戏

在多人游戏中,虚拟摄像机的网络同步是确保玩家体验一致性和流畅性的关键。本节将详细介绍如何在Unreal Engine中实现虚拟摄像机的网络同步,包括摄像机的基本网络同步原理、网络复制属性的设置、以及具体的代码示例。

摄像机网络同步原理

在Unreal Engine中,摄像机的网络同步主要依赖于网络复制系统。网络复制系统通过标记需要同步的属性和函数,确保这些属性和函数在服务器和客户端之间保持一致。对于摄像机,我们通常需要同步以下属性和行为:

  1. 位置和旋转:摄像机的位置和旋转是玩家视觉体验的基础,必须保持同步以确保所有玩家看到的内容一致。

  2. FOV(视场角):FOV的变化会影响玩家的视野范围,因此也需要在网络中同步。

  3. 摄像机模式:不同的摄像机模式(如第三人称、第一人称)可能会影响玩家的视觉体验,这些模式切换也需要同步。

  4. 特殊效果:如镜头抖动、变焦等效果,这些效果在网络游戏中往往需要同步以增强游戏的沉浸感。

网络复制属性

在网络同步中,我们需要标记类中的属性以便于网络系统知道哪些属性需要在服务器和客户端之间同步。Unreal Engine提供了几个关键的宏来实现这一点:

  • RepNotify:当属性在网络中发生变化时,触发一个回调函数。

  • Replicated:标记属性为网络复制属性。

  • ReplicatedUsing:使用指定的函数来同步属性。

网络复制函数

除了属性,我们还需要同步一些函数,如摄像机模式的切换、特殊效果的触发等。Unreal Engine提供了以下宏来标记需要网络同步的函数:

  • Server:标记为服务器端调用的函数。

  • Client:标记为客户端调用的函数。

  • Multicast:标记为多播函数,即服务器和所有客户端都会调用的函数。

摄像机位置和旋转的网络同步

基本步骤

  1. 创建摄像机组件:在角色类中创建一个摄像机组件。

  2. 标记位置和旋转属性:使用Replicated宏标记摄像机的位置和旋转属性。

  3. 实现同步逻辑:在角色类中实现位置和旋转的同步逻辑。

代码示例

假设我们有一个角色类MyCharacter,其中包含一个摄像机组件CameraComponent。我们需要在网络中同步摄像机的位置和旋转。

角色类定义

// MyCharacter.h

#pragma once



#include "CoreMinimal.h"

#include "GameFramework/Character.h"

#include "MyCharacter.generated.h"



UCLASS()

class MYGAME_API AMyCharacter : public ACharacter

{
    
    

    GENERATED_BODY()



public:

    // Sets default values for this character's properties

    AMyCharacter();



protected:

    // Called when the game starts or when spawned

    virtual void BeginPlay() override;



public:

    // Called every frame

    virtual void Tick(float DeltaTime) override;



    // Called to bind functionality to input

    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;



    // 摄像机组件

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", Meta = (AllowPrivateAccess = "true"))

    UCameraComponent* CameraComponent;



    // 摄像机位置

    UPROPERTY(Replicated, Category = "Camera")

    FVector CameraLocation;



    // 摄像机旋转

    UPROPERTY(Replicated, Category = "Camera")

    FRotator CameraRotation;



    // 同步摄像机位置和旋转

    UFUNCTION()

    void OnRep_CameraLocation();



    UFUNCTION()

    void OnRep_CameraRotation();



    // 服务器端设置摄像机位置和旋转

    UFUNCTION(Server, Reliable, WithValidation)

    void ServerSetCameraLocationAndRotation(const FVector& NewLocation, const FRotator& NewRotation);

};

角色类实现

// MyCharacter.cpp

#include "MyCharacter.h"

#include "Net/UnrealNetwork.h"



AMyCharacter::AMyCharacter()

{
    
    

    // 设置默认摄像机组件

    CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));

    CameraComponent->SetupAttachment(RootComponent);



    // 设置初始位置和旋转

    CameraLocation = FVector(0.0f, 0.0f, 60.0f);

    CameraRotation = FRotator(0.0f, 0.0f, 0.0f);

}



void AMyCharacter::BeginPlay()

{
    
    

    Super::BeginPlay();



    // 设置摄像机组件的位置和旋转

    CameraComponent->SetWorldLocation(CameraLocation);

    CameraComponent->SetWorldRotation(CameraRotation);

}



void AMyCharacter::Tick(float DeltaTime)

{
    
    

    Super::Tick(DeltaTime);



    // 更新摄像机组件的位置和旋转

    CameraComponent->SetWorldLocation(CameraLocation);

    CameraComponent->SetWorldRotation(CameraRotation);

}



void AMyCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)

{
    
    

    Super::SetupPlayerInputComponent(PlayerInputComponent);



    // 绑定输入到摄像机控制

    PlayerInputComponent->BindAxis("MoveForward", this, &AMyCharacter::MoveForward);

    PlayerInputComponent->BindAxis("MoveRight", this, &AMyCharacter::MoveRight);

    PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);

    PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);

}



void AMyCharacter::OnRep_CameraLocation()

{
    
    

    // 当摄像机位置在网络中发生变化时,更新摄像机组件的位置

    CameraComponent->SetWorldLocation(CameraLocation);

}



void AMyCharacter::OnRep_CameraRotation()

{
    
    

    // 当摄像机旋转在网络中发生变化时,更新摄像机组件的旋转

    CameraComponent->SetWorldRotation(CameraRotation);

}



void AMyCharacter::ServerSetCameraLocationAndRotation_Implementation(const FVector& NewLocation, const FRotator& NewRotation)

{
    
    

    // 服务器端设置摄像机位置和旋转

    CameraLocation = NewLocation;

    CameraRotation = NewRotation;



    // 通知客户端摄像机位置和旋转的变化

    OnRep_CameraLocation();

    OnRep_CameraRotation();

}



bool AMyCharacter::ServerSetCameraLocationAndRotation_Validate(const FVector& NewLocation, const FRotator& NewRotation)

{
    
    

    // 验证服务器端设置摄像机位置和旋转的输入是否有效

    return true; // 通常情况下,我们假设输入总是有效的

}



void AMyCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const

{
    
    

    Super::GetLifetimeReplicatedProps(OutLifetimeProps);



    // 注册需要同步的属性

    DOREPLIFETIME(AMyCharacter, CameraLocation);

    DOREPLIFETIME(AMyCharacter, CameraRotation);

}

说明

  1. 摄像机组件:在MyCharacter类中,我们创建了一个UCameraComponent并将其附加到角色的根组件。

  2. 位置和旋转属性:我们定义了两个属性CameraLocationCameraRotation,并使用Replicated宏标记为网络复制属性。

  3. 同步回调函数OnRep_CameraLocationOnRep_CameraRotation函数在网络属性发生变化时被调用,用于更新摄像机组件的位置和旋转。

  4. 服务器端函数ServerSetCameraLocationAndRotation函数在服务器端被调用,设置摄像机的位置和旋转,并通知客户端更新。

  5. 网络属性注册:在GetLifetimeReplicatedProps函数中,我们需要注册需要同步的属性,以确保Unreal Engine的网络系统能够正确处理这些属性。

摄像机模式的网络同步

在多人游戏中,玩家可能需要在不同的摄像机模式之间切换,如第三人称和第一人称。这些模式切换需要在网络中同步,以确保所有玩家的体验一致。

基本步骤

  1. 定义摄像机模式:在角色类中定义摄像机模式。

  2. 标记摄像机模式属性:使用Replicated宏标记摄像机模式属性。

  3. 实现模式切换逻辑:在角色类中实现摄像机模式切换的逻辑。

代码示例

角色类定义

// MyCharacter.h

#pragma once



#include "CoreMinimal.h"

#include "GameFramework/Character.h"

#include "MyCharacter.generated.h"



UENUM()

enum class ECameraMode : uint8

{
    
    

    FirstPerson,

    ThirdPerson

};



UCLASS()

class MYGAME_API AMyCharacter : public ACharacter

{
    
    

    GENERATED_BODY()



public:

    // Sets default values for this character's properties

    AMyCharacter();



protected:

    // Called when the game starts or when spawned

    virtual void BeginPlay() override;



public:

    // Called every frame

    virtual void Tick(float DeltaTime) override;



    // Called to bind functionality to input

    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;



    // 摄像机组件

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", Meta = (AllowPrivateAccess = "true"))

    UCameraComponent* CameraComponent;



    // 摄像机模式

    UPROPERTY(Replicated, Category = "Camera")

    ECameraMode CameraMode;



    // 同步摄像机模式

    UFUNCTION()

    void OnRep_CameraMode();



    // 服务器端设置摄像机模式

    UFUNCTION(Server, Reliable, WithValidation)

    void ServerSetCameraMode(ECameraMode NewMode);

};

角色类实现

// MyCharacter.cpp

#include "MyCharacter.h"

#include "Net/UnrealNetwork.h"



AMyCharacter::AMyCharacter()

{
    
    

    // 设置默认摄像机组件

    CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));

    CameraComponent->SetupAttachment(RootComponent);



    // 设置初始摄像机模式

    CameraMode = ECameraMode::ThirdPerson;

}



void AMyCharacter::BeginPlay()

{
    
    

    Super::BeginPlay();



    // 根据初始摄像机模式设置摄像机组件的位置和旋转

    UpdateCameraMode();

}



void AMyCharacter::Tick(float DeltaTime)

{
    
    

    Super::Tick(DeltaTime);



    // 更新摄像机组件的位置和旋转

    CameraComponent->SetWorldLocation(CameraLocation);

    CameraComponent->SetWorldRotation(CameraRotation);

}



void AMyCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)

{
    
    

    Super::SetupPlayerInputComponent(PlayerInputComponent);



    // 绑定输入到摄像机模式切换

    PlayerInputComponent->BindAction("ToggleCameraMode", IE_Pressed, this, &AMyCharacter::ToggleCameraMode);

}



void AMyCharacter::OnRep_CameraMode()

{
    
    

    // 当摄像机模式在网络中发生变化时,更新摄像机组件的位置和旋转

    UpdateCameraMode();

}



void AMyCharacter::ServerSetCameraMode_Implementation(ECameraMode NewMode)

{
    
    

    // 服务器端设置摄像机模式

    CameraMode = NewMode;



    // 通知客户端摄像机模式的变化

    OnRep_CameraMode();

}



bool AMyCharacter::ServerSetCameraMode_Validate(ECameraMode NewMode)

{
    
    

    // 验证服务器端设置摄像机模式的输入是否有效

    return true; // 通常情况下,我们假设输入总是有效的

}



void AMyCharacter::ToggleCameraMode()

{
    
    

    // 切换摄像机模式

    if (CameraMode == ECameraMode::FirstPerson)

    {
    
    

        ServerSetCameraMode(ECameraMode::ThirdPerson);

    }

    else

    {
    
    

        ServerSetCameraMode(ECameraMode::FirstPerson);

    }

}



void AMyCharacter::UpdateCameraMode()

{
    
    

    // 根据摄像机模式更新摄像机组件的位置和旋转

    if (CameraMode == ECameraMode::FirstPerson)

    {
    
    

        CameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f));

        CameraComponent->SetRelativeRotation(FRotator(0.0f, 0.0f, 0.0f));

    }

    else if (CameraMode == ECameraMode::ThirdPerson)

    {
    
    

        CameraComponent->SetRelativeLocation(FVector(-300.0f, 0.0f, 120.0f));

        CameraComponent->SetRelativeRotation(FRotator(-45.0f, 0.0f, 0.0f));

    }

}



void AMyCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const

{
    
    

    Super::GetLifetimeReplicatedProps(OutLifetimeProps);



    // 注册需要同步的属性

    DOREPLIFETIME(AMyCharacter, CameraLocation);

    DOREPLIFETIME(AMyCharacter, CameraRotation);

    DOREPLIFETIME(AMyCharacter, CameraMode);

}

说明

  1. 摄像机模式枚举:我们定义了一个枚举ECameraMode,用于表示不同的摄像机模式。

  2. 模式切换函数ToggleCameraMode函数用于在第一人称和第三人称之间切换。

  3. 服务器端模式设置ServerSetCameraMode函数在服务器端被调用,设置摄像机模式,并通知客户端更新。

  4. 更新模式逻辑UpdateCameraMode函数根据当前的摄像机模式更新摄像机组件的位置和旋转。

  5. 网络属性注册:在GetLifetimeReplicatedProps函数中,我们需要注册需要同步的属性,以确保Unreal Engine的网络系统能够正确处理这些属性。

摄像机特殊效果的网络同步

在多人游戏中,摄像机的特殊效果(如镜头抖动、变焦等)也需要在网络中同步,以增强游戏的沉浸感和一致性。

基本步骤

  1. 定义特殊效果函数:在角色类中定义特殊效果函数。

  2. 标记特殊效果函数:使用Multicast宏标记特殊效果函数。

  3. 实现特殊效果逻辑:在角色类中实现特殊效果的逻辑。

代码示例

角色类定义

// MyCharacter.h

#pragma once



#include "CoreMinimal.h"

#include "GameFramework/Character.h"

#include "MyCharacter.generated.h"



UCLASS()

class MYGAME_API AMyCharacter : public ACharacter

{
    
    

    GENERATED_BODY()



public:

    // Sets default values for this character's properties

    AMyCharacter();



protected:

    // Called when the game starts or when spawned

    virtual void BeginPlay() override;



public:

    // Called every frame

    virtual void Tick(float DeltaTime) override;



    // Called to bind functionality to input

    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;



    // 摄像机组件

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", Meta = (AllowPrivateAccess = "true"))

    UCameraComponent* CameraComponent;



    // 镜头抖动

    UFUNCTION(Multicast, Reliable)

    void MulticastCameraShake(float Intensity, float Duration);



    // 变焦

    UFUNCTION(Multicast, Reliable)

    void MulticastZoom(float ZoomFactor, float Duration);

};

角色类实现

// MyCharacter.cpp

#include "MyCharacter.h"

#include "Net/UnrealNetwork.h"

#include "Camera/CameraShake.h"

#include "GameFramework/PawnMovementComponent.h"



AMyCharacter::AMyCharacter()

{
    
    

    // 设置默认摄像机组件

    CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));

    CameraComponent->SetupAttachment(RootComponent);

}



void AMyCharacter::BeginPlay()

{
    
    

    Super::BeginPlay();

}



void AMyCharacter::Tick(float DeltaTime)

{
    
    

    Super::Tick(DeltaTime);

}



void AMyCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)

{
    
    

    Super::SetupPlayerInputComponent(PlayerInputComponent);



    // 绑定输入到特殊效果

    PlayerInputComponent->BindAction("CameraShake", IE_Pressed, this, &AMyCharacter::ServerCameraShake);

    PlayerInputComponent->BindAction("Zoom", IE_Pressed, this, &AMyCharacter::ServerZoom);

}



void AMyCharacter::MulticastCameraShake_Implementation(float Intensity, float Duration)

{
    
    

    // 触发镜头抖动效果

    if (CameraComponent && !IsLocallyControlled())

    {
    
    

        UCameraShake* Shake = NewObject<UCameraShake>(GetWorld());

        Shake->Duration = Duration;

        Shake->Intensity = Intensity;

        CameraComponent->StartCameraShake(Shake);

    }

}



void AMyCharacter::MulticastZoom_Implementation(float ZoomFactor, float Duration)

{
    
    

    // 触发变焦效果

    if (CameraComponent && !IsLocallyControlled())

    {
    
    

        CameraComponent->SetFieldOfView(CameraComponent->FieldOfView * ZoomFactor);

        GetWorld()->GetTimerManager().SetTimerForNextTick(this, &AMyCharacter::ResetZoom);

    }

}



void AMyCharacter::ResetZoom()

{
    
    

    // 重置变焦效果

    if (CameraComponent)

    {
    
    

        CameraComponent->SetFieldOfView(90.0f); // 假设初始FOV为90度

    }

}



void AMyCharacter::ServerCameraShake_Implementation(float Intensity, float Duration)

{
    
    

    // 服务器端触发镜头抖动效果

    MulticastCameraShake(Intensity, Duration);

}



bool AMyCharacter::ServerCameraShake_Validate(float Intensity, float Duration)

{
    
    

    // 验证服务器端触发镜头抖动效果的输入是否有效

    return true; // 通常情况下,我们假设输入总是有效的

}



void AMyCharacter::ServerZoom_Implementation(float ZoomFactor, float Duration)

{
    
    

    // 服务器端触发变焦效果

    MulticastZoom(ZoomFactor, Duration);

}



bool AMyCharacter::ServerZoom_Validate(float ZoomFactor, float Duration)

{
    
    

    // 验证服务器端触发变焦效果的输入是否有效

    return true; // 通常情况下,我们假设输入总是有效的

}

说明

  1. 特殊效果函数:我们定义了两个多播函数MulticastCameraShakeMulticastZoom,用于在网络中同步摄像机的特殊效果。

  2. 服务器端触发ServerCameraShakeServerZoom函数在服务器端被调用,触发多播函数,确保所有客户端都能执行相同的特殊效果。

具体实现

角色类定义

// MyCharacter.h

#pragma once



#include "CoreMinimal.h"

#include "GameFramework/Character.h"

#include "MyCharacter.generated.h"



UCLASS()

class MYGAME_API AMyCharacter : public ACharacter

{
    
    

    GENERATED_BODY()



public:

    // Sets default values for this character's properties

    AMyCharacter();



protected:

    // Called when the game starts or when spawned

    virtual void BeginPlay() override;



public:

    // Called every frame

    virtual void Tick(float DeltaTime) override;



    // Called to bind functionality to input

    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;



    // 摄像机组件

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", Meta = (AllowPrivateAccess = "true"))

    UCameraComponent* CameraComponent;



    // 镜头抖动

    UFUNCTION(Multicast, Reliable)

    void MulticastCameraShake(float Intensity, float Duration);



    // 变焦

    UFUNCTION(Multicast, Reliable)

    void MulticastZoom(float ZoomFactor, float Duration);



    // 服务器端触发镜头抖动

    UFUNCTION(Server, Reliable, WithValidation)

    void ServerCameraShake(float Intensity, float Duration);



    // 服务器端触发变焦

    UFUNCTION(Server, Reliable, WithValidation)

    void ServerZoom(float ZoomFactor, float Duration);

};

角色类实现

// MyCharacter.cpp

#include "MyCharacter.h"

#include "Net/UnrealNetwork.h"

#include "Camera/CameraShake.h"

#include "GameFramework/PawnMovementComponent.h"



AMyCharacter::AMyCharacter()

{
    
    

    // 设置默认摄像机组件

    CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));

    CameraComponent->SetupAttachment(RootComponent);



    // 设置初始FOV

    CameraComponent->SetFieldOfView(90.0f);

}



void AMyCharacter::BeginPlay()

{
    
    

    Super::BeginPlay();

}



void AMyCharacter::Tick(float DeltaTime)

{
    
    

    Super::Tick(DeltaTime);

}



void AMyCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)

{
    
    

    Super::SetupPlayerInputComponent(PlayerInputComponent);



    // 绑定输入到特殊效果

    PlayerInputComponent->BindAction("CameraShake", IE_Pressed, this, &AMyCharacter::ServerCameraShake);

    PlayerInputComponent->BindAction("Zoom", IE_Pressed, this, &AMyCharacter::ServerZoom);

}



void AMyCharacter::MulticastCameraShake_Implementation(float Intensity, float Duration)

{
    
    

    // 触发镜头抖动效果

    if (CameraComponent && !IsLocallyControlled())

    {
    
    

        UCameraShake* Shake = NewObject<UCameraShake>(GetWorld());

        Shake->Duration = Duration;

        Shake->Intensity = Intensity;

        CameraComponent->StartCameraShake(Shake);

    }

}



void AMyCharacter::MulticastZoom_Implementation(float ZoomFactor, float Duration)

{
    
    

    // 触发变焦效果

    if (CameraComponent && !IsLocallyControlled())

    {
    
    

        CameraComponent->SetFieldOfView(CameraComponent->FieldOfView * ZoomFactor);

        GetWorld()->GetTimerManager().SetTimerForNextTick(this, &AMyCharacter::ResetZoom);

    }

}



void AMyCharacter::ResetZoom()

{
    
    

    // 重置变焦效果

    if (CameraComponent)

    {
    
    

        CameraComponent->SetFieldOfView(90.0f); // 假设初始FOV为90度

    }

}



void AMyCharacter::ServerCameraShake_Implementation(float Intensity, float Duration)

{
    
    

    // 服务器端触发镜头抖动效果

    MulticastCameraShake(Intensity, Duration);

}



bool AMyCharacter::ServerCameraShake_Validate(float Intensity, float Duration)

{
    
    

    // 验证服务器端触发镜头抖动效果的输入是否有效

    return true; // 通常情况下,我们假设输入总是有效的

}



void AMyCharacter::ServerZoom_Implementation(float ZoomFactor, float Duration)

{
    
    

    // 服务器端触发变焦效果

    MulticastZoom(ZoomFactor, Duration);

}



bool AMyCharacter::ServerZoom_Validate(float ZoomFactor, float Duration)

{
    
    

    // 验证服务器端触发变焦效果的输入是否有效

    return true; // 通常情况下,我们假设输入总是有效的

}

说明

  1. 摄像机组件:在MyCharacter类中,我们创建了一个UCameraComponent并将其附加到角色的根组件。

  2. 初始FOV:我们设置了初始的FOV为90度。

  3. 输入绑定:在SetupPlayerInputComponent函数中,我们绑定了输入到特殊效果的触发函数ServerCameraShakeServerZoom

  4. 多播函数MulticastCameraShakeMulticastZoom函数在网络中被调用,确保所有客户端都能执行相同的特殊效果。

  5. 服务器端触发ServerCameraShakeServerZoom函数在服务器端被调用,触发多播函数,确保所有客户端都能同步执行特殊效果。

  6. 重置变焦ResetZoom函数在变焦效果结束后重置FOV,以确保摄像机恢复到初始状态。

客户端验证

在多人游戏中,客户端验证是非常重要的,以防止作弊和不一致的行为。Unreal Engine提供了WithValidation宏来标记需要验证的服务器端函数。验证函数会在客户端调用服务器端函数之前执行,确保输入的有效性。

代码示例

// MyCharacter.h

#pragma once



#include "CoreMinimal.h"

#include "GameFramework/Character.h"

#include "MyCharacter.generated.h"



UCLASS()

class MYGAME_API AMyCharacter : public ACharacter

{
    
    

    GENERATED_BODY()



public:

    // Sets default values for this character's properties

    AMyCharacter();



protected:

    // Called when the game starts or when spawned

    virtual void BeginPlay() override;



public:

    // Called every frame

    virtual void Tick(float DeltaTime) override;



    // Called to bind functionality to input

    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;



    // 摄像机组件

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", Meta = (AllowPrivateAccess = "true"))

    UCameraComponent* CameraComponent;



    // 镜头抖动

    UFUNCTION(Multicast, Reliable)

    void MulticastCameraShake(float Intensity, float Duration);



    // 变焦

    UFUNCTION(Multicast, Reliable)

    void MulticastZoom(float ZoomFactor, float Duration);



    // 服务器端触发镜头抖动

    UFUNCTION(Server, Reliable, WithValidation)

    void ServerCameraShake(float Intensity, float Duration);



    // 服务器端触发变焦

    UFUNCTION(Server, Reliable, WithValidation)

    void ServerZoom(float ZoomFactor, float Duration);



    // 重置变焦

    UFUNCTION()

    void ResetZoom();

};

角色类实现

// MyCharacter.cpp

#include "MyCharacter.h"

#include "Net/UnrealNetwork.h"

#include "Camera/CameraShake.h"

#include "GameFramework/PawnMovementComponent.h"



AMyCharacter::AMyCharacter()

{
    
    

    // 设置默认摄像机组件

    CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));

    CameraComponent->SetupAttachment(RootComponent);



    // 设置初始FOV

    CameraComponent->SetFieldOfView(90.0f);

}



void AMyCharacter::BeginPlay()

{
    
    

    Super::BeginPlay();

}



void AMyCharacter::Tick(float DeltaTime)

{
    
    

    Super::Tick(DeltaTime);

}



void AMyCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)

{
    
    

    Super::SetupPlayerInputComponent(PlayerInputComponent);



    // 绑定输入到特殊效果

    PlayerInputComponent->BindAction("CameraShake", IE_Pressed, this, &AMyCharacter::ServerCameraShake);

    PlayerInputComponent->BindAction("Zoom", IE_Pressed, this, &AMyCharacter::ServerZoom);

}



void AMyCharacter::MulticastCameraShake_Implementation(float Intensity, float Duration)

{
    
    

    // 触发镜头抖动效果

    if (CameraComponent && !IsLocallyControlled())

    {
    
    

        UCameraShake* Shake = NewObject<UCameraShake>(GetWorld());

        Shake->Duration = Duration;

        Shake->Intensity = Intensity;

        CameraComponent->StartCameraShake(Shake);

    }

}



void AMyCharacter::MulticastZoom_Implementation(float ZoomFactor, float Duration)

{
    
    

    // 触发变焦效果

    if (CameraComponent && !IsLocallyControlled())

    {
    
    

        CameraComponent->SetFieldOfView(CameraComponent->FieldOfView * ZoomFactor);

        GetWorld()->GetTimerManager().SetTimerForNextTick(this, &AMyCharacter::ResetZoom);

    }

}



void AMyCharacter::ResetZoom()

{
    
    

    // 重置变焦效果

    if (CameraComponent)

    {
    
    

        CameraComponent->SetFieldOfView(90.0f); // 假设初始FOV为90度

    }

}



void AMyCharacter::ServerCameraShake_Implementation(float Intensity, float Duration)

{
    
    

    // 服务器端触发镜头抖动效果

    MulticastCameraShake(Intensity, Duration);

}



bool AMyCharacter::ServerCameraShake_Validate(float Intensity, float Duration)

{
    
    

    // 验证服务器端触发镜头抖动效果的输入是否有效

    return Intensity >= 0.0f && Duration >= 0.0f; // 确保强度和持续时间非负

}



void AMyCharacter::ServerZoom_Implementation(float ZoomFactor, float Duration)

{
    
    

    // 服务器端触发变焦效果

    MulticastZoom(ZoomFactor, Duration);

}



bool AMyCharacter::ServerZoom_Validate(float ZoomFactor, float Duration)

{
    
    

    // 验证服务器端触发变焦效果的输入是否有效

    return ZoomFactor > 0.0f && Duration >= 0.0f; // 确保变焦因子大于0,持续时间非负

}

说明

  1. 客户端验证:在ServerCameraShake_ValidateServerZoom_Validate函数中,我们确保输入的强度、持续时间和变焦因子都是有效的。这有助于防止客户端发送无效的参数,从而避免游戏中的不一致行为。

  2. 多播函数调用:在服务器端函数的实现中,我们调用多播函数MulticastCameraShakeMulticastZoom,确保所有客户端都能同步执行特殊效果。

总结

在Unreal Engine中实现虚拟摄像机的网络同步涉及以下几个关键步骤:

  1. 标记需要同步的属性:使用Replicated宏标记摄像机的位置、旋转、模式等属性。

  2. 实现同步回调函数:使用OnRep_前缀的函数在网络属性发生变化时更新摄像机组件。

  3. 标记需要同步的函数:使用Server宏标记服务器端调用的函数,使用Multicast宏标记多播函数。

  4. 注册网络属性:在GetLifetimeReplicatedProps函数中注册需要同步的属性,以确保Unreal Engine的网络系统能够正确处理这些属性。

  5. 客户端验证:使用WithValidation宏标记服务器端函数,并实现验证逻辑,确保输入的有效性。

通过这些步骤,我们可以确保在多人游戏中虚拟摄像机的行为在网络中保持一致,从而提供流畅的多人游戏体验。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/chenlz2007/article/details/147031698