跳至主要內容

NT-2.3.2|网络同步PushModel

Mr.Si大约 3 分钟unreal

本章概要

ReplicatedUsing同步触发条件 蓝图和C++的区别

问题

头像
BABA!ReplicatedUsing到底怎么用?为什么我修改了某个背包中的物体堆叠数后并没有触发ReplicatedUsing函数?
头像
别急,我们先来用简单的Demo复刻一下你的问题

1. 准备一个组件设置为可复制

2. 新建一个数组变量,并且使用ReplicatedUsing标记为可复制。

3. 增加1个RPC,标记为Server执行,用于添加内容到数组

4. 客户端/服务端上触发函数

头像
无论是客户端还是服务端触发这个RPC,只要修改了数组内容都会触发ReplicatedUsing。提到了ReplicatedUsing我们来复习一下 ReplicatedUsing定义
头像
现在回到我们的问题,如果我们不去修改这个数组,而是改变数组中的变量会发生什么?

5.增加1个RPC,标记为Server执行,用于修改数组里的内容。

6.运行

GIF
头像
对对对!就是这样!后面无论怎么重复触发都不会打印改变的信息。
头像
数组这个变量只有在发生改变时才会触发复制,但数组内成员对象内的成员变量修改并不会触发数组这个变量变化。

解决方案

1. 本地使用Tick监听数组

头像
这个方案好像挺方便!就是挺消耗性能。
头像
是的Tick的方法可以一直检测数组,相当于每个Tick都在更新数组

2. 使用临时变量传递一次,触发数组变量改变。

3. 用其他变量代替。

4. C++ PushModel

头像

PushModel 机制时,复制系统依赖于开发者明确标记属性为脏来触发网络复制。这意味着即使属性发生了变化,如果没有手动标记,系统不会将其复制到客户端。

  1. **配置项目Config-DefaultEngine.ini **
[SystemSettings]
net.IsPushModelEnabled=1
net.PushModelSkipUndirtiedReplication=1
  1. Server.Target.cs中,bWithPushModel 也必须设置为 true。
public PushModelNetworkingServerTarget(TargetInfo Target) : base(Target)
{
	Type = TargetType.Server;
	
	bWithPushModel = true;
		
	//...
}
  1. 引入必要的头文件和模块引用
#include "Net/UnrealNetwork.h"
#include "Net/Core/PushModel/PushModel.h"

NetCore 模块引用添加到我们的 build.cs

PrivateDependencyModuleNames.AddRange(new string[] { "NetCore" });
  1. 定义类和属性

定义属性,确保被标记为可复制,并使用 ReplicatedUsing 指定相应的通知函数。

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "YourClass.generated.h"

UCLASS()
class YOURPROJECT_API AYourClass : public AActor
{
    GENERATED_BODY()

public:
    AYourClass();

protected:
    virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

    UPROPERTY(ReplicatedUsing = OnRep_Array)
    TArray<AActor*> ReplicatedArray;

    UFUNCTION()
    void OnRep_Array();
    
    UFUNCTION(Server, Reliable, WithValidation)
    void ServerModifyArray(AActor* NewActor);
};
  1. 实现复制通知函数

实现 OnRep_ArrayOnRep_ArrayTrigger 函数。

void AYourClass::OnRep_Array()
{
    // 处理数组变化后的逻辑
    UE_LOG(LogTemp, Warning, TEXT("Array has been replicated!"));
}
void AYourClass::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    FDoRepLifetimeParams SharedParams;
	SharedParams.bIsPushBased = true;
	
	DOREPLIFETIME_WITH_PARAMS_FAST(AYourClass, ReplicatedArray, SharedParams);
}
  1. 手动标记属性为脏

在修改数组内容时,使用 PushModel 手动标记属性为脏。

void AYourClass::ServerModifyArray(AActor* NewActor)
{
    if (HasAuthority())
    {
        // 修改数组内容
        MyReplicatedArray.Add(NewActor);

        // 手动标记数组属性为脏
        MARK_PROPERTY_DIRTY_FROM_NAME(AYourClass, ReplicatedArray, this);
        
    }
}

扩展阅读

官方论坛open in new window

大佬博客open in new window