【UE4C++-ActionRougelike-19】多人游戏网络同步


【UE4C++-ActionRougelike-19】多人游戏网络同步

一、联网宝箱

1、交互组件RPC函数

在SInteractionComponent.h中添加在服务器上执行的RPC函数,用于客户端请求交互。

UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class ACTIONROGUELIKE_API USInteractionComponent : public UActorComponent
{
public:
    // 声明一个主动交互函数
    void PrimaryInteract();
protected:
    // 在服务器上执行的RPC函数,用于客户端请求进行交互
    UFUNCTION(Server, Reliable)
    void ServerInteract(AActor* InFocus);
}

在SInteractionComponent.cpp中交互时发送RPC调用请求,并实现RPC函数ServerInteract

void USInteractionComponent::PrimaryInteract()
{
    // 发送RPC调用请求
    ServerInteract(FocusedActor);
}
// 定义在服务器上执行的RPC函数
void USInteractionComponent::ServerInteract_Implementation(AActor* InFocus)
{
    // 检查是否选中了交互对象
    if (InFocus == nullptr)
    {
        GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Red, "No Focus Actor to interact.");
        return;
    }
    // 获取拥有该组件的Actor对象所属的Pawn对象
    APawn* MyPawn = Cast<APawn>(GetOwner());
    // 调用Interact函数进行交互
    ISInteractionInterface::Execute_Interact(InFocus, MyPawn);
}

2、修改宝箱

在STreasureChestItem.h中添加一个状态变量bLidOpened表示宝箱是否打开,并且使用RepNotify标记实现状态同步;再定义回调函数OnRep_LidOpened用于状态变量bLidOpened变换后调用

UCLASS()
class ACTIONROGUELIKE_API ASTreasureChestItem : public AActor, public ISInteractionInterface
{
public:	
	// Sets default values for this actor's properties
	ASTreasureChestItem();
    //宝箱打开时盖子角度
	UPROPERTY(EditAnywhere)
	float OpenPitch;
	//重写互动接口的互动方法
	void Interact_Implementation(APawn* InstigatorPawn) override;
protected:
	//表示盖子是否打开,并通过RepNotify标记实现了状态同步,RepNotify是一个属性标记,用于表示在属性更新时应该调用哪个函数。
	//ReplicatedUsing 参数指定一个名为 OnRep_LidOpened 的函数,当该属性的值在网络上改变时会调用该函数,以使客户端和服务器上的值保持同步。
	UPROPERTY(ReplicatedUsing="OnRep_LidOpened", BlueprintReadOnly)
	bool bLidOpened;
	// OnRep函数在客户端和服务器同步变量后调用
	UFUNCTION()
	void OnRep_LidOpened();
	UPROPERTY(VisibleAnywhere)
	UStaticMeshComponent* BaseMesh;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	UStaticMeshComponent* LidMesh;
};

在STreasureChestItem.cpp的构造函数中开启复制,并实现回调函数OnRep_LidOpened

ASTreasureChestItem::ASTreasureChestItem()
{
	//创建宝箱底部Mesh
	BaseMesh = CreateDefaultSubobject<UStaticMeshComponent>("BaseMesh");
	RootComponent = BaseMesh;
	//创建宝箱盖子Mesh
	LidMesh = CreateDefaultSubobject<UStaticMeshComponent>("LidMesh");
	LidMesh->SetupAttachment(BaseMesh);
	//打开宝箱时盖子倾斜角度
	OpenPitch = 110.0f;
	// 开启复制
	SetReplicates(true);
}
void ASTreasureChestItem::Interact_Implementation(APawn* InstigatorPawn)
{
	// 切换盖子状态
	bLidOpened = !bLidOpened;
	// 本地客户端也要同步宝箱盖子状态
	OnRep_LidOpened();
}
void ASTreasureChestItem::OnRep_LidOpened()
{
	// 根据盖子状态设置盖子的旋转角度
	float CurrPitch = bLidOpened ? OpenPitch : 0.0f;
	LidMesh->SetRelativeRotation(FRotator(CurrPitch, 0, 0));
}
// 在GetLifetimeReplicatedProps函数中声明需要同步的属性
void ASTreasureChestItem::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	// 将 bLidOpened 属性添加到需要复制的属性列表中
	DOREPLIFETIME(ASTreasureChestItem, bLidOpened);
}

