vault backup: 2024-10-12 17:19:45
@@ -0,0 +1,89 @@
|
||||
# DuplicateTransient
|
||||
|
||||
- **功能描述:** 在对象复制或COPY格式导出的时候,忽略该属性。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Serialization
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_DuplicateTransient](../../../../Flags/EPropertyFlags/CPF_DuplicateTransient.md)
|
||||
- **常用程度:** ★★
|
||||
|
||||
在对象复制或COPY格式导出的时候,忽略该属性。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_Serialization :public UDataAsset
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MyInt_Default = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_Transient | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient)
|
||||
int32 MyInt_Transient = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_DuplicateTransient | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, DuplicateTransient)
|
||||
int32 MyInt_DuplicateTransient = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_NonPIEDuplicateTransient | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, NonPIEDuplicateTransient)
|
||||
int32 MyInt_NonPIEDuplicateTransient = 123;
|
||||
};
|
||||
|
||||
void UMyProperty_Serialization_Test::RunTest()
|
||||
{
|
||||
|
||||
UMyProperty_Serialization* obj = NewObject<UMyProperty_Serialization>(GetTransientPackage());
|
||||
|
||||
obj->MyInt_Default = 456;
|
||||
obj->MyInt_Transient = 456;
|
||||
obj->MyInt_DuplicateTransient = 456;
|
||||
obj->MyInt_NonPIEDuplicateTransient = 456;
|
||||
|
||||
UMyProperty_Serialization* obj3= DuplicateObject<UMyProperty_Serialization>(obj,GetTransientPackage());
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||
复制蓝图,可以看到DuplicateTransient并不会被复制
|
||||
|
||||

|
||||
|
||||
在采用C++复制的时候:也看到MyInt_DuplicateTransient 并不会产生复制,还是123而不是456。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
在文本导出的时候,如果是T3D格式,则依然会导出。如果是COPY格式,则不导出。
|
||||
|
||||
```cpp
|
||||
bool FProperty::ShouldPort( uint32 PortFlags/*=0*/ ) const
|
||||
{
|
||||
// if we're copying, treat DuplicateTransient as transient
|
||||
if ((PortFlags & PPF_Copy) && HasAnyPropertyFlags(CPF_DuplicateTransient | CPF_TextExportTransient) && !(PortFlags & (PPF_ParsingDefaultProperties | PPF_IncludeTransient)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在二进制序列化的时候:
|
||||
|
||||
只有在PPF_Duplicate生效的时候,(DuplicateObject或者资产复制),才会跳过该属性
|
||||
|
||||
```cpp
|
||||
bool FProperty::ShouldSerializeValue(FArchive& Ar) const
|
||||
{
|
||||
// Skip properties marked DuplicateTransient when duplicating
|
||||
if ((PropertyFlags & CPF_DuplicateTransient) && (Ar.GetPortFlags() & PPF_Duplicate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在对资产进行Duplidate的时候,发生DuplicateAsset然后DuplicateObject,这个时候PortFlags=PPF_Duplicate,然后会触发ShouldSerializeValue进行判断。这个时候会跳过该属性
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,76 @@
|
||||
# Export
|
||||
|
||||
- **功能描述:** 在对Asset导出的时候,决定该类的对象应该导出内部的属性值,而是对象的路径。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Serialization
|
||||
- **限制类型:** Object属性,或Object数组
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_ExportObject](../../../../Flags/EPropertyFlags/CPF_ExportObject.md)
|
||||
- **常用程度:** ★
|
||||
|
||||
在对Asset导出的时候,决定该类的对象应该导出内部的属性值,而是对象的路径。
|
||||
|
||||
- 说明Object被复制时(例如复制/粘贴操作)指定到此属性的Object应整体导出为一个子Object块(后文的例子里会看到,其实就是也输出内部属性的值),而非只是输出Object引用本身。
|
||||
- 只适用于Object属性(或Object数组),因为是用在对象的导出的上的。
|
||||
- 其实就是浅复制和深复制的区别。不标Export就是浅复制,只输出对象路径。标上Export后是深复制,也输出对象的内部属性。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```jsx
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_ExportObject :public UDataAsset
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MyValue = 123;
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_Export :public UDataAsset
|
||||
{
|
||||
public:
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
UMyProperty_Export(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
||||
public:
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ExportObject | CPF_ZeroConstructor | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Export, Category = Behavior)
|
||||
UMyProperty_ExportObject* ObjectExport;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Behavior)
|
||||
UMyProperty_ExportObject* ObjectWithoutExport;
|
||||
};
|
||||
```
|
||||
|
||||
配置的对象值:
|
||||
|
||||

|
||||
|
||||
主要是用在Export 操作的时候,用来决定如何导出Object*属性的内容。NoExport的话是只输出对象引用的路径,而Export的话会输出这个对象其再内部的的属性值。
|
||||
|
||||

|
||||
|
||||
导出的文本:
|
||||
|
||||
```jsx
|
||||
Begin Object Class=/Script/Insider.MyProperty_Export Name="MyPropertyExportAsset" ExportPath=/Script/Insider.MyProperty_Export'"/Game/Property/MyPropertyExportAsset.MyPropertyExportAsset"'
|
||||
Begin Object Class=/Script/Insider.MyProperty_ExportObject Name="EO1" ExportPath=/Script/Insider.MyProperty_ExportObject'"/Game/Property/EO1.EO1"'
|
||||
"MyValue"=456
|
||||
End Object
|
||||
"ObjectExport"=/Script/Insider.MyProperty_ExportObject'"/Game/Property/EO1.EO1"'
|
||||
"ObjectWithoutExport"=/Script/Insider.MyProperty_ExportObject'"/Game/Property/EO2.EO2"'
|
||||
End Object
|
||||
```
|
||||
|
||||
可以看到ObjectExport的对象也导出的字段值,但是ObjectWithoutExport只有路径。
|
||||
|
||||
## 原理:
|
||||
|
||||
源码内作用的函数,要注意一点的是,要让Export标记在ExportProperties起作用,export标记不能用在对象的sub object上,否则会走ExportInnerObjects的调用路线。上面例子中ObjectExport和ObjectWithoutExport都是指向了外部的另外一个对象,所以用DataAsset来产生资产。
|
||||
|
||||
```cpp
|
||||
void ExportProperties()
|
||||
{
|
||||
FObjectPropertyBase* ExportObjectProp = (Property->PropertyFlags & CPF_ExportObject) != 0 ? CastField<FObjectPropertyBase>(Property) : NULL;
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 203 KiB |
After Width: | Height: | Size: 102 KiB |
@@ -0,0 +1,100 @@
|
||||
# NonPIEDuplicateTransient
|
||||
|
||||
- **功能描述:** 在对象复制的时候,且在不是PIE的场合,忽略该属性。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Serialization
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_NonPIEDuplicateTransient](../../../../Flags/EPropertyFlags/CPF_NonPIEDuplicateTransient.md)
|
||||
- **常用程度:** ★
|
||||
|
||||
在对象复制的时候,且在不是PIE的场合,忽略该属性。
|
||||
|
||||
- DuplicateTransient和NonPIEDuplicateTransient的区别是,前者在任何情况的对象复制时都忽略该属性,而后者在PIE的时候(也是在发生对象复制过程)依然会复制该属性,其他情况下的复制和前者行为一致。
|
||||
- PIE的时候本质就是把当前的编辑World里Actor复制一份到PIE的世界里,会触发Actor的复制。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
准备了一份DataAsset和Actor来分别验证复制行为的不同。
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_Serialization :public UDataAsset
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MyInt_Default = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_Transient | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient)
|
||||
int32 MyInt_Transient = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_DuplicateTransient | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, DuplicateTransient)
|
||||
int32 MyInt_DuplicateTransient = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_NonPIEDuplicateTransient | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, NonPIEDuplicateTransient)
|
||||
int32 MyInt_NonPIEDuplicateTransient = 123;
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyProperty_Serialization_TestActor :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MyInt_Default = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_Transient | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient)
|
||||
int32 MyInt_Transient = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_DuplicateTransient | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, DuplicateTransient)
|
||||
int32 MyInt_DuplicateTransient = 123;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, NonPIEDuplicateTransient)
|
||||
int32 MyInt_NonPIEDuplicateTransient = 123;
|
||||
};
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||
在对资产进行Duplidate的时候,发生DuplicateAsset然后DuplicateObject,这个时候PortFlags=PPF_Duplicate,然后会触发ShouldSerializeValue进行判断。这个时候会跳过该属性。
|
||||
|
||||
可以看到NonPIEDuplicateTransient并不会被复制。
|
||||
|
||||

|
||||
|
||||
在点击PIE的时候,可以看到NonPIEDuplicateTransient这个时候却是会复制值过去了。这是因为这个时候PortFlags=PPF_DuplicateForPIE&PPF_Duplicate
|
||||
|
||||

|
||||
|
||||
结论是用于一些Cache数据,在复制的时候并不需要序列化复制,这样可以阻止两个不同的Actor采用同一份计算后的临时数据。但是又可以在PIE的时候,让Actor各自采用自己的一份数据,因为PIE的时候,本质就是把当前的编辑World里Actor复制一份到PIE的世界里,会触发Actor的复制。
|
||||
|
||||
## 原理:
|
||||
|
||||
在文本导出的的时候,在不是PIE里的复制的时候,不序列化该属性。
|
||||
|
||||
```cpp
|
||||
bool FProperty::ShouldPort( uint32 PortFlags/*=0*/ ) const
|
||||
{
|
||||
// if we're not copying for PIE and NonPIETransient is set, don't export
|
||||
if (!(PortFlags & PPF_DuplicateForPIE) && HasAnyPropertyFlags(CPF_NonPIEDuplicateTransient))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在二进制序列化的时候:
|
||||
|
||||
只有在PPF_Duplicate生效的时候,(DuplicateObject?或者资产复制),才会跳过该属性。但是在PIE的时候,又要继续序列化。
|
||||
|
||||
```cpp
|
||||
bool FProperty::ShouldSerializeValue(FArchive& Ar) const
|
||||
{
|
||||
// Skip properties marked NonPIEDuplicateTransient when duplicating, but not when we're duplicating for PIE
|
||||
if ((PropertyFlags & CPF_NonPIEDuplicateTransient) && !(Ar.GetPortFlags() & PPF_DuplicateForPIE) && (Ar.GetPortFlags() & PPF_Duplicate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 54 KiB |
@@ -0,0 +1,174 @@
|
||||
# SaveGame
|
||||
|
||||
- **功能描述:** 在SaveGame存档的时候,只序列化有SaveGame标记的属性,而不序列化别的属性。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Serialization
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
在SaveGame存档的时候,只序列化有SaveGame标记的属性,而不序列化别的属性。
|
||||
|
||||
特别的标识哪些属性是用于存档保存的。
|
||||
|
||||
对于子结构或子对象属性,也必须要加上SaveGame标记。
|
||||
|
||||
NoExportTypes.h里的很多基础结构内部属性都被标上了SaveGame。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
struct FMySaveGameArchive : public FObjectAndNameAsStringProxyArchive
|
||||
{
|
||||
FMySaveGameArchive (FArchive& InInnerArchive)
|
||||
: FObjectAndNameAsStringProxyArchive(InInnerArchive)
|
||||
{
|
||||
ArIsSaveGame = true;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FMySaveGameStruct
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FString MyString_Default;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite,SaveGame)
|
||||
FString MyString_SaveGame;
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_SaveGame :public USaveGame
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MyInt_Default = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_SaveGame | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame)
|
||||
int32 MyInt_SaveGame = 123;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite,SaveGame)
|
||||
FMySaveGameStruct MyStruct;
|
||||
};
|
||||
|
||||
UMyProperty_SaveGame* UMyProperty_SaveGame_Test::LoadGameFromMemory(const TArray<uint8>& InSaveData)
|
||||
{
|
||||
FMemoryReader MemoryReader(InSaveData, true);
|
||||
|
||||
FObjectAndNameAsStringProxyArchive Ar(MemoryReader, true);
|
||||
Ar.ArIsSaveGame = true;//必须手动加上这个标记
|
||||
|
||||
UMyProperty_SaveGame* OutSaveGameObject = NewObject<UMyProperty_SaveGame>(GetTransientPackage(), UMyProperty_SaveGame::StaticClass());
|
||||
OutSaveGameObject->Serialize(Ar);
|
||||
|
||||
return OutSaveGameObject;
|
||||
}
|
||||
|
||||
bool UMyProperty_SaveGame_Test::SaveGameToMemory(UMyProperty_SaveGame* SaveGameObject, TArray<uint8>& OutSaveData)
|
||||
{
|
||||
FMemoryWriter MemoryWriter(OutSaveData, true);
|
||||
|
||||
// Then save the object state, replacing object refs and names with strings
|
||||
FObjectAndNameAsStringProxyArchive Ar(MemoryWriter, false);
|
||||
Ar.ArIsSaveGame = true;//必须手动加上这个标记
|
||||
SaveGameObject->Serialize(Ar);
|
||||
|
||||
return true; // Not sure if there's a failure case here.
|
||||
}
|
||||
|
||||
void UMyProperty_SaveGame_Test::RunTest()
|
||||
{
|
||||
UMyProperty_SaveGame* saveGame = Cast<UMyProperty_SaveGame>(UGameplayStatics::CreateSaveGameObject(UMyProperty_SaveGame::StaticClass()));
|
||||
saveGame->MyInt_Default = 456;
|
||||
saveGame->MyInt_SaveGame = 456;
|
||||
saveGame->MyStruct.MyString_Default = TEXT("Hello");
|
||||
saveGame->MyStruct.MyString_SaveGame = TEXT("Hello");
|
||||
|
||||
TArray<uint8> outBytes;
|
||||
UMyProperty_SaveGame_Test::SaveGameToMemory(saveGame, outBytes);
|
||||
|
||||
UMyProperty_SaveGame* saveGame2 = UMyProperty_SaveGame_Test::LoadGameFromMemory(outBytes);
|
||||
}
|
||||
```
|
||||
|
||||
测试结果,只有SaveGame标记的属性这个值才序列化进去。
|
||||
|
||||

|
||||
|
||||
等价于在蓝图的细节面板里表示:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
只在ArIsSaveGame的时候检测这个标记,意味着这个标记只在检测USaveGame的对象的子对象结构属性的时候才用。但是ArIsSaveGame需要自己手动设置为true,否则默认这个机制是不工作的。实现的一种方式是自己手动加上一句Ar.ArIsSaveGame = true;,或者自己自定义一个FMySaveGameArchive来进行序列化。
|
||||
|
||||
在源码里发现UEnhancedInputUserSettings也是继承于USaveGame,采用存档的方式保存的。
|
||||
|
||||
```cpp
|
||||
bool FProperty::ShouldSerializeValue(FArchive& Ar) const
|
||||
{
|
||||
// Skip the property if the archive says we should
|
||||
if (Ar.ShouldSkipProperty(this))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip non-SaveGame properties if we're saving game state
|
||||
if (!(PropertyFlags & CPF_SaveGame) && Ar.IsSaveGame())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64 SkipFlags = CPF_Transient | CPF_DuplicateTransient | CPF_NonPIEDuplicateTransient | CPF_NonTransactional | CPF_Deprecated | CPF_DevelopmentAssets | CPF_SkipSerialization;
|
||||
if (!(PropertyFlags & SkipFlags))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Skip properties marked Transient when persisting an object, unless we're saving an archetype
|
||||
if ((PropertyFlags & CPF_Transient) && Ar.IsPersistent() && !Ar.IsSerializingDefaults())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip properties marked DuplicateTransient when duplicating
|
||||
if ((PropertyFlags & CPF_DuplicateTransient) && (Ar.GetPortFlags() & PPF_Duplicate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip properties marked NonPIEDuplicateTransient when duplicating, but not when we're duplicating for PIE
|
||||
if ((PropertyFlags & CPF_NonPIEDuplicateTransient) && !(Ar.GetPortFlags() & PPF_DuplicateForPIE) && (Ar.GetPortFlags() & PPF_Duplicate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip properties marked NonTransactional when transacting
|
||||
if ((PropertyFlags & CPF_NonTransactional) && Ar.IsTransacting())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip deprecated properties when saving or transacting, unless the archive has explicitly requested them
|
||||
if ((PropertyFlags & CPF_Deprecated) && !Ar.HasAllPortFlags(PPF_UseDeprecatedProperties) && (Ar.IsSaving() || Ar.IsTransacting() || Ar.WantBinaryPropertySerialization()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip properties marked SkipSerialization, unless the archive is forcing them
|
||||
if ((PropertyFlags & CPF_SkipSerialization) && (Ar.WantBinaryPropertySerialization() || !Ar.HasAllPortFlags(PPF_ForceTaggedSerialization)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip editor-only properties when the archive is rejecting them
|
||||
if (IsEditorOnlyProperty() && Ar.IsFilterEditorOnly())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise serialize!
|
||||
return true;
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,99 @@
|
||||
# SkipSerialization
|
||||
|
||||
- **功能描述:** 二进制序列化时跳过该属性,但在ExportText的时候依然可以导出。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Serialization
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_SkipSerialization](../../../../Flags/EPropertyFlags/CPF_SkipSerialization.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
在进行普通的二进制序列化的时候,这个标记会阻止序列化。作用和Transient一样。但如果是ExportText,则依然可以把该属性导出。其内部用的ExportProperties。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_SerializationText :public UObject
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MyInt_Default= 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic | CPF_SkipSerialization
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite,SkipSerialization)
|
||||
int32 MyInt_SkipSerialization = 123;
|
||||
};
|
||||
|
||||
void UMyProperty_SerializationText_Test::RunTest()
|
||||
{
|
||||
UMyProperty_SerializationText* obj = NewObject<UMyProperty_SerializationText>(GetTransientPackage());
|
||||
|
||||
obj->MyInt_Default = 456;
|
||||
obj->MyInt_SkipSerialization = 456;
|
||||
|
||||
//save obj
|
||||
TArray<uint8> outBytes;
|
||||
FMemoryWriter MemoryWriter(outBytes, true);
|
||||
FObjectAndNameAsStringProxyArchive Ar(MemoryWriter, false);
|
||||
obj->Serialize(Ar);
|
||||
|
||||
//load
|
||||
FMemoryReader MemoryReader(outBytes, true);
|
||||
|
||||
FObjectAndNameAsStringProxyArchive Ar2(MemoryReader, true);
|
||||
|
||||
UMyProperty_SerializationText* obj2 = NewObject<UMyProperty_SerializationText>(GetTransientPackage());
|
||||
obj2->Serialize(Ar2);
|
||||
}
|
||||
```
|
||||
|
||||
此时可见测试结果,该属性并没有被序列化进去。
|
||||
|
||||

|
||||
|
||||
如果采用ExportText导出:T3D或COPY格式都行
|
||||
|
||||
```cpp
|
||||
UMyProperty_SerializationText* obj = NewObject<UMyProperty_SerializationText>(GetTransientPackage());
|
||||
|
||||
obj->MyInt_Default = 456;
|
||||
obj->MyInt_SkipSerialization = 456;
|
||||
|
||||
FStringOutputDevice Ar;
|
||||
UExporter::ExportToOutputDevice(nullptr, obj, nullptr,Ar,TEXT("T3D"), 3);
|
||||
```
|
||||
|
||||
则输出结果为:
|
||||
|
||||
```cpp
|
||||
Begin Object Class=/Script/Insider.MyProperty_SerializationText Name="MyProperty_SerializationText_0" ExportPath=/Script/Insider.MyProperty_SerializationText'"/Engine/Transient.MyProperty_SerializationText_0"'
|
||||
MyInt_Default=456
|
||||
MyInt_SkipSerialization=456
|
||||
End Object
|
||||
```
|
||||
|
||||
另外如果在编辑器里右击复制
|
||||
|
||||

|
||||
|
||||
也可以产生文本的导出:
|
||||
|
||||
```cpp
|
||||
{
|
||||
"Tagged": [
|
||||
[
|
||||
"MyInt_Default",
|
||||
"456"
|
||||
],
|
||||
[
|
||||
"MyInt_SkipSerialization",
|
||||
"456"
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
注意在判断一个Property是否应该序列化的时候,ShouldSerializeValue函数是用在普通的序列化的时候用来判断的。而在ExportText的时候,是用ShouldPort判断的。
|
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,154 @@
|
||||
# TextExportTransient
|
||||
|
||||
- **功能描述:** 在ExportText导出为.COPY格式的时候,忽略该属性。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Serialization
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_TextExportTransient](../../../Flags/EPropertyFlags/CPF_TextExportTransient.md)
|
||||
- **常用程度:** ★
|
||||
|
||||
在ExportText导出为.COPY格式的时候,忽略该属性。
|
||||
|
||||
但鼠标复制拷贝属性依然会有文本导出生效。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_SerializationText :public UDataAsset
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MyInt_Default= 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic | CPF_SkipSerialization
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite,SkipSerialization)
|
||||
int32 MyInt_SkipSerialization = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_TextExportTransient | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite,TextExportTransient)
|
||||
int32 MyInt_TextExportTransient= 123;
|
||||
|
||||
};
|
||||
|
||||
void UMyProperty_SerializationText_Test::RunExportTest()
|
||||
{
|
||||
UMyProperty_SerializationText* obj = NewObject<UMyProperty_SerializationText>(GetTransientPackage());
|
||||
|
||||
obj->MyInt_Default = 456;
|
||||
obj->MyInt_SkipSerialization = 456;
|
||||
obj->MyInt_TextExportTransient = 456;
|
||||
|
||||
FStringOutputDevice Ar;
|
||||
UExporter::ExportToOutputDevice(nullptr, obj, nullptr,Ar,TEXT("T3D"), 3);
|
||||
|
||||
FStringOutputDevice Ar2;
|
||||
UExporter::ExportToOutputDevice(nullptr, obj, nullptr,Ar,TEXT("COPY"), 3);
|
||||
|
||||
FString str=Ar;
|
||||
}
|
||||
```
|
||||
|
||||
导出的结果:
|
||||
|
||||
```cpp
|
||||
T3D格式:
|
||||
Begin Object Class=/Script/Insider.MyProperty_SerializationText Name="BP_SerializationText" ExportPath="/Script/Insider.MyProperty_SerializationText'/Game/Property/BP_SerializationText.BP_SerializationText'"
|
||||
MyInt_Default=456
|
||||
MyInt_SkipSerialization=456
|
||||
MyInt_TextExportTransient=456
|
||||
End Object
|
||||
|
||||
COPY格式:
|
||||
Begin Object Class=/Script/Insider.MyProperty_SerializationText Name="BP_SerializationText" ExportPath="/Script/Insider.MyProperty_SerializationText'/Game/Property/BP_SerializationText.BP_SerializationText'"
|
||||
MyInt_Default=456
|
||||
MyInt_SkipSerialization=456
|
||||
End Object
|
||||
```
|
||||
|
||||
复制拷贝依然会有文本生效:
|
||||
|
||||
```cpp
|
||||
{
|
||||
"Tagged": [
|
||||
[
|
||||
"MyInt_Default",
|
||||
"456"
|
||||
],
|
||||
[
|
||||
"MyInt_SkipSerialization",
|
||||
"456"
|
||||
],
|
||||
[
|
||||
"MyInt_TextExportTransient",
|
||||
"456"
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
因此可以发现在COPY格式的时候,MyInt_TextExportTransient并没有被导出。
|
||||
|
||||
## 原理:
|
||||
|
||||
注意在判断一个Property是否应该序列化的时候,ShouldSerializeValue函数是用在普通的序列化的时候用来判断的。而在ExportText的时候,是用ShouldPort判断的。
|
||||
|
||||
但是如果序列化出的格式是COPY,在设置PortFlags的时候,会额外的加上PPF_Copy。因此在后续的判断里才会生效对CPF_TextExportTransient的判断。
|
||||
|
||||
```cpp
|
||||
if ( FCString::Stricmp(FileType, TEXT("COPY")) == 0 )
|
||||
{
|
||||
// some code which doesn't have access to the exporter's file type needs to handle copy/paste differently than exporting to file,
|
||||
// so set the export flag accordingly
|
||||
PortFlags |= PPF_Copy;
|
||||
}
|
||||
|
||||
//
|
||||
// Return whether the property should be exported.
|
||||
//
|
||||
bool FProperty::ShouldPort( uint32 PortFlags/*=0*/ ) const
|
||||
{
|
||||
// if no size, don't export
|
||||
if (GetSize() <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HasAnyPropertyFlags(CPF_Deprecated) && !(PortFlags & (PPF_ParsingDefaultProperties | PPF_UseDeprecatedProperties)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we're parsing default properties or the user indicated that transient properties should be included
|
||||
if (HasAnyPropertyFlags(CPF_Transient) && !(PortFlags & (PPF_ParsingDefaultProperties | PPF_IncludeTransient)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we're copying, treat DuplicateTransient as transient
|
||||
if ((PortFlags & PPF_Copy) && HasAnyPropertyFlags(CPF_DuplicateTransient | CPF_TextExportTransient) && !(PortFlags & (PPF_ParsingDefaultProperties | PPF_IncludeTransient)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we're not copying for PIE and NonPIETransient is set, don't export
|
||||
if (!(PortFlags & PPF_DuplicateForPIE) && HasAnyPropertyFlags(CPF_NonPIEDuplicateTransient))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we're only supposed to export components and this isn't a component property, don't export
|
||||
if ((PortFlags & PPF_SubobjectsOnly) && !ContainsInstancedObjectProperty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// hide non-Edit properties when we're exporting for the property window
|
||||
if ((PortFlags & PPF_PropertyWindow) && !(PropertyFlags & CPF_Edit))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
@@ -0,0 +1,212 @@
|
||||
# Transient
|
||||
|
||||
- **功能描述:** 不序列化该属性,该属性初始化时候会被0填充。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Serialization
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_Transient](../../../../Flags/EPropertyFlags/CPF_Transient.md)
|
||||
- **常用程度:★★★★★**
|
||||
|
||||
序列化的时候略过该属性,用0来填充默认值。
|
||||
|
||||
二进制和文本都不序列化该属性。
|
||||
|
||||
一般用于一些临时中间变量或计算后的结果变量。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_Serialization :public UDataAsset
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MyInt_Default = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_Transient | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient)
|
||||
int32 MyInt_Transient = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_DuplicateTransient | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, DuplicateTransient)
|
||||
int32 MyInt_DuplicateTransient = 123;
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_NonPIEDuplicateTransient | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, NonPIEDuplicateTransient)
|
||||
int32 MyInt_NonPIEDuplicateTransient = 123;
|
||||
};
|
||||
void UMyProperty_Serialization_Test::RunTest()
|
||||
{
|
||||
UMyProperty_Serialization* obj = NewObject<UMyProperty_Serialization>(GetTransientPackage());
|
||||
|
||||
obj->MyInt_Default = 456;
|
||||
obj->MyInt_Transient = 456;
|
||||
obj->MyInt_DuplicateTransient = 456;
|
||||
obj->MyInt_NonPIEDuplicateTransient = 456;
|
||||
|
||||
//save obj
|
||||
TArray<uint8> outBytes;
|
||||
FMemoryWriter MemoryWriter(outBytes, true);
|
||||
FObjectAndNameAsStringProxyArchive Ar(MemoryWriter, false);
|
||||
obj->Serialize(Ar);
|
||||
|
||||
//load
|
||||
FMemoryReader MemoryReader(outBytes, true);
|
||||
|
||||
FObjectAndNameAsStringProxyArchive Ar2(MemoryReader, true);
|
||||
|
||||
UMyProperty_Serialization* obj2 = NewObject<UMyProperty_Serialization>(GetTransientPackage());
|
||||
obj2->Serialize(Ar2);
|
||||
}
|
||||
```
|
||||
|
||||
对这么一个BP DataAsset进行AssetActions→Export,
|
||||
|
||||
T3D格式:
|
||||
|
||||
```cpp
|
||||
Begin Object Class=/Script/Insider.MyProperty_Serialization Name="BP_Serialization" ExportPath="/Script/Insider.MyProperty_Serialization'/Game/Property/BP_Serialization.BP_Serialization'"
|
||||
MyInt_Default=456
|
||||
MyInt_DuplicateTransient=456
|
||||
End Object
|
||||
```
|
||||
|
||||
COPY格式:
|
||||
|
||||
```cpp
|
||||
Begin Object Class=/Script/Insider.MyProperty_Serialization Name="BP_Serialization" ExportPath="/Script/Insider.MyProperty_Serialization'/Game/Property/BP_Serialization.BP_Serialization'"
|
||||
MyInt_Default=456
|
||||
End Object
|
||||
```
|
||||
|
||||
如果是普通的序列化:
|
||||
|
||||
可见obj2的MyInt_Transient 属性并没有从序列化中获得新值456.
|
||||
|
||||

|
||||
|
||||
## 原理代码:
|
||||
|
||||
判断CPF_Transient的生效,只有在IsPersistent()的时候,并且不是在保存CDO。SetIsPersistent()的调用在很多地方都出现,比如在MemoryReader/MemoryWriter都是IsPersistent。
|
||||
|
||||
因此Transient是在序列化的时候会被忽略。
|
||||
|
||||
在ExportText的时候发现会进行CPF_Transient的判断,除非强制进行包括PPF_IncludeTransient
|
||||
|
||||
```cpp
|
||||
bool FProperty::ShouldSerializeValue(FArchive& Ar) const
|
||||
{
|
||||
// Skip the property if the archive says we should
|
||||
if (Ar.ShouldSkipProperty(this))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip non-SaveGame properties if we're saving game state
|
||||
if (!(PropertyFlags & CPF_SaveGame) && Ar.IsSaveGame())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64 SkipFlags = CPF_Transient | CPF_DuplicateTransient | CPF_NonPIEDuplicateTransient | CPF_NonTransactional | CPF_Deprecated | CPF_DevelopmentAssets | CPF_SkipSerialization;
|
||||
if (!(PropertyFlags & SkipFlags))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Skip properties marked Transient when persisting an object, unless we're saving an archetype
|
||||
if ((PropertyFlags & CPF_Transient) && Ar.IsPersistent() && !Ar.IsSerializingDefaults())
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip properties marked DuplicateTransient when duplicating
|
||||
if ((PropertyFlags & CPF_DuplicateTransient) && (Ar.GetPortFlags() & PPF_Duplicate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip properties marked NonPIEDuplicateTransient when duplicating, but not when we're duplicating for PIE
|
||||
if ((PropertyFlags & CPF_NonPIEDuplicateTransient) && !(Ar.GetPortFlags() & PPF_DuplicateForPIE) && (Ar.GetPortFlags() & PPF_Duplicate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip properties marked NonTransactional when transacting
|
||||
if ((PropertyFlags & CPF_NonTransactional) && Ar.IsTransacting())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip deprecated properties when saving or transacting, unless the archive has explicitly requested them
|
||||
if ((PropertyFlags & CPF_Deprecated) && !Ar.HasAllPortFlags(PPF_UseDeprecatedProperties) && (Ar.IsSaving() || Ar.IsTransacting() || Ar.WantBinaryPropertySerialization()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip properties marked SkipSerialization, unless the archive is forcing them
|
||||
if ((PropertyFlags & CPF_SkipSerialization) && (Ar.WantBinaryPropertySerialization() || !Ar.HasAllPortFlags(PPF_ForceTaggedSerialization)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip editor-only properties when the archive is rejecting them
|
||||
if (IsEditorOnlyProperty() && Ar.IsFilterEditorOnly())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise serialize!
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
bool FProperty::ShouldPort( uint32 PortFlags/*=0*/ ) const
|
||||
{
|
||||
// if no size, don't export
|
||||
if (GetSize() <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HasAnyPropertyFlags(CPF_Deprecated) && !(PortFlags & (PPF_ParsingDefaultProperties | PPF_UseDeprecatedProperties)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we're parsing default properties or the user indicated that transient properties should be included
|
||||
if (HasAnyPropertyFlags(CPF_Transient) && !(PortFlags & (PPF_ParsingDefaultProperties | PPF_IncludeTransient)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we're copying, treat DuplicateTransient as transient
|
||||
if ((PortFlags & PPF_Copy) && HasAnyPropertyFlags(CPF_DuplicateTransient | CPF_TextExportTransient) && !(PortFlags & (PPF_ParsingDefaultProperties | PPF_IncludeTransient)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we're not copying for PIE and NonPIETransient is set, don't export
|
||||
if (!(PortFlags & PPF_DuplicateForPIE) && HasAnyPropertyFlags(CPF_NonPIEDuplicateTransient))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we're only supposed to export components and this isn't a component property, don't export
|
||||
if ((PortFlags & PPF_SubobjectsOnly) && !ContainsInstancedObjectProperty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// hide non-Edit properties when we're exporting for the property window
|
||||
if ((PortFlags & PPF_PropertyWindow) && !(PropertyFlags & CPF_Edit))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
因为不序列Transient属性,因此该属性修改值也并不会被保存起来。打开Asset的时候依然会是默认值,也并不会被复制。
|
||||
|
||||

|
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 28 KiB |