vault backup: 2024-10-12 17:19:45
@@ -0,0 +1,55 @@
|
||||
# AssetRegistrySearchable
|
||||
|
||||
- **功能描述:** 标记该属性可以作为AssetRegistry的Tag和Value值来进行资产的过滤搜索
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Asset
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_AssetRegistrySearchable](../../../../Flags/EPropertyFlags/CPF_AssetRegistrySearchable.md),在Meta中加入[RequiredAssetDataTags](../../../../Meta/Asset/RequiredAssetDataTags/RequiredAssetDataTags.md)、[DisallowedAssetDataTags](../../../../Meta/Asset/DisallowedAssetDataTags.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
不能用在结构属性上。
|
||||
|
||||
子类也可以重载GetAssetRegistryTags以提供自定义的Tag。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_AssetRegistrySearchable :public UDataAsset
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, AssetRegistrySearchable, Category = DataRegistry)
|
||||
FString MyIdForSearch;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 OtherValue = 123;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataRegistry)
|
||||
FString MyOtherIdForSearch;
|
||||
public:
|
||||
virtual void GetAssetRegistryTags(FAssetRegistryTagsContext Context) const override
|
||||
{
|
||||
//called on CDO and instances
|
||||
Super::GetAssetRegistryTags(Context);
|
||||
Context.AddTag(FAssetRegistryTag(TEXT("MyOtherId"),MyOtherIdForSearch, UObject::FAssetRegistryTag::TT_Alphabetical));
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 测试结果:
|
||||
|
||||
在EditorUtilityWidget中测试,可见ListAssetByTagValue都可以搜索找到该Asset。
|
||||
|
||||

|
||||
|
||||
测试的蓝图代码,也可用IAssetRegistry::Get()->GetAssetsByTagValues(tagValues,outAssets);来进行搜索,不过要注意搜索的时机要在AssetRegistry加载之后,AssetRegistry如果是Runtime要记得序列化到磁盘
|
||||
|
||||
```cpp
|
||||
//DefaultEngine.ini
|
||||
[AssetRegistry]
|
||||
bSerializeAssetRegistry=true
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
可查看GetAssetRegistryTags的函数的实现和调用。在UObject::GetAssetRegistryTags中调用使用,把该属性的值作为AssetData的Tag供给AssetRegistry
|
After Width: | Height: | Size: 289 KiB |
@@ -0,0 +1,6 @@
|
||||
# Localized
|
||||
|
||||
- **功能描述:** 此属性的值将拥有一个定义的本地化值。多用于字符串。暗示为 ReadOnly。该值有一个本地化值。最常标记在string上
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Behavior
|
||||
- **限制类型:** FString
|
@@ -0,0 +1,36 @@
|
||||
# BlueprintAssignable
|
||||
|
||||
- **功能描述:** 在蓝图中可以为这个多播委托绑定事件
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint
|
||||
- **限制类型:** Multicast Delegates
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_BlueprintAssignable](../../../../Flags/EPropertyFlags/CPF_BlueprintAssignable.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
## C++的测试代码:
|
||||
|
||||
```cpp
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyDynamicMulticastDelegate_One, int32, Value);
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, BlueprintAssignable, BlueprintCallable)
|
||||
FMyDynamicMulticastDelegate_One MyMulticastDelegateAssignAndCall;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, BlueprintCallable)
|
||||
FMyDynamicMulticastDelegate_One MyMulticastDelegateCall;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, BlueprintAssignable)
|
||||
FMyDynamicMulticastDelegate_One MyMulticastDelegateAssign;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FMyDynamicMulticastDelegate_One MyMulticastDelegate;
|
||||
|
||||
```
|
||||
|
||||
## 蓝图中的表现:
|
||||
|
||||

|
||||
|
||||
因此一般建议二者标记都加上:
|
||||
|
||||

|
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,23 @@
|
||||
# BlueprintAuthorityOnly
|
||||
|
||||
- **功能描述:** 只能绑定为BlueprintAuthorityOnly的事件,让该多播委托只接受在服务端运行的事件
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint, Network
|
||||
- **限制类型:** Multicast Delegates
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_BlueprintAuthorityOnly](../../../../Flags/EPropertyFlags/CPF_BlueprintAuthorityOnly.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, BlueprintAssignable, BlueprintCallable)
|
||||
FMyDynamicMulticastDelegate_One MyMulticastDelegateAssignAndCall;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, BlueprintAssignable, BlueprintCallable, BlueprintAuthorityOnly)
|
||||
FMyDynamicMulticastDelegate_One MyMulticastDelegateAuthorityOnly;
|
||||
```
|
||||
|
||||
## 蓝图中表现:
|
||||
|
||||

|
After Width: | Height: | Size: 96 KiB |
@@ -0,0 +1,47 @@
|
||||
# BlueprintCallable
|
||||
|
||||
- **功能描述:** 在蓝图中可以调用这个多播委托
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint
|
||||
- **限制类型:** Multicast Delegates
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_BlueprintCallable](../../../../Flags/EPropertyFlags/CPF_BlueprintCallable.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
在蓝图中可以调用这个多播委托。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyDynamicMulticastDelegate_One, int32, Value);
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, BlueprintAssignable, BlueprintCallable)
|
||||
FMyDynamicMulticastDelegate_One MyMulticastDelegateAssignAndCall;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, BlueprintCallable)
|
||||
FMyDynamicMulticastDelegate_One MyMulticastDelegateCall;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, BlueprintAssignable)
|
||||
FMyDynamicMulticastDelegate_One MyMulticastDelegateAssign;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FMyDynamicMulticastDelegate_One MyMulticastDelegate;
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||