3、效果演示

联网宝箱效果演示

4、总结:

4.1 RPC
  • 概念:RPC(Remote Procedure Call,远程过程调用)是一种用于多人游戏中网络同步的机制,是在本地调用但在其他机器(不同于执行调用的机器)上远程执行的函数,它可以在服务器和客户端之间传递函数调用。

  • 使用:要将一个函数声明为 RPC,您只需将 ServerClientNetMulticast 关键字添加到 UFUNCTION 声明。

    • Server:从客户端调用到服务器的RPC。
    • Client:从服务器调用到客户端的RPC。
    • Multicast:从服务器调用到所有客户端的RPC。
  • 注意事项:

    • Actor属性:RPC只能在具有网络复制功能的对象上使用,即执行RPC的对象必须是一个继承自AActor的类,且必须被复制。
    • 函数实现:需要在客户端和服务器端都实现RPC函数
    • 可靠性:默认情况下RPC是不可靠的。要确保在远程机器上执行 RPC 调用,可以指定 Reliable 关键字
4.2 属性复制
  • 概念:属性复制是UE中一种实现网络同步的机制。通过标记属性为可复制,UE能够自动同步服务器和客户端之间的这些属性,以保持游戏状态一致。

  • 使用:

    • 在需要复制的属性的UPROPERTY宏中添加Replicated修饰符
    • 实现一个名为GetLifetimeReplicatedProps的函数,以告诉UE引擎哪些属性需要复制。在这个函数中,使用DOREPLIFETIME宏指定需要复制的属性
    • 为Actor开启复制(SetReplicates(true)、bReplicates=true)
  • 注意事项:

    • 客户端只能读取被同步的属性,不能修改。
    • 只为关键属性启用复制,以减少网络传输和处理开销。

二、联网属性和用户界面

1、客户端上的权限

  • PlayerController:客户端拥有与其本地玩家相关的PlayerController实例。客户端可以通过这个实例处理输入和控制游戏角色(Pawn)。
  • PlayerState:客户端可以访问所有客户端玩家的PlayerState实例,但只能读取这些信息。客户端无法直接修改其他客户端玩家的PlayerState。如果需要修改玩家状态信息,需要通过向服务器发送RPC请求来实现。
  • GameState:客户端可以拥有GameState实例。GameState包含全局游戏状态信息,如游戏进度、剩余时间等。服务器负责更新GameState并同步到客户端。
  • GameMode:客户端拥有GameMode实例。GameMode仅在服务器上存在,负责管理游戏的核心逻辑。客户端无法直接访问或修改GameMode,必须通过发送RPC请求或通知服务器来与GameMode进行交互。
  • Pawn:客户端可以拥有Pawn(游戏角色)实例。客户端通常控制一个与其本地玩家相关的Pawn,通过PlayerController来处理输入和控制Pawn。客户端也可以看到其他玩家的Pawn,但无法直接控制它们。

2、联网属性和用户界面

2.1 属性复制

在MyPick和MyProjectileBase的构造函数中添加SetReplicates(true);使得这两个Actor及其子类能够同步到客户端

2.2 同步血量

在MyAttributeComponent.h中为生命值和最大生命值添加Replicated标记,用来复制这两个属性;添加多播函数MulticastHealthChanged用来告知客户端血量变化

UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class ACTIONROGUELIKE_API UMyAttributeComponent : public UActorComponent
{
protected:
    //当前生命值,可在编辑器中修改,默认只在服务器同步
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Replicated, Category = "Attributes")
    float Health;
    //最大生命值,可在编辑器中修改,默认只在服务器同步
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Replicated, Category = "Attributes")
    float MaxHealth;
    // 用于多播当前生命值变化的函数
    UFUNCTION(NetMulticast, Reliable)
    void MulticastHealthChanged(AActor* InstigatorActor, float NewHealth, float Delta);
};

在MyAttributeComponent.cpp的构造函数中设置组件可复制;在生命值变化函数中调用多播函数MulticastHealthChanged通知客户端血量的变化;实现多播函数;重写GetLifetimeReplicatedProps方法,设置该组件可以被网络同步的属性

UMyAttributeComponent::UMyAttributeComponent()
{
	MaxHealth = 100;
	Health = MaxHealth;
	//用于设置组件的默认Replication行为
	SetIsReplicatedByDefault(true);
}
bool UMyAttributeComponent::ApplyHealthChange(AActor* InstigatorActor, float Delta)
{
	// 广播触发委托
	//OnHealthChange.Broadcast(InstigatorActor,this, Health,ActualDelta);

	//如果实际变化量不为0,则通知客户端
	if (ActualDelta != 0.0f)
	{
		MulticastHealthChanged(InstigatorActor, Health, ActualDelta);
	}
}
//多播函数,用于通知客户端生命值发生变化
void UMyAttributeComponent::MulticastHealthChanged_Implementation(AActor* InstigatorActor, float NewHealth, float Delta)
{
	//广播生命值变化事件
	OnHealthChange.Broadcast(InstigatorActor, this, NewHealth, Delta);
}
//重写父类的函数,用于设置该组件可以被网络同步
void UMyAttributeComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	//声明要同步的属性
	DOREPLIFETIME(UMyAttributeComponent, Health);
	DOREPLIFETIME(UMyAttributeComponent, MaxHealth);
	//DOREPLIFETIME_CONDITION(UMyAttributeComponent, MaxHealth, COND_InitialOnly);
}
2.3 同步用户UI

在PlayerCharacter_BP中添加血量条Widget

添加widget

3、同步Action

在MyActionComponent中增加服务器RPC函数ServerStartAction,使得客户端也能够在服务器上启动某个动作。

//MyActionComponent.h
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class ACTIONROGUELIKE_API UMyActionComponent : public UActorComponent
{
protected:
    // 声明一个可靠的服务器 RPC 函数,用于在服务器上启动动作
    UFUNCTION(Server, Reliable)
    void ServerStartAction(AActor* Instigator, FName ActionName);
}
//MyActionComponent.cpp
UMyActionComponent::UMyActionComponent()
{
	PrimaryComponentTick.bCanEverTick = true;
	SetIsReplicatedByDefault(true);
}
bool UMyActionComponent::StartActionByName(AActor* Instigator, FName ActionName)
{
    // 遍历 Actions 数组,查找并运行指定名称ActionName的动作
    for (UMyAction* Action : Actions)
    {
        if (Action && Action->ActionName == ActionName)
        {
            // 如果当前组件不在服务器上,向服务器发送 RPC 启动动作
            if (!GetOwner()->HasAuthority())
            {
                ServerStartAction(Instigator, ActionName);
            }
            // 启动动作
            Action->StartAction(Instigator);
            return true;
        }
    }
    return false;
}
// 实现服务器 RPC 函数 ServerStartAction,调用 StartActionByName 启动动作
void UMyActionComponent::ServerStartAction_Implementation(AActor* Instigator, FName ActionName)
{
    StartActionByName(Instigator, ActionName);
}

4、效果演示

生命值网络同步效果演示

三、蓝图同步

1、爆炸油桶

设置爆炸油桶定时结束后爆炸,触发MulticastExplode。

爆炸油桶蓝图设置

设置爆炸事件的Replicates方式为多播

多播爆炸事件设置

2、效果演示

爆炸油桶网络同步效果演示


文章作者: Woilin
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Woilin !
评论
  目录