文章目录
前言
在 Unreal Engine 中,FFastArraySerializer 和 FFastArraySerializerItem 是用于高效网络复制数组数据的工具类,尤其在需要频繁同步动态数组时(如背包系统、技能列表等场景)。以下通过一个 背包物品同步 的示例详细说明它们的用法。
提示:以下是本篇文章正文内容,下面案例可供参考
一、核心概念
- FFastArraySerializerItem: 表示数组中的单个元素,需要包含复制标识符 ReplicationID 和 ReplicationKey。
- FFastArraySerializer: 管理数组的容器类,负责处理网络复制逻辑和变化追踪。
二、实现步骤
2.1 定义 Item 结构体
// 继承自 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 定义数组容器
// 继承自 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 中使用
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 属性后调用,如:
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 中实现复制回调,响应数据变化:
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 能高效同步动态数组,适用于需要低延迟和高频率更新的游戏系统。