|
||||
|
||||
注意BlueprintAssignable和BlueprintCallable只能用于多播委托:
|
||||
|
||||
```cpp
|
||||
DECLARE_DYNAMIC_DELEGATE_OneParam(FMyDynamicSinglecastDelegate_One, int32, Value);
|
||||
|
||||
//编译报错:'BlueprintCallable' is only allowed on a property when it is a multicast delegate
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, BlueprintCallable)
|
||||
FMyDynamicSinglecastDelegate_One MyMyDelegate4;
|
||||
|
||||
//编译报错:'BlueprintAssignable' is only allowed on a property when it is a multicast delegate
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, BlueprintAssignable)
|
||||
FMyDynamicSinglecastDelegate_One MyMyDelegate5;
|
||||
```
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,42 @@
|
||||
# BlueprintGetter
|
||||
|
||||
- **功能描述:** 为属性定义一个自定义的Get函数来读取。
|
||||
|
||||
- **元数据类型:** string="abc"
|
||||
- **引擎模块:** Blueprint
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_BlueprintReadOnly](../../../../Flags/EPropertyFlags/CPF_BlueprintReadOnly.md)、[CPF_BlueprintVisible](../../../../Flags/EPropertyFlags/CPF_BlueprintVisible.md),在Meta中加入[BlueprintGetter](../../../../Meta/Blueprint/BlueprintGetter.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
为属性定义一个自定义的Get函数来读取。
|
||||
如果没有设置BlueprintSetter或BlueprintReadWrite,则会默认设置BlueprintReadOnly,这个属性变成只读的。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
public:
|
||||
//(BlueprintGetter = , Category = Blueprint, ModuleRelativePath = Property/MyProperty_Test.h)
|
||||
UFUNCTION(BlueprintGetter, Category = Blueprint) //or BlueprintPure
|
||||
int32 MyInt_Getter()const { return MyInt_WithGetter * 2; }
|
||||
|
||||
//(BlueprintSetter = , Category = Blueprint, ModuleRelativePath = Property/MyProperty_Test.h)
|
||||
UFUNCTION(BlueprintSetter, Category = Blueprint) //or BlueprintCallable
|
||||
void MyInt_Setter(int NewValue) { MyInt_WithSetter = NewValue / 4; }
|
||||
private:
|
||||
//(BlueprintGetter = MyInt_Getter, Category = Blueprint, ModuleRelativePath = Property/MyProperty_Test.h)
|
||||
//PropertyFlags: CPF_BlueprintVisible | CPF_BlueprintReadOnly | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPrivate
|
||||
UPROPERTY(BlueprintGetter = MyInt_Getter, Category = Blueprint)
|
||||
int32 MyInt_WithGetter = 123;
|
||||
|
||||
//(BlueprintSetter = MyInt_Setter, Category = Blueprint, ModuleRelativePath = Property/MyProperty_Test.h)
|
||||
//PropertyFlags: CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPrivate
|
||||
UPROPERTY(BlueprintSetter = MyInt_Setter, Category = Blueprint)
|
||||
int32 MyInt_WithSetter = 123;
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||
可见MyInt_WithGetter是只读的。
|
||||
|
||||
而MyInt_WithSetter 是可读写的。
|
||||
|
||||

|
After Width: | Height: | Size: 39 KiB |
@@ -0,0 +1,69 @@
|
||||
# BlueprintReadOnly
|
||||
|
||||
- **功能描述:** 此属性可由蓝图读取,但不能被修改。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_BlueprintVisible](../../../../Flags/EPropertyFlags/CPF_BlueprintVisible.md), [CPF_BlueprintReadOnly](../../../../Flags/EPropertyFlags/CPF_BlueprintReadOnly.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
此属性可由蓝图读取,但不能被修改。此说明符与 BlueprintReadWrite 说明符不兼容。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
public:
|
||||
//PropertyFlags: CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(BlueprintReadWrite, Category = Blueprint)
|
||||
int32 MyInt_ReadWrite = 123;
|
||||
//PropertyFlags: CPF_BlueprintVisible | CPF_BlueprintReadOnly | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(BlueprintReadOnly, Category = Blueprint)
|
||||
int32 MyInt_ReadOnly = 123;
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||
指定蓝图中只读:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
有CPF_BlueprintVisible 就可以Get
|
||||
|
||||
加上CPF_BlueprintReadOnly 后就不能修改。
|
||||
|
||||
```cpp
|
||||
EPropertyAccessResultFlags PropertyAccessUtil::CanGetPropertyValue(const FProperty* InProp)
|
||||
{
|
||||
if (!InProp->HasAnyPropertyFlags(CPF_Edit | CPF_BlueprintVisible | CPF_BlueprintAssignable))
|
||||
{
|
||||
return EPropertyAccessResultFlags::PermissionDenied | EPropertyAccessResultFlags::AccessProtected;
|
||||
}
|
||||
|
||||
return EPropertyAccessResultFlags::Success;
|
||||
}
|
||||
|
||||
FBlueprintEditorUtils::EPropertyWritableState FBlueprintEditorUtils::IsPropertyWritableInBlueprint(const UBlueprint* Blueprint, const FProperty* Property)
|
||||
{
|
||||
if (Property)
|
||||
{
|
||||
if (!Property->HasAnyPropertyFlags(CPF_BlueprintVisible))
|
||||
{
|
||||
return EPropertyWritableState::NotBlueprintVisible;
|
||||
}
|
||||
if (Property->HasAnyPropertyFlags(CPF_BlueprintReadOnly))
|
||||
{
|
||||
return EPropertyWritableState::BlueprintReadOnly;
|
||||
}
|
||||
if (Property->GetBoolMetaData(FBlueprintMetadata::MD_Private))
|
||||
{
|
||||
const UClass* OwningClass = Property->GetOwnerChecked<UClass>();
|
||||
if (OwningClass->ClassGeneratedBy.Get() != Blueprint)
|
||||
{
|
||||
return EPropertyWritableState::Private;
|
||||
}
|
||||
}
|
||||
}
|
||||
return EPropertyWritableState::Writable;
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 45 KiB |
@@ -0,0 +1,47 @@
|
||||
# BlueprintReadWrite
|
||||
|
||||
- **功能描述:** 可从蓝图读取或写入此属性。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_BlueprintVisible](../../../../Flags/EPropertyFlags/CPF_BlueprintVisible.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
可从蓝图读取或写入此属性。
|
||||
|
||||
此说明符与 BlueprintReadOnly 说明符不兼容。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
public:
|
||||
//PropertyFlags: CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(BlueprintReadWrite, Category = Blueprint)
|
||||
int32 MyInt_ReadWrite = 123;
|
||||
//PropertyFlags: CPF_BlueprintVisible | CPF_BlueprintReadOnly | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(BlueprintReadOnly, Category = Blueprint)
|
||||
int32 MyInt_ReadOnly = 123;
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||
蓝图中可读写:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
如果有CPF_Edit | CPF_BlueprintVisible | CPF_BlueprintAssignable之一,则可以Get属性。
|
||||
|
||||
```cpp
|
||||
EPropertyAccessResultFlags PropertyAccessUtil::CanGetPropertyValue(const FProperty* InProp)
|
||||
{
|
||||
if (!InProp->HasAnyPropertyFlags(CPF_Edit | CPF_BlueprintVisible | CPF_BlueprintAssignable))
|
||||
{
|
||||
return EPropertyAccessResultFlags::PermissionDenied | EPropertyAccessResultFlags::AccessProtected;
|
||||
}
|
||||
|
||||
return EPropertyAccessResultFlags::Success;
|
||||
}
|
||||
|
||||
```
|
After Width: | Height: | Size: 45 KiB |
@@ -0,0 +1,58 @@
|
||||
# BlueprintSetter
|
||||
|
||||
- **功能描述:** 采用一个自定义的set函数来读取。
|
||||
|
||||
- **元数据类型:** string="abc"
|
||||
- **引擎模块:** Blueprint
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_BlueprintVisible](../../../Flags/EPropertyFlags/CPF_BlueprintVisible.md),在Meta中加入[BlueprintSetter](../../../Meta/Blueprint/BlueprintSetter.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
采用一个自定义的set函数来读取。
|
||||
|
||||
会默认设置BlueprintReadWrite。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
public:
|
||||
//(BlueprintGetter = , Category = Blueprint, ModuleRelativePath = Property/MyProperty_Test.h)
|
||||
UFUNCTION(BlueprintGetter, Category = Blueprint) //or BlueprintPure
|
||||
int32 MyInt_Getter()const { return MyInt_WithGetter * 2; }
|
||||
|
||||
//(BlueprintSetter = , Category = Blueprint, ModuleRelativePath = Property/MyProperty_Test.h)
|
||||
UFUNCTION(BlueprintSetter, Category = Blueprint) //or BlueprintCallable
|
||||
void MyInt_Setter(int NewValue) { MyInt_WithSetter = NewValue / 4; }
|
||||
private:
|
||||
//(BlueprintGetter = MyInt_Getter, Category = Blueprint, ModuleRelativePath = Property/MyProperty_Test.h)
|
||||
//PropertyFlags: CPF_BlueprintVisible | CPF_BlueprintReadOnly | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPrivate
|
||||
UPROPERTY(BlueprintGetter = MyInt_Getter, Category = Blueprint)
|
||||
int32 MyInt_WithGetter = 123;
|
||||
|
||||
//(BlueprintSetter = MyInt_Setter, Category = Blueprint, ModuleRelativePath = Property/MyProperty_Test.h)
|
||||
//PropertyFlags: CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPrivate
|
||||
UPROPERTY(BlueprintSetter = MyInt_Setter, Category = Blueprint)
|
||||
int32 MyInt_WithSetter = 123;
|
||||
```
|
||||
|
||||
## 蓝图表现:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
如果有MD_PropertySetFunction则用它来作为Set的调用。
|
||||
|
||||
```cpp
|
||||
void UK2Node_VariableSet::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
|
||||
{
|
||||
// If property has a BlueprintSetter accessor, then replace the variable get node with a call function
|
||||
if (VariableProperty)
|
||||
{
|
||||
// todo check with BP team if we need to test if the variable has native Setter
|
||||
const FString& SetFunctionName = VariableProperty->GetMetaData(FBlueprintMetadata::MD_PropertySetFunction);
|
||||
if (!SetFunctionName.IsEmpty())
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,109 @@
|
||||
# Getter
|
||||
|
||||
- **功能描述:** 为属性增加一个C++的Get函数,只在C++层面应用。
|
||||
|
||||
- **元数据类型:** string="abc"
|
||||
- **引擎模块:** Blueprint
|
||||
- **常用程度:** ★★★
|
||||
|
||||
为属性增加一个C++的Get函数,只在C++层面应用。
|
||||
|
||||
- Getter上如不提供函数名,那就用默认的GetXXX的名字。也可以提供另外一个函数名。
|
||||
- 这些Getter函数是不加UFUNCTION的,这点要和BlueprintGetter区分。
|
||||
- 感觉更好的名字是NativeGetter。
|
||||
- GetXXX的函数必须自己手写,否则UHT会报错。
|
||||
- 我们当然也可以自己写GetSet函数,不需要写Getter和Setter的元数据。但写上Getter和Settter的好处是,万一在项目里别的地方,用到了反射来获取和设置值,这个时候如果没有标上Getter和Setter,就会直接从属性上获取值,从而跳过我们想要的自定义Get/Set流程。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
public:
|
||||
//GetterFunc: Has Native Getter
|
||||
UPROPERTY(BlueprintReadWrite, Getter)
|
||||
float MyFloat = 1.0f;
|
||||
|
||||
//GetterFunc: Has Native Getter
|
||||
UPROPERTY(BlueprintReadWrite, Getter = GetMyCustomFloat)
|
||||
float MyFloat2 = 1.0f;
|
||||
public:
|
||||
float GetMyFloat()const { return MyFloat + 100.f; }
|
||||
|
||||
float GetMyCustomFloat()const { return MyFloat2 + 100.f; }
|
||||
|
||||
void UMyProperty_Get::RunTest()
|
||||
{
|
||||
float Value1=MyFloat;
|
||||
|
||||
FProperty* prop=GetClass()->FindPropertyByName(TEXT("MyFloat"));
|
||||
float Value2=0.f;
|
||||
|
||||
prop->GetValue_InContainer(this,&Value2);
|
||||
}
|
||||
```
|
||||
|
||||
## 蓝图表现:
|
||||
|
||||
在测试的时候,可见如果是用GetValue_InContainer这种反射的方式来获取值,就会自动的调用到GetMyFloat,从而返回不同的值。
|
||||
|
||||
在蓝图里直接Get MyFloat 是依然是1.
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
UHT在分析Getter标记后,会在gen.cpp里生成相应的函数包装。在构建FProperty的时候,就会创建TPropertyWithSetterAndGetter,之后在GetSingleValue_InContainer的时候就会调用到CallGetter。
|
||||
|
||||
```cpp
|
||||
void UMyProperty_Get::GetMyFloat_WrapperImpl(const void* Object, void* OutValue)
|
||||
{
|
||||
const UMyProperty_Get* Obj = (const UMyProperty_Get*)Object;
|
||||
float& Result = *(float*)OutValue;
|
||||
Result = (float)Obj->GetMyFloat();
|
||||
}
|
||||
|
||||
const UECodeGen_Private::FFloatPropertyParams Z_Construct_UClass_UMyProperty_Get_Statics::NewProp_MyFloat = { "MyFloat", nullptr, (EPropertyFlags)0x0010000000000004, UECodeGen_Private::EPropertyGenFlags::Float, RF_Public|RF_Transient|RF_MarkAsNative, nullptr, &UMyProperty_Get::GetMyFloat_WrapperImpl, 1, STRUCT_OFFSET(UMyProperty_Get, MyFloat), METADATA_PARAMS(UE_ARRAY_COUNT(NewProp_MyFloat_MetaData), NewProp_MyFloat_MetaData) };
|
||||
|
||||
template <typename PropertyType, typename PropertyParamsType>
|
||||
PropertyType* NewFProperty(FFieldVariant Outer, const FPropertyParamsBase& PropBase)
|
||||
{
|
||||
const PropertyParamsType& Prop = (const PropertyParamsType&)PropBase;
|
||||
PropertyType* NewProp = nullptr;
|
||||
|
||||
if (Prop.SetterFunc || Prop.GetterFunc)
|
||||
{
|
||||
NewProp = new TPropertyWithSetterAndGetter<PropertyType>(Outer, Prop);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewProp = new PropertyType(Outer, Prop);
|
||||
}
|
||||
}
|
||||
|
||||
void FProperty::GetSingleValue_InContainer(const void* InContainer, void* OutValue, int32 ArrayIndex) const
|
||||
{
|
||||
checkf(ArrayIndex <= ArrayDim, TEXT("ArrayIndex (%d) must be less than the property %s array size (%d)"), ArrayIndex, *GetFullName(), ArrayDim);
|
||||
if (!HasGetter())
|
||||
{
|
||||
// Fast path - direct memory access
|
||||
CopySingleValue(OutValue, ContainerVoidPtrToValuePtrInternal((void*)InContainer, ArrayIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ArrayDim == 1)
|
||||
{
|
||||
// Slower but no mallocs. We can copy the value directly to the resulting param
|
||||
CallGetter(InContainer, OutValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Malloc a temp value that is the size of the array. Getter will then copy the entire array to the temp value
|
||||
uint8* ValueArray = (uint8*)AllocateAndInitializeValue();
|
||||
GetValue_InContainer(InContainer, ValueArray);
|
||||
// Copy the item we care about and free the temp array
|
||||
CopySingleValue(OutValue, ValueArray + ArrayIndex * ElementSize);
|
||||
DestroyAndFreeValue(ValueArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
@@ -0,0 +1,114 @@
|
||||
# Setter
|
||||
|
||||
- **功能描述:** 为属性增加一个C++的Set函数,只在C++层面应用。
|
||||
- **元数据类型:** string="abc"
|
||||
- **引擎模块:** Blueprint
|
||||
- **关联项:** [Getter](../Getter.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
为属性增加一个C++的Set函数,只在C++层面应用。
|
||||
|
||||
- Getter上如不提供函数名,那就用默认的SetXXX的名字。也可以提供另外一个函数名。
|
||||
- 这些Getter函数是不加UFUNCTION的,这点要和BlueprintGetter区分。
|
||||
- 感觉更好的名字是NativeSetter。
|
||||
- SetXXX的函数必须自己手写,否则UHT会报错。
|
||||
- 我们当然也可以自己写GetSet函数,不需要写Getter和Setter的元数据。但写上Getter和Settter的好处是,万一在项目里别的地方,用到了反射来获取和设置值,这个时候如果没有标上Getter和Setter,就会直接从属性上获取值,从而跳过我们想要的自定义Get/Set流程。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, Setter)
|
||||
float MyFloat = 1.0f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Setter = SetMyCustomFloat)
|
||||
float MyFloat2 = 1.0f;
|
||||
public:
|
||||
void SetMyFloat(float val) { MyFloat = val + 100.f; }
|
||||
void SetMyCustomFloat(float val) { MyFloat2 = val + 100.f; }
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void RunTest();
|
||||
};
|
||||
|
||||
void UMyProperty_Set::RunTest()
|
||||
{
|
||||
float OldValue=MyFloat;
|
||||
|
||||
FProperty* prop=GetClass()->FindPropertyByName(TEXT("MyFloat"));
|
||||
const float Value2=20.f;
|
||||
|
||||
prop->SetValue_InContainer(this,&Value2);
|
||||
|
||||
float NewValue=MyFloat;
|
||||
}
|
||||
```
|
||||
|
||||
## 蓝图表现:
|
||||
|
||||
在测试的时候,可见如果是用SetValue_InContainer这种反射的方式来获取值,就会自动的调用到SetMyFloat,从而实际上设置到不同的值。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
UHT在分析Setter标记后,会在gen.cpp里生成相应的函数包装。在构建FProperty的时候,就会创建TPropertyWithSetterAndGetter,之后在GetSingleValue_InContainer的时候就会调用到CallGetter。
|
||||
|
||||
```cpp
|
||||
void UMyProperty_Set::SetMyFloat_WrapperImpl(void* Object, const void* InValue)
|
||||
{
|
||||
UMyProperty_Set* Obj = (UMyProperty_Set*)Object;
|
||||
float& Value = *(float*)InValue;
|
||||
Obj->SetMyFloat(Value);
|
||||
}
|
||||
|
||||
const UECodeGen_Private::FFloatPropertyParams Z_Construct_UClass_UMyProperty_Set_Statics::NewProp_MyFloat = { "MyFloat", nullptr, (EPropertyFlags)0x0010000000000004, UECodeGen_Private::EPropertyGenFlags::Float, RF_Public|RF_Transient|RF_MarkAsNative, &UMyProperty_Set::SetMyFloat_WrapperImpl, nullptr, 1, STRUCT_OFFSET(UMyProperty_Set, MyFloat), METADATA_PARAMS(UE_ARRAY_COUNT(NewProp_MyFloat_MetaData), NewProp_MyFloat_MetaData) };
|
||||
|
||||
template <typename PropertyType, typename PropertyParamsType>
|
||||
PropertyType* NewFProperty(FFieldVariant Outer, const FPropertyParamsBase& PropBase)
|
||||
{
|
||||
const PropertyParamsType& Prop = (const PropertyParamsType&)PropBase;
|
||||
PropertyType* NewProp = nullptr;
|
||||
|
||||
if (Prop.SetterFunc || Prop.GetterFunc)
|
||||
{
|
||||
NewProp = new TPropertyWithSetterAndGetter<PropertyType>(Outer, Prop);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewProp = new PropertyType(Outer, Prop);
|
||||
}
|
||||
}
|
||||
|
||||
void FProperty::SetSingleValue_InContainer(void* OutContainer, const void* InValue, int32 ArrayIndex) const
|
||||
{
|
||||
checkf(ArrayIndex <= ArrayDim, TEXT("ArrayIndex (%d) must be less than the property %s array size (%d)"), ArrayIndex, *GetFullName(), ArrayDim);
|
||||
if (!HasSetter())
|
||||
{
|
||||
// Fast path - direct memory access
|
||||
CopySingleValue(ContainerVoidPtrToValuePtrInternal((void*)OutContainer, ArrayIndex), InValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ArrayDim == 1)
|
||||
{
|
||||
// Slower but no mallocs. We can copy the value directly to the resulting param
|
||||
CallSetter(OutContainer, InValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Malloc a temp value that is the size of the array. We will then copy the entire array to the temp value
|
||||
uint8* ValueArray = (uint8*)AllocateAndInitializeValue();
|
||||
GetValue_InContainer(OutContainer, ValueArray);
|
||||
// Replace the value at the specified index in the temp array with the InValue
|
||||
CopySingleValue(ValueArray + ArrayIndex * ElementSize, InValue);
|
||||
// Now call a setter to replace the entire array and then destroy the temp value
|
||||
CallSetter(OutContainer, ValueArray);
|
||||
DestroyAndFreeValue(ValueArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 368 KiB |
After Width: | Height: | Size: 316 KiB |
@@ -0,0 +1,13 @@
|
||||
# Config
|
||||
|
||||
- **功能描述:** 指定该属性是一个配置属性,该属性可以被序列化读写到ini文件(路径由uclass的config标签指定)中。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Config
|
||||
- **作用机制:** [CPF_Config](../../../Flags/EPropertyFlags/CPF_Config.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
指定该属性是一个配置属性,该属性可以被序列化读写到ini文件(路径由uclass的config标签指定)中。
|
||||
|
||||
在载入的时候会自动从ini中加载。如果没再加写标记,则会隐含该属性为ReadOnly。
|
||||
|
||||
参见UCLASS中的config标记的示例代码和效果。
|
@@ -0,0 +1,130 @@
|
||||
# GlobalConfig
|
||||
|
||||
- **功能描述:** 和Config一样指定该属性可作为配置读取和写入ini中,但只会读取写入到配置文件里基类的值,而不会使用配置文件里子类里的值。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Config
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_GlobalConfig](../../../../Flags/EPropertyFlags/CPF_GlobalConfig.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
和Config一样指定该属性可作为配置读取和写入ini中,但只会读取写入到配置文件里基类的值,而不会使用配置文件里子类里的值。
|
||||
|
||||
但是不同点在于,该属性在LoadConfig的时候,只会读取基类的ini,而不会去读取子类的ini。因为只有基类里的Ini设置在生效,相当于全局只有一个配置在生效,因此名字叫做GlobalConfig。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Config = MyOtherGame)
|
||||
class INSIDER_API UMyProperty_Config :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MyProperty = 123;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Config)
|
||||
int32 MyPropertyWithConfig = 123;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, GlobalConfig)
|
||||
int32 MyPropertyWithGlobalConfig = 123;
|
||||
};
|
||||
|
||||
UCLASS(Config = MyOtherGame)
|
||||
class INSIDER_API UMyProperty_Config_Child :public UMyProperty_Config
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
};
|
||||
|
||||
void UMyProperty_Config_Test::TestConfigSave()
|
||||
{
|
||||
FString fileName = FPaths::ProjectConfigDir() / TEXT("MyOtherGame.ini");
|
||||
fileName = FConfigCacheIni::NormalizeConfigIniPath(fileName);
|
||||
|
||||
{
|
||||
UMyProperty_Config* testObject = NewObject<UMyProperty_Config>(GetTransientPackage(), TEXT("testObject"));
|
||||
|
||||
testObject->MyProperty = 777;
|
||||
testObject->MyPropertyWithConfig = 777;
|
||||
testObject->MyPropertyWithGlobalConfig = 777;
|
||||
|
||||
testObject->SaveConfig(CPF_Config, *fileName);
|
||||
}
|
||||
|
||||
{
|
||||
UMyProperty_Config_Child* testObject = NewObject<UMyProperty_Config_Child>(GetTransientPackage(), TEXT("testObjectChild"));
|
||||
|
||||
testObject->MyProperty = 888;
|
||||
testObject->MyPropertyWithConfig = 888;
|
||||
testObject->MyPropertyWithGlobalConfig = 888;
|
||||
|
||||
testObject->SaveConfig(CPF_Config, *fileName);
|
||||
}
|
||||
}
|
||||
|
||||
void UMyProperty_Config_Test::TestConfigLoad()
|
||||
{
|
||||
FString fileName = FPaths::ProjectConfigDir() / TEXT("MyOtherGame.ini");
|
||||
fileName = FConfigCacheIni::NormalizeConfigIniPath(fileName);
|
||||
|
||||
UMyProperty_Config* testObject = NewObject<UMyProperty_Config>(GetTransientPackage(), TEXT("testObject"));
|
||||
testObject->LoadConfig(nullptr, *fileName);
|
||||
|
||||
UMyProperty_Config_Child* testObjectChild = NewObject<UMyProperty_Config_Child>(GetTransientPackage(), TEXT("testObjectChild"));
|
||||
testObjectChild->LoadConfig(nullptr, *fileName);
|
||||
}
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||
TestConfigSave之后,MyPropertyWithGlobalConfig=888,可见保存的时候也只会保存在基类上。
|
||||
|
||||
```cpp
|
||||
[/Script/Insider.MyProperty_Config]
|
||||
MyPropertyWithConfig=777
|
||||
MyPropertyWithGlobalConfig=888
|
||||
|
||||
[/Script/Insider.MyProperty_Config_Child]
|
||||
MyPropertyWithConfig=888
|
||||
```
|
||||
|
||||
为了测试,假如手动把配置里的值改为:然后再进行TestConfigLoad测试
|
||||
|
||||
```cpp
|
||||
[/Script/Insider.MyProperty_Config]
|
||||
MyPropertyWithConfig=777
|
||||
MyPropertyWithGlobalConfig=888
|
||||
|
||||
[/Script/Insider.MyProperty_Config_Child]
|
||||
MyPropertyWithConfig=888
|
||||
MyPropertyWithGlobalConfig=999
|
||||
```
|
||||
|
||||
显示效果:
|
||||
|
||||
可见testObjectChild 的值并没有使用ini里MyProperty_Config_Child下的999的值,而是同样的888.
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
如果是bGlobalConfig ,会采用基类。
|
||||
|
||||
```cpp
|
||||
void UObject::LoadConfig( UClass* ConfigClass/*=NULL*/, const TCHAR* InFilename/*=NULL*/, uint32 PropagationFlags/*=LCPF_None*/, FProperty* PropertyToLoad/*=NULL*/ )
|
||||
{
|
||||
const bool bGlobalConfig = (Property->PropertyFlags&CPF_GlobalConfig) != 0;
|
||||
UClass* OwnerClass = Property->GetOwnerClass();
|
||||
|
||||
UClass* BaseClass = bGlobalConfig ? OwnerClass : ConfigClass;
|
||||
if ( !bPerObject )
|
||||
{
|
||||
ClassSection = BaseClass->GetPathName();
|
||||
LongCommitName = BaseClass->GetOutermost()->GetFName();
|
||||
|
||||
// allow the class to override the expected section name
|
||||
OverrideConfigSection(ClassSection);
|
||||
}
|
||||
|
||||
// globalconfig properties should always use the owning class's config file
|
||||
// specifying a value for InFilename will override this behavior (as it does with normal properties)
|
||||
const FString& PropFileName = (bGlobalConfig && InFilename == NULL) ? OwnerClass->GetConfigName() : Filename;
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 44 KiB |
@@ -0,0 +1,43 @@
|
||||
# AdvancedDisplay
|
||||
|
||||
- **功能描述:** 被折叠到高级栏下,要手动打开。一般用在不太常用的属性上面。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** DetailsPanel, Editor
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_AdvancedDisplay](../../../../Flags/EPropertyFlags/CPF_AdvancedDisplay.md)
|
||||
- **常用程度:★★★★★**
|
||||
|
||||
被折叠到高级栏下,要手动打开。一般用在不太常用的属性上面。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_Test :public UObject
|
||||
{
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_SimpleDisplay | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, SimpleDisplay, Category = Display)
|
||||
int32 MyInt_SimpleDisplay = 123;
|
||||
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_AdvancedDisplay | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = Display)
|
||||
int32 MyInt_AdvancedDisplay = 123;
|
||||
}
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
如果CPF_AdvancedDisplay,bAdvanced =true
|
||||
|
||||
```cpp
|
||||
void FPropertyNode::InitNode(const FPropertyNodeInitParams& InitParams)
|
||||
{
|
||||
// Property is advanced if it is marked advanced or the entire class is advanced and the property not marked as simple
|
||||
static const FName Name_AdvancedClassDisplay("AdvancedClassDisplay");
|
||||
bool bAdvanced = Property.IsValid() ? ( Property->HasAnyPropertyFlags(CPF_AdvancedDisplay) || ( !Property->HasAnyPropertyFlags( CPF_SimpleDisplay ) && Property->GetOwnerClass() && Property->GetOwnerClass()->GetBoolMetaData(Name_AdvancedClassDisplay) ) ) : false;
|
||||
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,53 @@
|
||||
# Category
|
||||
|
||||
- **功能描述:** 指定属性的类别,使用 | 运算符定义嵌套类目。
|
||||
- **元数据类型:** strings=“a|b|c”
|
||||
- **引擎模块:** DetailsPanel, Editor
|
||||
- **作用机制:** 在Meta中加入[Category](../../../../Meta/DetailsPanel/Category.md)
|
||||
- **常用程度:★★★★★**
|
||||
|
||||
指定属性的类别,使用 | 运算符定义嵌套类目。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_Test :public UObject
|
||||
{
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_SimpleDisplay | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, SimpleDisplay, Category = Display)
|
||||
int32 MyInt_SimpleDisplay = 123;
|
||||
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_AdvancedDisplay | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = Display)
|
||||
int32 MyInt_AdvancedDisplay = 123;
|
||||
public:
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, Category = Edit)
|
||||
int32 MyInt_EditAnywhere = 123;
|
||||
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_DisableEditOnInstance | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditDefaultsOnly, Category = Edit)
|
||||
int32 MyInt_EditDefaultsOnly = 123;
|
||||
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_DisableEditOnTemplate | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditInstanceOnly, Category = Edit)
|
||||
int32 MyInt_EditInstanceOnly = 123;
|
||||
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_EditConst | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(VisibleAnywhere, Category = Edit)
|
||||
int32 MyInt_VisibleAnywhere = 123;
|
||||
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_DisableEditOnInstance | CPF_EditConst | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(VisibleDefaultsOnly, Category = Edit)
|
||||
int32 MyInt_VisibleDefaultsOnly = 123;
|
||||
}
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
比较简单,把值设置到meta里的Category,之后读取出来使用。
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,50 @@
|
||||
# EditAnywhere
|
||||
|
||||
- **功能描述:** 在默认值和实例的细节面板上均可编辑
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** DetailsPanel, Editor
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_Edit](../../../../Flags/EPropertyFlags/CPF_Edit.md)
|
||||
- **常用程度:★★★★★**
|
||||
|
||||
在默认值和实例的细节面板上均可编辑。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_Test :public UObject
|
||||
{
|
||||
public:
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, Category = Edit)
|
||||
int32 MyInt_EditAnywhere = 123;
|
||||
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_DisableEditOnInstance | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditDefaultsOnly, Category = Edit)
|
||||
int32 MyInt_EditDefaultsOnly = 123;
|
||||
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_DisableEditOnTemplate | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditInstanceOnly, Category = Edit)
|
||||
int32 MyInt_EditInstanceOnly = 123;
|
||||
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_EditConst | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(VisibleAnywhere, Category = Edit)
|
||||
int32 MyInt_VisibleAnywhere = 123;
|
||||
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_DisableEditOnInstance | CPF_EditConst | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(VisibleDefaultsOnly, Category = Edit)
|
||||
int32 MyInt_VisibleDefaultsOnly = 123;
|
||||
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_DisableEditOnTemplate | CPF_EditConst | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(VisibleInstanceOnly, Category = Edit)
|
||||
int32 MyInt_VisibleInstanceOnly = 123;
|
||||
}
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
CPF_Edit在源码里有非常多的使用,决定了很多地方属性是否可以显示和编辑。有兴趣可以自己去搜搜CPF_Edit的使用。
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,9 @@
|
||||
# EditDefaultsOnly
|
||||
|
||||
- **功能描述:** 只能在默认值面板里编辑
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** DetailsPanel, Editor
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_Edit](../../../Flags/EPropertyFlags/CPF_Edit.md), [CPF_DisableEditOnInstance](../../../Flags/EPropertyFlags/CPF_DisableEditOnInstance.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
一并参见EditAnywhere里的示例代码和效果。
|
@@ -0,0 +1,52 @@
|
||||
# EditFixedSize
|
||||
|
||||
- **功能描述:** 在细节面板上不允许改变该容器的元素个数。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** DetailsPanel, Editor
|
||||
- **限制类型:** TArray<T>,TSet<T>,TMap<T>
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_EditFixedSize](../../../../Flags/EPropertyFlags/CPF_EditFixedSize.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
在细节面板上不允许改变该容器的元素个数。
|
||||
|
||||
只适用于容器。这能防止用户通过虚幻编辑器属性窗口修改容器的元素个数。
|
||||
|
||||
但在C++代码和蓝图中依然是可以修改的。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
以TArray为例,其他同理。
|
||||
|
||||
```cpp
|
||||
UPROPERTY(EditAnywhere, Category = Array)
|
||||
TArray<int32> MyIntArray_Normal{1,2,3};
|
||||
|
||||
UPROPERTY(EditAnywhere, EditFixedSize,Category = Array)
|
||||
TArray<int32> MyIntArray_FixedSize{1,2,3};
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||
蓝图中的表现,前者可以动态再添加元素。后者不可。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
如果有CPF_EditFixedSize,则不会添加+和清空的按钮。
|
||||
|
||||
```cpp
|
||||
void PropertyEditorHelpers::GetRequiredPropertyButtons( TSharedRef<FPropertyNode> PropertyNode, TArray<EPropertyButton::Type>& OutRequiredButtons, bool bUsingAssetPicker )
|
||||
{
|
||||
// Handle a container property.
|
||||
if( NodeProperty->IsA(FArrayProperty::StaticClass()) || NodeProperty->IsA(FSetProperty::StaticClass()) || NodeProperty->IsA(FMapProperty::StaticClass()) )
|
||||
{
|
||||
if( !(NodeProperty->PropertyFlags & CPF_EditFixedSize) )
|
||||
{
|
||||
OutRequiredButtons.Add( EPropertyButton::Add );
|
||||
OutRequiredButtons.Add( EPropertyButton::Empty );
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,10 @@
|
||||
# EditInstanceOnly
|
||||
|
||||
- **功能描述:** 只能在实例的细节面板上编辑该属性
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** DetailsPanel, Editor
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_Edit](../../../Flags/EPropertyFlags/CPF_Edit.md), [CPF_DisableEditOnTemplate](../../../Flags/EPropertyFlags/CPF_DisableEditOnTemplate.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
一并参见EditAnywhere里的示例代码和效果。
|
@@ -0,0 +1,34 @@
|
||||
# Interp
|
||||
|
||||
- **功能描述:** 指定该属性值可暴露到时间轴里编辑,在平常的Timeline或UMG的动画里使用。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Sequencer
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_Edit](../../../../Flags/EPropertyFlags/CPF_Edit.md), [CPF_BlueprintVisible](../../../../Flags/EPropertyFlags/CPF_BlueprintVisible.md), [CPF_Interp](../../../../Flags/EPropertyFlags/CPF_Interp.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
该属性可以暴露到时间轴里,一般用来编辑动画。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyProperty_Interp :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category = Animation)
|
||||
FVector MyInterpVector;
|
||||
};
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||
影响的是属性上的该标志
|
||||
|
||||

|
||||
|
||||
从而可以在Sequencer里对该属性添加Track
|
||||
|
||||

|
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,40 @@
|
||||
# NoClear
|
||||
|
||||
- **功能描述:** 指定该属性的编辑选项中不出现Clear按钮,不允许置null。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** DetailsPanel, Editor
|
||||
- **限制类型:** 引用类型
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_NoClear](../../../../Flags/EPropertyFlags/CPF_NoClear.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
指定该属性的编辑选项中不出现Clear按钮。
|
||||
|
||||
作用是阻止用户在编辑器面板上将此Object引用设为null。但其实也可用在其他表示一个引用类型的结构上,比如FPrimaryAssetId,FInstancedStruct,FDataRegistryType等。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
UPROPERTY(EditAnywhere, Category = Object)
|
||||
class UMyClass_Default* MyObject_Normal;
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_NoClear | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, NoClear, Category = Object)
|
||||
class UMyClass_Default* MyObject_NoClear;
|
||||
|
||||
//构造函数赋值:
|
||||
MyObject_Normal = CreateDefaultSubobject<UMyClass_Default>("MyObject_Normal");
|
||||
MyObject_NoClear = CreateDefaultSubobject<UMyClass_Default>("MyObject_NoClear");
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
CPF_NoClear在引擎里有挺多使用。
|
||||
|
||||
```cpp
|
||||
const bool bAllowClear = !StructPropertyHandle->GetMetaDataProperty()->HasAnyPropertyFlags(CPF_NoClear);
|
||||
|
||||
```
|
After Width: | Height: | Size: 49 KiB |
@@ -0,0 +1,32 @@
|
||||
# NonTransactional
|
||||
|
||||
- **功能描述:** 对该属性的改变操作,不会被包含进编辑器的Undo/Redo命令中。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Editor
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_NonTransactional](../../../../Flags/EPropertyFlags/CPF_NonTransactional.md)
|
||||
- **常用程度:** ★★
|
||||
|
||||
指定该属性的改变,不能在编辑器中通过Ctrl+Z来撤销或Ctrl+Y来重做。在Actor或在BP的Class Defautls都可以生效。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```jsx
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyProperty_Transaction :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite,NonTransactional,Category = Transaction)
|
||||
int32 MyInt_NonTransactional= 123;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite,Category = Transaction)
|
||||
int32 MyInt_Transactional = 123;
|
||||
};
|
||||
```
|
||||
|
||||
## 蓝图表现:
|
||||
|
||||
在MyInt_Transactional 上可以撤销之前的输入,而MyInt_NonTransactional上的输入无法用Ctrl+Z撤销。
|
||||
|
||||

|
After Width: | Height: | Size: 41 KiB |
@@ -0,0 +1,45 @@
|
||||
# SimpleDisplay
|
||||
|
||||
- **功能描述:** 在细节面板中直接可见,不折叠到高级中。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** DetailsPanel, Editor
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_SimpleDisplay](../../../../Flags/EPropertyFlags/CPF_SimpleDisplay.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
在细节面板中直接可见,不折叠到高级中。
|
||||
|
||||
默认情况下本身就是不折叠,但可以用来覆盖掉类上的AdvancedClassDisplay的设置。具体可参见AdvancedClassDisplay的代码和效果。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_Test :public UObject
|
||||
{
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_SimpleDisplay | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, SimpleDisplay, Category = Display)
|
||||
int32 MyInt_SimpleDisplay = 123;
|
||||
|
||||
//PropertyFlags: CPF_Edit | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_AdvancedDisplay | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = Display)
|
||||
int32 MyInt_AdvancedDisplay = 123;
|
||||
}
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
如果有CPF_SimpleDisplay,则bAdvanced =false
|
||||
|
||||
```cpp
|
||||
void FPropertyNode::InitNode(const FPropertyNodeInitParams& InitParams)
|
||||
{
|
||||
// Property is advanced if it is marked advanced or the entire class is advanced and the property not marked as simple
|
||||
static const FName Name_AdvancedClassDisplay("AdvancedClassDisplay");
|
||||
bool bAdvanced = Property.IsValid() ? ( Property->HasAnyPropertyFlags(CPF_AdvancedDisplay) || ( !Property->HasAnyPropertyFlags( CPF_SimpleDisplay ) && Property->GetOwnerClass() && Property->GetOwnerClass()->GetBoolMetaData(Name_AdvancedClassDisplay) ) ) : false;
|
||||
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,10 @@
|
||||
# VisibleAnywhere
|
||||
|
||||
- **功能描述:** 在默认值和实例细节面板均可见,但不可编辑
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** DetailsPanel, Editor
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_Edit](../../../Flags/EPropertyFlags/CPF_Edit.md), [CPF_EditConst](../../../Flags/EPropertyFlags/CPF_EditConst.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
一并参见EditAnywhere里的示例代码和效果。
|
@@ -0,0 +1,9 @@
|
||||
# VisibleDefaultsOnly
|
||||
|
||||
- **功能描述:** 在默认值细节面板可见,但不可编辑
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** DetailsPanel, Editor
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_Edit](../../../Flags/EPropertyFlags/CPF_Edit.md), [CPF_DisableEditOnInstance](../../../Flags/EPropertyFlags/CPF_DisableEditOnInstance.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
一并参见EditAnywhere里的示例代码和效果。
|
@@ -0,0 +1,9 @@
|
||||
# VisibleInstanceOnly
|
||||
|
||||
- **功能描述:** 在实例细节面板可见,但不可编辑
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** DetailsPanel, Editor
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_Edit](../../../Flags/EPropertyFlags/CPF_Edit.md), [CPF_DisableEditOnTemplate](../../../Flags/EPropertyFlags/CPF_DisableEditOnTemplate.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
一并参见EditAnywhere里的示例代码和效果。
|
@@ -0,0 +1,52 @@
|
||||
# Instanced
|
||||
|
||||
- **功能描述:** 指定对该对象属性的编辑赋值应该新创建一个实例并作为子对象,而不是寻找一个对象引用。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Instance
|
||||
- **限制类型:** UObject*
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_PersistentInstance](../../../../Flags/EPropertyFlags/CPF_PersistentInstance.md), [CPF_ExportObject](../../../../Flags/EPropertyFlags/CPF_ExportObject.md), [CPF_InstancedReference](../../../../Flags/EPropertyFlags/CPF_InstancedReference.md),在Meta中加入[EditInline](../../../../Meta/DetailsPanel/EditInline/EditInline.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
指定对该对象属性的编辑赋值应该新创建一个实例并作为子对象,而不是寻找一个对象引用。
|
||||
|
||||
- 单个属性上的Instanced和UCLASS上的DefaultToInstanced作用有点类似,区别是前者只作用于单个属性,后者作用于该类类型的所有属性。
|
||||
- 常常和EditInlineNew一起使用,在细节面板上可以为对象属性新创建实例并编辑。
|
||||
- Instanced隐含了EditInline and Export.
|
||||
|
||||
在Object*属性上设置值的时候,如果不标Instanced,则只能为其设置一个对象引用。而如果想在编辑器里为其真正的创建一个对象实例并赋予给这个属性,则需要加上Instanced标记。但光有Instanced还不够,这个Class还需要加上EditInlineNew,才能让该类出现在可新创建类实例的列表里。
|
||||
|
||||
当然,在C++里手动设置对象给这个属性的话还是都可以的。也要注意和UCLASS(DefaultToInstanced)区分,DefaultToInstanced是表明这个类的所有属性都默认的加上Instanced的意思,避免了对该类的所有属性每次都要手动设置。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType,editinlinenew)
|
||||
class INSIDER_API UMyProperty_InstancedObject :public UDataAsset
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MyValue = 123;
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyProperty_Instanced :public UObject
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
UMyProperty_Instanced(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
||||
public:
|
||||
//PropertyFlags: CPF_Edit | CPF_BlueprintVisible | CPF_ExportObject | CPF_ZeroConstructor | CPF_InstancedReference | CPF_NoDestructor | CPF_PersistentInstance | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, Category = Behavior)
|
||||
UMyProperty_InstancedObject* ObjectInstanced;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Behavior)
|
||||
UMyProperty_InstancedObject* Object;
|
||||
};
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||
可见ObjectInstanced和Object弹出的编辑框是不同的。
|
||||
|
||||

