212 lines
7.0 KiB
Markdown
212 lines
7.0 KiB
Markdown
|
# 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的时候依然会是默认值,也并不会被复制。
|
|||
|
|
|||
|

|