文章目录
- 前言
- 一、核心概念
- 二、实现步骤
-
- [2.1 定义 Item 结构体](#2.1 定义 Item 结构体)
- [2.2 定义数组容器](#2.2 定义数组容器)
- [2.3 在 Actor 或 Component 中使用](#2.3 在 Actor 或 Component 中使用)
- 三、关键方法说明
-
- [3.1 MarkItemDirty()](#3.1 MarkItemDirty())
- [3.2 MarkArrayDirty()](#3.2 MarkArrayDirty())
- 四、回调函数处理
- 五、网络复制流程
- 六、注意事项
前言
在 Unreal Engine 中,FFastArraySerializer 和 FFastArraySerializerItem 是用于高效网络复制数组数据的工具类,尤其在需要频繁同步动态数组时(如背包系统、技能列表等场景)。以下通过一个 背包物品同步 的示例详细说明它们的用法。
提示:以下是本篇文章正文内容,下面案例可供参考
一、核心概念
- 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);
}
五、网络复制流程
-
服务器修改数据 :调用 AddItem() 、RemoveItem() 或直接修改 Item 属性后标记脏数据。
-
引擎自动同步 :通过 NetDeltaSerialize 增量复制变化到客户端。
-
客户端触发回调 :根据操作类型(Add/Change/Remove)执行 PostReplicatedAdd 等函数。
六、注意事项
-
仅在服务器修改数据 :客户端应通过 RPC(如 Server_AddItem)请求修改。
-
避免频繁操作 :批量操作后调用一次 MarkArrayDirty() 减少带宽。
-
自定义序列化 :如需优化,可重写 FInventoryItem::NetSerialize()。
通过这种机制,FFastArraySerializer 能高效同步动态数组,适用于需要低延迟和高频率更新的游戏系统。