|
After Width: | Height: | Size: 704 KiB |
@@ -0,0 +1,37 @@
|
||||
# NotReplicated
|
||||
|
||||
- **功能描述:** 跳过复制。这只会应用到服务请求函数中的结构体成员和参数。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
||||
- **限制类型:** Struct members
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_RepSkip](../../../Flags/EPropertyFlags/CPF_RepSkip.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
只用在结构成员中,指定struct中的某个属性不复制,否则默认就都会复制。这个用于排除掉结构中的某属性。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
USTRUCT(BlueprintType)
|
||||
struct FMyReplicatedStruct
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FString MyString_Default;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite,NotReplicated)
|
||||
FString MyString_NotReplicated;
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyProperty_Network :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated)
|
||||
FMyReplicatedStruct MyStruct_Replicated;
|
||||
};
|
||||
```
|
||||
|
||||
其中MyStruct_Replicated会复制,但是其中的MyString_NotReplicated不会复制。
|
@@ -0,0 +1,5 @@
|
||||
# RepRetry
|
||||
|
||||
- **功能描述:** 只适用于结构体属性。如果此属性未能完全发送(举例而言:Object引用尚无法通过网络进行序列化),则重新尝试对其的复制。对简单引用而言,这是默认选择;但对结构体而言,这会产生带宽开销,并非优选项。因此在指定此标签之前其均为禁用状态。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
@@ -0,0 +1,39 @@
|
||||
# Replicated
|
||||
|
||||
- **功能描述:** 指定该属性应随网络进行复制。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_Net](../../../Flags/EPropertyFlags/CPF_Net.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
## 示例代码:
|
||||
|
||||
记得要在cpp代码中相应添加GetLifetimeReplicatedProps函数
|
||||
|
||||
```jsx
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyProperty_Network :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MyInt_Default = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated)
|
||||
int32 MyInt_Replicated = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated)
|
||||
FMyReplicatedStruct MyStruct_Replicated;
|
||||
};
|
||||
|
||||
void AMyProperty_Network::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(AMyProperty_Network, MyInt_Replicated);
|
||||
DOREPLIFETIME(AMyProperty_Network, MyStruct_Replicated);
|
||||
}
|
||||
```
|
||||
|
||||
示例效果就不发了,这个是基本的网络标记。
|
@@ -0,0 +1,35 @@
|
||||
# ReplicatedUsing
|
||||
|
||||
- **功能描述:** 指定一个通知回调函数,在属性通过网络更新后执行。
|
||||
- **元数据类型:** string="abc"
|
||||
- **引擎模块:** Network
|
||||
- **作用机制:** 在PropertyFlags中加入[CPF_Net](../../../../Flags/EPropertyFlags/CPF_Net.md), [CPF_RepNotify](../../../../Flags/EPropertyFlags/CPF_RepNotify.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
ReplicatedUsing 可以接受无参数的函数,或是带一个参数的函数携带旧值。一般在OnRep函数里,做一些开启关闭的相应操作,比如enabled的复制就会触发相应的后续逻辑。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```jsx
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyProperty_Network :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
protected:
|
||||
UFUNCTION()
|
||||
void OnRep_MyInt(int32 oldValue);
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_MyInt)
|
||||
int32 MyInt_ReplicatedUsing = 123;
|
||||
};
|
||||
|
||||
void AMyProperty_Network::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(AMyProperty_Network, MyInt_ReplicatedUsing);
|
||||
}
|
||||
```
|
||||
|
||||
在蓝图中等价于RepNotify的作用。
|
||||
|
||||

|
After Width: | Height: | Size: 51 KiB |
@@ -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 |
@@ -0,0 +1,87 @@
|
||||
# FieldNotify
|
||||
|
||||
- **功能描述:** 在打开MVVM插件后,使得该属性变成支持FieldNotify的属性。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** MVVM, UHT
|
||||
- **限制类型:** ViewModel里的属性
|
||||
- **常用程度:** ★★★★
|
||||
|
||||
在打开MVVM插件后,使得该属性变成支持FieldNotify的属性。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(BlueprintType)
|
||||
class INSIDER_API UMyViewModel :public UMVVMViewModelBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
protected:
|
||||
UPROPERTY(BlueprintReadWrite, FieldNotify, Getter, Setter, BlueprintSetter = SetHP)
|
||||
float HP = 1.f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, FieldNotify, Getter, Setter, BlueprintSetter = SetMaxHP)
|
||||
float MaxHP = 100.f;
|
||||
public:
|
||||
float GetHP()const { return HP; }
|
||||
UFUNCTION(BlueprintSetter)
|
||||
void SetHP(float val)
|
||||
{
|
||||
if (UE_MVVM_SET_PROPERTY_VALUE(HP, val))
|
||||
{
|
||||
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHPPercent);
|
||||
}
|
||||
}
|
||||
|
||||
float GetMaxHP()const { return MaxHP; }
|
||||
UFUNCTION(BlueprintSetter)
|
||||
void SetMaxHP(float val)
|
||||
{
|
||||
if (UE_MVVM_SET_PROPERTY_VALUE(MaxHP, val))
|
||||
{
|
||||
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHPPercent);
|
||||
}
|
||||
}
|
||||
|
||||
//You need to manually notify that GetHealthPercent changed when CurrentHealth or MaxHealth changed.
|
||||
UFUNCTION(BlueprintPure, FieldNotify)
|
||||
float GetHPPercent() const
|
||||
{
|
||||
return (MaxHP != 0.f) ? HP / MaxHP : 0.f;
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||
效果一方面是UHT生成的.generated.h里和.gen.cpp里的代码,具体的宏作用就不展开了,大家可查看其他更细致的MVVM相关文章。这里只要知道UHT会为标了FieldNotify的属性定义FFieldId以及FFieldNotificationClassDescriptor来标识一个接受可通知的属性。
|
||||
|
||||
```cpp
|
||||
//MyViewModel.generated.h
|
||||
#define FID_GitWorkspace_Hello_Source_Insider_Property_MVVM_MyViewModel_h_12_FIELDNOTIFY \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_CLASS_DESCRIPTOR_BEGIN(INSIDER_API ) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_FIELD(HP) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_FIELD(MaxHP) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_FIELD(GetHPPercent) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_ENUM_FIELD_BEGIN(HP) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_ENUM_FIELD(MaxHP) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_ENUM_FIELD(GetHPPercent) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_ENUM_FIELD_END() \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_CLASS_DESCRIPTOR_END();
|
||||
//MyViewModel.gen.cpp
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENT_FIELD(UMyViewModel, HP)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENT_FIELD(UMyViewModel, MaxHP)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENT_FIELD(UMyViewModel, GetHPPercent)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENTATION_BEGIN(UMyViewModel)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENT_ENUM_FIELD(UMyViewModel, HP)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENT_ENUM_FIELD(UMyViewModel, MaxHP)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENT_ENUM_FIELD(UMyViewModel, GetHPPercent)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENTATION_END(UMyViewModel);
|
||||
```
|
||||
|
||||
蓝图效果:
|
||||
|
||||
这些控件的属性就可以和ViewModel里的属性绑定起来。
|
||||
|
||||

|
After Width: | Height: | Size: 212 KiB |
@@ -0,0 +1,7 @@
|
||||
# Native
|
||||
|
||||
- **功能描述:** 属性为本地:C++代码负责对其进行序列化并公开到垃圾回收 。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Behavior
|
||||
|
||||
已被删除
|