博客

技术思考、排障与开发记录。

博客/UE开发

蓝图泛型结构体序列化:区分内存表示与持久化格式

结构体反射取参、JSON 解析与安全持久化边界。

返回
本页目录

UE:如何在蓝图中实现数据序列化与字节转换

前言

在UE项目中,网络通信、数据持久化、与外部程序交互是非常常见的需求。无论是通过UDP/TCP发送结构化数据,还是将游戏状态序列化为二进制存储,你都绑不开一个核心操作:将数据转换为字节数组。

然而在蓝图中,这件事并不简单:

  • 蓝图没有原生的字节转换节点,int32、float、double 都无法直接转 TArray<uint8>
  • 结构体序列化更是难题,CustomThunk 对纯蓝图用户来说门槛太高
  • UE自带的 FJsonObjectConverter 序列化浮点数精度丢失严重
  • 与 Python、C#、C++ 外部程序交换数据时,编码格式对不上

SimpleByteConversion 插件就是为了解决这些问题而生。

它提供了一套完整的蓝图节点,支持基础类型和任意结构体与字节数组/JSON字符串的双向转换,开箱即用。


一、核心功能

1.1 基础类型转换

插件覆盖了所有常用的基础类型,每种类型都提供 ToBytes 和 FromBytes 双向转换:

类型转字节从字节还原
int32IntToBytesBytesToInt
int64Int64ToBytesBytesToInt64
floatFloatToBytesBytesToFloat
doubleDoubleToBytesBytesToDouble
boolBoolToBytesBytesToBool
FStringStringToBytesBytesToString

所有节点均为 BlueprintPure,可以直接在蓝图中作为纯函数使用,无需执行引脚。

实现原理:

// 以 int32 为例,直接内存拷贝,零开销
void ByteConverter::IntToBytes(int32 Value, TArray<uint8>& OutBytes)
{
    OutBytes.SetNumUninitialized(sizeof(int32));
    FMemory::Memcpy(OutBytes.GetData(), &Value, sizeof(int32));
}

所有基础类型都采用 FMemory::Memcpy 直接内存拷贝,性能最优。字符串转换使用 UTF-8 编码,自动处理 Null Terminator,确保跨平台兼容。

1.2 结构体序列化

这是插件最强大的功能——支持任意蓝图结构体与字节数组的双向转换:

  • StructToBytes / BytesToStruct — 二进制序列化
  • StructToJsonString / JsonStringToStruct — JSON 字符串序列化

关键技术:CustomThunk

蓝图中无法直接传递泛型结构体,插件通过 CustomThunk + CustomStructureParam 实现了通用的结构体参数:

UFUNCTION(BlueprintPure, CustomThunk,
    meta = (CustomStructureParam = "Struct", DisplayName = "Struct To Bytes"),
    Category = "SimpleByteConversion|Struct")
static void StructToBytes(const int32& Struct, TArray<uint8>& Bytes);
DECLARE_FUNCTION(execStructToBytes);

这意味着你可以在蓝图中直接拖入任何自定义结构体,插件会自动识别其类型并完成序列化。

1.3 高精度 JSON 转换

UE 默认的 JSON 序列化存在两个问题:

  1. 字段名带有引擎内部后缀(如 MyField_5_A3B2C1D4)
  2. 浮点数精度丢失(float 只保留6位有效数字)

插件通过自定义 FExactPrintPolicy 和字段名清洗解决了这两个问题:

// 高精度输出:float 保留9位,double 保留17位
template <typename CharT>
struct FExactPrintPolicy : TCondensedJsonPrintPolicy<CharT>
{
    static void WriteFloat(FArchive* Stream, float V)
    {
        FString S = FString::Printf(TEXT("%.9f"), V);
        TCondensedJsonPrintPolicy<CharT>::WriteString(Stream, S);
    }
    static void WriteDouble(FArchive* Stream, double V)
    {
        FString S = FString::Printf(TEXT("%.17g"), V);
        TCondensedJsonPrintPolicy<CharT>::WriteString(Stream, S);
    }
};

输出的 JSON 字段名干净、数值精度完整,可以直接被 Python、C# 等外部程序解析。


二、使用场景

场景1:UDP/TCP 网络通信

当你使用 SimpleUDP 或 SimpleTCPClient 发送数据时,需要将结构化数据编码为字节数组:

1. 定义一个结构体(如 FPlayerState:位置、血量、状态)
2. 使用 StructToBytes 将结构体转为 TArray<uint8>
3. 通过 UDP/TCP 发送字节数组
4. 接收端使用 BytesToStruct 还原为结构体

场景2:与 Python 程序交换数据

使用 JSON 格式进行跨语言数据交换:

1. UE 端:StructToJsonString 将游戏数据转为 JSON 字符串
2. 通过 TCP/WebSocket 发送给 Python 程序
3. Python 端:json.loads() 直接解析
4. 反向同理:Python json.dumps() → UE JsonStringToStruct

场景3:数据持久化

将游戏存档序列化为二进制文件:

1. 将存档结构体通过 StructToBytes 转为字节数组
2. 使用 FFileHelper::SaveArrayToFile 写入磁盘
3. 读取时 FFileHelper::LoadFileToArray 加载字节数组
4. BytesToStruct 还原为存档结构体

三、快速上手

步骤1:安装插件

将插件文件夹复制到项目的 Plugins 目录,重启编辑器。

步骤2:在蓝图中使用

所有节点都在 SimpleByteConversion 分类下:

  • 基础类型:搜索 IntToBytes、FloatToBytes 等
  • 结构体:搜索 StructToBytes、StructToJsonString 等

步骤3:C++ 中使用

如果你在 C++ 中需要使用,可以直接调用 ByteConverter 工具类:

#include "ByteConverter.h"

// 基础类型
TArray<uint8> Bytes;
ByteConverter::IntToBytes(42, Bytes);

// 结构体
FMyStruct Data;
ByteConverter::StructToBytes(&Data, FMyStruct::StaticStruct(), Bytes);

// JSON
FString JsonStr;
ByteConverter::StructToJsonString(&Data, FMyStruct::StaticStruct(), JsonStr);

四、插件获取

适用版本: Unreal Engine 5.2+

获取方式: 在 Fab 商店搜索 SimpleByteConversion 即可找到

有任何问题欢迎在评论区交流。


总结

SimpleByteConversion 解决了 UE 蓝图中数据序列化的核心痛点:

  • 基础类型:int32/int64/float/double/bool/FString 与字节数组双向转换
  • 结构体:任意蓝图结构体的二进制和 JSON 序列化
  • 高精度:自定义输出策略,浮点数精度不丢失
  • 跨语言:干净的 JSON 输出,方便与外部程序交互

无论你是做网络通信、数据持久化还是跨语言数据交换,这个插件都能帮你省去大量的底层编码工作。


感谢阅读,欢迎点赞、关注、收藏,有问题可在评论区交流。
如果本文对你有帮助,点击这里捐赠支持作者。