Unreal Engine 高效数组复制用法详解

文章目录


前言

在 Unreal Engine 中,FFastArraySerializerFFastArraySerializerItem 是用于高效网络复制数组数据的工具类,尤其在需要频繁同步动态数组时(如背包系统、技能列表等场景)。以下通过一个 背包物品同步 的示例详细说明它们的用法。


提示:以下是本篇文章正文内容,下面案例可供参考

一、核心概念

  • FFastArraySerializerItem: 表示数组中的单个元素,需要包含复制标识符 ReplicationID 和 ReplicationKey。
  • FFastArraySerializer: 管理数组的容器类,负责处理网络复制逻辑和变化追踪。

二、实现步骤

2.1 定义 Item 结构体

cpp 复制代码
// 继承自 FFastArraySerializerItem
USTRUCT()
struct FInventoryItem : public FFastArraySerializerItem
{
    GENERATED_BODY()

    UPROPERTY()
    FName ItemID;       // 物品唯一ID

    UPROPERTY()
    int32 Quantity;     // 数量

    UPROPERTY()
    float Durability;   // 耐久度

    // 网络复制标识符(必须实现)
    void PreReplicatedRemove(const struct FInventoryArray& Serializer);
    void PostReplicatedAdd(const struct FInventoryArray& Serializer);
    void PostReplicatedChange(const struct FInventoryArray& Serializer);
};

2.2 定义数组容器

cpp 复制代码
// 继承自 FFastArraySerializer
USTRUCT()
struct FInventoryArray : public FFastArraySerializer
{
    GENERATED_BODY()

    UPROPERTY()
    TArray<FInventoryItem> Items;  // 物品数组

    // 必须重写:标识数组变化
    bool NetDeltaSerialize(FNetDeltaSerializeInfo& DeltaParms) override
    {
        return FFastArraySerializer::FastArrayDeltaSerialize<FInventoryItem, FInventoryArray>(Items, DeltaParms, *this);
    }

    // 示例方法:添加物品
    void AddItem(const FInventoryItem& NewItem)
    {
        Items.Add(NewItem);
        MarkItemDirty(Items.Last()); // 标记为脏数据以触发复制
    }

    // 示例方法:移除物品
    void RemoveItem(int32 Index)
    {
        if (Items.IsValidIndex(Index))
        {
            Items.RemoveAt(Index);
            MarkArrayDirty(); // 标记数组变化
        }
    }
};

2.3 在 Actor 或 Component 中使用

cpp 复制代码
UCLASS()
class AMyCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    // 复制的背包数据
    UPROPERTY(Replicated)
    FInventoryArray Inventory;

    // 注册复制属性
    virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override
    {
        Super::GetLifetimeReplicatedProps(OutLifetimeProps);
        DOREPLIFETIME(AMyCharacter, Inventory);
    }

    // 服务器端添加物品
    UFUNCTION(Server, Reliable)
    void Server_AddItem(FName ItemID, int32 Quantity);
    {
        FInventoryItem NewItem;
        NewItem.ItemID = ItemID;
        NewItem.Quantity = Quantity;
        NewItem.Durability = 100.f;
        Inventory.AddItem(NewItem);
    }
};

三、关键方法说明

3.1 MarkItemDirty()

  • 作用: 标记单个 Item 为"脏数据",触发网络复制。

  • 使用场景: 修改 Item 属性后调用,如:

cpp 复制代码
void ChangeItemQuantity(int32 Index, int32 NewQuantity)
{
    if (Inventory.Items.IsValidIndex(Index))
    {
        Inventory.Items[Index].Quantity = NewQuantity;
        Inventory.MarkItemDirty(Inventory.Items[Index]);
    }
}

3.2 MarkArrayDirty()

  • 作用: 标记整个数组变化(如添加/删除元素)。

  • 自动调用: 通过 AddItem() 或 RemoveItem() 间接调用。

四、回调函数处理

FInventoryItem 中实现复制回调,响应数据变化:

cpp 复制代码
void FInventoryItem::PostReplicatedAdd(const FInventoryArray& Serializer)
{
    // 客户端新增物品时触发(如更新UI)
    UMyUIWidget::Get()->RefreshInventory();
}

void FInventoryItem::PostReplicatedChange(const FInventoryArray& Serializer)
{
    // 客户端物品属性变化时触发(如更新数量显示)
    UMyUIWidget::Get()->UpdateItemDisplay(ItemID);
}

void FInventoryItem::PreReplicatedRemove(const FInventoryArray& Serializer)
{
    // 客户端移除物品前触发(如播放消失动画)
    UMyUIWidget::Get()->RemoveItemDisplay(ItemID);
}

五、网络复制流程

  1. 服务器修改数据 :调用 AddItem()RemoveItem() 或直接修改 Item 属性后标记脏数据。

  2. 引擎自动同步 :通过 NetDeltaSerialize 增量复制变化到客户端。

  3. 客户端触发回调 :根据操作类型(Add/Change/Remove)执行 PostReplicatedAdd 等函数。

六、注意事项

  • 仅在服务器修改数据 :客户端应通过 RPC(如 Server_AddItem)请求修改。

  • 避免频繁操作 :批量操作后调用一次 MarkArrayDirty() 减少带宽。

  • 自定义序列化 :如需优化,可重写 FInventoryItem::NetSerialize()

通过这种机制,FFastArraySerializer 能高效同步动态数组,适用于需要低延迟和高频率更新的游戏系统。

相关推荐
吴梓穆8 小时前
UE5 插件没有自动打包所需的dll
ue5
AllBlue8 小时前
unity嵌入安卓界面,如何显示状态
android·unity·游戏引擎
tealcwu9 小时前
【Unity技巧】实现在Play时自动保存当前场景
java·unity·游戏引擎
tealcwu9 小时前
【Unity基础】实现Scroll View跟随动态内容滚动
java·unity·游戏引擎
zhangzhangkeji9 小时前
UE5 蓝图-游老师-10-蓝图中的选择分支与循环:br , for loop, while loop
ue5
曼巴UE510 小时前
UE5 C++ 动态单播放
c++·ue5
技术小甜甜11 小时前
[Godot排错] 上传 Google Play Console 封闭测试时签名证书不匹配错误的解决方案
游戏引擎·godot·游戏开发
变身缎带19 小时前
Unity中的NetworkManager基于protobuf, Socket-TCP
tcp/ip·unity·游戏引擎
AllBlue1 天前
unity调用安卓方法
android·unity·游戏引擎
郝学胜-神的一滴1 天前
Horse3D游戏引擎研发笔记(十):在QtOpenGL环境下,视图矩阵与投影矩阵(摄像机)带你正式进入三维世界
c++·3d·unity·游戏引擎·godot·图形渲染·unreal engine