vault backup: 2024-10-12 17:19:45

This commit is contained in:
2024-10-12 17:19:46 +08:00
parent ff94ddca61
commit 244c0c52f6
960 changed files with 31348 additions and 10 deletions

View File

@@ -0,0 +1,109 @@
# UCLASS(标识符)
## UHT
| Name | 引擎模块 | 功能描述 | 常用程度 |
| ---------------------------------------------------------- | -------- | ------------------------------------------------------------ | -------- |
| [NoExport](UCLASS/UHT/NoExport.md) | UHT | 指定UHT不要用来自动生成注册的代码而只是进行词法分析提取元数据。 | 💀 |
| [Intrinsic](UCLASS/UHT/Intrinsic.md) | UHT | 指定UHT完全不为此类生成代码需要自己手写。 | 💀 |
| [Interface](UCLASS/UHT/Interface.md) | UHT | 标识这个Class是个Interface。 | 💀 |
| [UCLASS()](UCLASS/UHT/UCLASS().md) | UHT | 留空的默认行为是不能在蓝图中被继承,不能在蓝图中定义变量,但拥有反射的功能。 | ★★★★★ |
| [不写UCLASS()](UCLASS/UHT/不写UCLASS().md) | UHT | 只是作为一个普通的C++对象,没有反射功能。 | ★ |
| [CustomThunkTemplates](UCLASS/UHT/CustomThunkTemplates.md) | UHT | Specifies the struct that contains the CustomThunk implementations | 💀 |
| [CustomConstructor](UCLASS/UHT/CustomConstructor.md) | UHT | 阻止构造函数声明自动生成。 | 💀 |
| [CustomFieldNotify](UCLASS/UHT/CustomFieldNotify.md) | UHT | 阻止UHT为该类生成FieldNotify的相关代码。 | 💀 |
## Blueprint
| Name | 引擎模块 | 功能描述 | 常用程度 |
| ------------------------------------------------------------ | --------- | ------------------------------------------------------------ | -------- |
| [Blueprintable](UCLASS/Blueprint/Blueprintable/Blueprintable.md) | Blueprint | 可以在蓝图里被继承,隐含的作用也可当变量类型 | ★★★★★ |
| [NotBlueprintable](UCLASS/Blueprint/NotBlueprintable.md) | Blueprint | 不可在蓝图里继承,隐含作用也不可当作变量 | ★★★★ |
| [BlueprintType](UCLASS/Blueprint/BlueprintType/BlueprintType.md) | Blueprint | 可当做变量类型 | ★★★★★ |
| [NotBlueprintType](UCLASS/Blueprint/NotBlueprintType.md) | Blueprint | 不可当做变量类型 | ★★★★ |
| [Abstract](UCLASS/Blueprint/Abstract/Abstract.md) | Blueprint | 指定此类为抽象基类。可被继承,但不可生成对象。 | ★★★★★ |
| [Const](UPARAM/Blueprint/Const/Const.md) | Blueprint | 表示本类的内部属性不可在蓝图中被修改,只读不可写。 | ★★★ |
| [ShowFunctions](UCLASS/Blueprint/ShowFunctions.md) | Blueprint | 在子类的函数覆盖列表里重新打开某些函数。 | ★★ |
| [HideFunctions](UCLASS/Blueprint/HideFunctions/HideFunctions.md) | Blueprint | 在子类的函数覆盖列表里隐藏掉某些函数。 | ★★ |
| [SparseClassDataType](UCLASS/Blueprint/SparseClassDataType/SparseClassDataType.md) | Blueprint | 让Actor的一些重复不变的数据存放在一个共同的结构里以达到减少内容使用量的目的 | ★★★ |
| [NeedsDeferredDependencyLoading](UCLASS/Blueprint/NeedsDeferredDependencyLoading.md) | Blueprint | | 💀 |
## DllExport
| Name | 引擎模块 | 功能描述 | 常用程度 |
| ------------------------------------------ | --------- | --------------------------------------------- | -------- |
| [MinimalAPI](UINTERFACE/UHT/MinimalAPI.md) | DllExport | 不dll导出该类的函数只导出类型信息当作变量。 | ★★★ |
## Category
| Name | 引擎模块 | 功能描述 | 常用程度 |
| ------------------------------------------------------------ | ---------------- | ------------------------------------------------------------ | -------- |
| [ClassGroup](UCLASS/Category/ClassGroup/ClassGroup.md) | Category | 指定组件在Actor的AddComponent面板里的分组以及在蓝图右键菜单中的分组。 | ★★★ |
| [ShowCategories](UCLASS/Category/ShowCategories/ShowCategories.md) | Category | 在类的ClassDefaults属性面板里显示某些Category的属性。 | ★★★ |
| [HideCategories](UCLASS/Category/HideCategories/HideCategories.md) | Category | 在类的ClassDefaults属性面板里隐藏某些Category的属性。 | ★★★★ |
| [CollapseCategories](UCLASS/Category/CollapseCategories/CollapseCategories.md) | Category | 在类的属性面板里隐藏所有带Category的属性但是只对带有多个嵌套Category的属性才起作用。 | ★★ |
| [DontCollapseCategories](UCLASS/Category/DontCollapseCategories.md) | Category | 使继承自基类的CollapseCatogories说明符无效。 | ★★ |
| [AutoExpandCategories](UCLASS/Category/AutoExpandCategories/AutoExpandCategories.md) | Category | 指定此类的对象在细节面板中应该自动展开的Category。 | ★ |
| [AutoCollapseCategories](UCLASS/Category/AutoCollapseCategories/AutoCollapseCategories.md) | Category | AutoCollapseCategories说明符使父类上的 AutoExpandCategories 说明符的列出类别的效果无效。 | ★ |
| [DontAutoCollapseCategories](UCLASS/Category/DontAutoCollapseCategories.md) | Category | 使列出的类别的继承自父类的AutoCollapseCategories说明符无效。 | ★ |
| [PrioritizeCategories](UCLASS/Category/PrioritizeCategories/PrioritizeCategories.md) | Category | 把指定的属性目录优先显示在细节面板的前面。 | ★★★ |
| [ComponentWrapperClass](UCLASS/Category/ComponentWrapperClass/ComponentWrapperClass.md) | Category | 指定该类为一个简单的封装类忽略掉子类的Category相关设置。 | ★★ |
| [AdvancedClassDisplay](UCLASS/Category/AdvancedClassDisplay/AdvancedClassDisplay.md) | Category | 把该类下的所有属性都默认显示在高级目录下 | ★★★★ |
## TypePicker
| Name | 引擎模块 | 功能描述 | 常用程度 |
| ------------------------------------------------------------ | ---------- | -------------------- | -------- |
| [HideDropDown](UCLASS/TypePicker/HideDropDown/HideDropDown.md) | TypePicker | 在类选择器中隐藏此类 | ★★ |
## Development
| Name | 引擎模块 | 功能描述 | 常用程度 |
| ------------------------------------------------------------ | ----------- | ------------------------------------------------------------ | -------- |
| [Deprecated](UCLASS/Development/Deprecated/Deprecated.md) | Development | 标明该类已经弃用。 | ★★★ |
| [Experimental](UCLASS/Development/Experimental/Experimental.md) | Development | 标明该类是试验性版本,当前没有文档描述,之后有可能废弃掉。 | ★★★ |
| [EarlyAccessPreview](UCLASS/Development/EarlyAccessPreview/EarlyAccessPreview.md) | Development | 标明该类是早期预览版,比试验版要更完善一些,但还是没到产品级。 | ★★★ |
## Instance
| Name | 引擎模块 | 功能描述 | 常用程度 |
| ------------------------------------------------------------ | -------- | ------------------------------------------------------------ | -------- |
| [Within](UCLASS/Instance/Within.md) | Instance | 指定对象创建的时候必须依赖于OuterClassName的对象作为Outer。 | ★★★ |
| [DefaultToInstanced](UCLASS/Instance/DefaultToInstanced/DefaultToInstanced.md) | Instance | 指定该类的所有实例属性都默认是UPROPERTY(instanced),即都默认创建新的实例,而不是对对象的引用。 | ★★★★ |
| [EditInlineNew](UCLASS/Instance/EditInlineNew/EditInlineNew.md) | Instance | 指定该类的对象可以在属性细节面板里直接内联创建要和属性的Instanced配合。 | ★★★★★ |
| [NotEditInlineNew](UCLASS/Instance/NotEditInlineNew.md) | Instance | 不能通过EditInline按钮创建 | ★ |
## Scene
| Name | 引擎模块 | 功能描述 | 常用程度 |
| ------------------------------------------------------------ | ---------------- | ------------------------------------------------------------ | -------- |
| [NotPlaceable](UCLASS/Scene/NotPlaceable/NotPlaceable.md) | Scene | 标明该Actor不可被放置在关卡里 | ★★★ |
| [Placeable](UCLASS/Scene/Placeable/Placeable.md) | Scene | 标明该Actor可以放置在关卡里。 | ★★★ |
| [ConversionRoot](UCLASS/Scene/ConversionRoot/ConversionRoot.md) | Scene | 在场景编辑器里允许Actor在自身以及子类之间做转换 | ★ |
## Config
| Name | 引擎模块 | 功能描述 | 常用程度 |
| ------------------------------------------------------------ | ---------------- | ------------------------------------------------------------ | -------- |
| [Config](UCLASS/Config/Config.md) | Config | 指定配置文件的名字把该对象的值保存到ini配置文件中。 | ★★★★★ |
| [PerObjectConfig](UCLASS/Config/PerObjectConfig.md) | Config | 在已经有config配置文件名字的情况下指定应该按每个对象实例来存储值而不是一个类一个存储值。 | ★★★★★ |
| [ConfigDoNotCheckDefaults](UCLASS/Config/ConfigDoNotCheckDefaults.md) | Config | 指定在保存配置值的时候忽略上一级的配置值的一致性检查。 | ★ |
| [DefaultConfig](UCLASS/Config/DefaultConfig/DefaultConfig.md) | Config | 指定保存到的配置文件层级是Project/Config/DefaultXXX.ini。 | ★★★ |
| [GlobalUserConfig](UCLASS/Config/GlobalUserConfig/GlobalUserConfig.md) | Config | 指定保存到的配置文件层级是全局用户设置 Engine/Config/UserXXX.ini。 | ★★★ |
| [ProjectUserConfig](UCLASS/Config/ProjectUserConfig/ProjectUserConfig.md) | Config | 指定保存到的配置文件层级是项目用户设置 Project/Config/UserXXX.ini。 | ★★★ |
| [EditorConfig](UCLASS/Config/EditorConfig/EditorConfig.md) | Config | 用来在编辑器状态下保存信息。 | ★ |
## Serialization
| Name | 引擎模块 | 功能描述 | 常用程度 |
| ------------------------------------------------------------ | ------------- | ------------------------------------------------------------ | -------- |
| [Transient](UCLASS/Serialization/Transient/Transient.md) | Serialization | 指定该类的所有对象都略过序列化。 | ★★★ |
| [NonTransient](UCLASS/Serialization/NonTransient.md) | Serialization | 使继承自基类的Transient说明符无效。 | ★★★ |
| [Optional](UCLASS/Serialization/Optional/Optional.md) | Serialization | 标记该类的对象是可选的在Cooking的时候可以选择是否要忽略保存它们。 | ★ |
| [MatchedSerializers](UCLASS/Serialization/MatchedSerializers/MatchedSerializers.md) | Serialization | 指定类支持文本结构序列化 | 💀 |

View File

@@ -0,0 +1,79 @@
# Abstract
- **功能描述:** 指定此类为抽象基类。可被继承,但不可生成对象。
- **引擎模块:** Blueprint
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中添加[CLASS_Abstract](../../../../Flags/EClassFlags/CLASS_Abstract.md)
- **常用程度:** ★★★★★
指定此类为抽象基类。可被继承,但不可生成对象。
一般是用在XXXBase基类。
## 示例代码:
```cpp
/*
ClassFlags: CLASS_Abstract | CLASS_MatchedSerializers | CLASS_Native | CLASS_RequiredAPI | CLASS_TokenStreamAssembled | CLASS_Intrinsic | CLASS_Constructed
*/
UCLASS(Blueprintable, abstract)
class INSIDER_API UMyClass_Abstract :public UObject
{
GENERATED_BODY()
};
//测试语句:
UMyClass_Abstract* obj=NewObject<UMyClass_Abstract>();
```
## 示例效果:
在蓝图中的ConstructObject不会出现该类。同时在C++中NewObject也会报错。
![image](image.png)
## 原理:
在NewObject的时候会进行Abstract的判断。
```cpp
bool StaticAllocateObjectErrorTests( const UClass* InClass, UObject* InOuter, FName InName, EObjectFlags InFlags)
{
// Validation checks.
if( !InClass )
{
UE_LOG(LogUObjectGlobals, Fatal, TEXT("Empty class for object %s"), *InName.ToString() );
return true;
}
// for abstract classes that are being loaded NOT in the editor we want to error. If they are in the editor we do not want to have an error
if (FScopedAllowAbstractClassAllocation::IsDisallowedAbstractClass(InClass, InFlags))
{
if ( GIsEditor )
{
const FString ErrorMsg = FString::Printf(TEXT("Class which was marked abstract was trying to be loaded in Outer %s. It will be nulled out on save. %s %s"), *GetPathNameSafe(InOuter), *InName.ToString(), *InClass->GetName());
// if we are trying instantiate an abstract class in the editor we'll warn the user that it will be nulled out on save
UE_LOG(LogUObjectGlobals, Warning, TEXT("%s"), *ErrorMsg);
ensureMsgf(false, TEXT("%s"), *ErrorMsg);
}
else
{
UE_LOG(LogUObjectGlobals, Fatal, TEXT("%s"), *FString::Printf( TEXT("Can't create object %s in Outer %s: class %s is abstract"), *InName.ToString(), *GetPathNameSafe(InOuter), *InClass->GetName()));
return true;
}
}
}
bool FScopedAllowAbstractClassAllocation::IsDisallowedAbstractClass(const UClass* InClass, EObjectFlags InFlags)
{
if (((InFlags& RF_ClassDefaultObject) == 0) && InClass->HasAnyClassFlags(CLASS_Abstract))
{
if (AllowAbstractCount == 0)
{
return true;
}
}
return false;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -0,0 +1,93 @@
# BlueprintType
- **功能描述:** 可当做变量类型
- **引擎模块:** Blueprint
- **元数据类型:** bool
- **作用机制:** Meta增加[BlueprintType](../../../../Meta/Blueprint/BlueprintType.md)
- **关联项:** [NotBlueprintType ](../NotBlueprintType.md)
- **常用程度:** ★★★★★
可当做变量类型。
关键是设置BlueprintType和NotBlueprintType这两个metadata.
## 示例代码:
```cpp
/*
(BlueprintType = true, IncludePath = Class/MyClass_BlueprintType.h, ModuleRelativePath = Class/MyClass_BlueprintType.h)
*/
UCLASS(BlueprintType)
class INSIDER_API UMyClass_BlueprintType :public UObject
{
GENERATED_BODY()
};
/*
(IncludePath = Class/MyClass_BlueprintType.h, ModuleRelativePath = Class/MyClass_BlueprintType.h)
*/
UCLASS()
class INSIDER_API UMyClass_BlueprintType_Child :public UMyClass_BlueprintType
{
GENERATED_BODY()
};
/*
(IncludePath = Class/MyClass_BlueprintType.h, ModuleRelativePath = Class/MyClass_BlueprintType.h, NotBlueprintType = true)
*/
UCLASS(NotBlueprintType)
class INSIDER_API UMyClass_NotBlueprintType :public UObject
{
GENERATED_BODY()
};
/*
(BlueprintType = true, IncludePath = Class/MyClass_BlueprintType.h, ModuleRelativePath = Class/MyClass_BlueprintType.h)
*/
UCLASS(BlueprintType)
class INSIDER_API UMyClass_NotBlueprintType_To_BlueprintType:public UMyClass_NotBlueprintType
{
GENERATED_BODY()
};
/*
(IncludePath = Class/MyClass_BlueprintType.h, ModuleRelativePath = Class/MyClass_BlueprintType.h, NotBlueprintType = true)
*/
UCLASS(NotBlueprintType)
class INSIDER_API UMyClass_BlueprintType_To_NotBlueprintType:public UMyClass_BlueprintType
{
GENERATED_BODY()
};
```
## 示例结果:
带有BlueprintType =true的才可以当作变量
![Untitled](Untitled.png)
## 原理:
在UEdGraphSchema_K2::IsAllowableBlueprintVariableType的3个重载函数分别判断UEnumUClassUScriptStruct能否当作变量。
```cpp
UEdGraphSchema_K2::IsAllowableBlueprintVariableType来判断
const UClass* ParentClass = InClass;
while(ParentClass)
{
// Climb up the class hierarchy and look for "BlueprintType" and "NotBlueprintType" to see if this class is allowed.
if(ParentClass->GetBoolMetaData(FBlueprintMetadata::MD_AllowableBlueprintVariableType)
|| ParentClass->HasMetaData(FBlueprintMetadata::MD_BlueprintSpawnableComponent))
{
return true;
}
else if(ParentClass->GetBoolMetaData(FBlueprintMetadata::MD_NotAllowableBlueprintVariableType))
{
return false;
}
ParentClass = ParentClass->GetSuperClass();
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,96 @@
# Blueprintable
- **功能描述:** 可以在蓝图里被继承,隐含的作用也可当变量类型
- **引擎模块:** Blueprint
- **元数据类型:** bool
- **作用机制:** 在Meta添加[IsBlueprintBase](../../../../Meta/Blueprint/IsBlueprintBase.md)和[BlueprintType](../../../../Meta/Blueprint/BlueprintType.md)
- **关联项:** [NotBlueprintable](../NotBlueprintable.md)
- **常用程度:** ★★★★★
可以在蓝图里被继承,隐含的作用也可当变量类型。
当设置Blueprintable标记的时候会隐含的设置上BlueprintType = true的metadata。去除的时候也会相应的去除掉BlueprintType = true。
## 示例代码:
```cpp
/*
(BlueprintType = true, IncludePath = Class/MyClass_Blueprintable.h, IsBlueprintBase = true, ModuleRelativePath = Class/MyClass_Blueprintable.h)
*/
UCLASS(Blueprintable)
class INSIDER_API UMyClass_Blueprintable :public UObject
{
GENERATED_BODY()
};
/*
(IncludePath = Class/MyClass_Blueprintable.h, IsBlueprintBase = false, ModuleRelativePath = Class/MyClass_Blueprintable.h)
*/
UCLASS(NotBlueprintable)
class INSIDER_API UMyClass_NotBlueprintable :public UObject
{
GENERATED_BODY()
};
/*
(BlueprintType = true, IncludePath = Class/MyClass_Blueprintable.h, IsBlueprintBase = true, ModuleRelativePath = Class/MyClass_Blueprintable.h)
*/
UCLASS(Blueprintable)
class INSIDER_API UMyClass_NotBlueprintable_To_Blueprintable :public UMyClass_NotBlueprintable
{
GENERATED_BODY()
};
/*
(IncludePath = Class/MyClass_Blueprintable.h, IsBlueprintBase = false, ModuleRelativePath = Class/MyClass_Blueprintable.h)
*/
UCLASS(NotBlueprintable)
class INSIDER_API UMyClass_Blueprintable_To_NotBlueprintable :public UMyClass_Blueprintable
{
GENERATED_BODY()
};
```
## 示例效果:
只有带有Blueprintable才可以被选做基类。
![Untitled](Untitled.png)
不过是否能够当做变量的规则还是会依赖父类的Blueprint标记。因此以下这3个都是可以当做变量的。
其中UMyClass_Blueprintable_To_NotBlueprintable可以当做变量是因为父类UMyClass_Blueprintable可以当做变量因此就继承了下来。
![Untitled](Untitled%201.png)
## 原理:
可见MD_IsBlueprintBase的判断用来决定是否能创建子类
```cpp
bool FKismetEditorUtilities::CanCreateBlueprintOfClass(const UClass* Class)
{
bool bCanCreateBlueprint = false;
if (Class)
{
bool bAllowDerivedBlueprints = false;
GConfig->GetBool(TEXT("Kismet"), TEXT("AllowDerivedBlueprints"), /*out*/ bAllowDerivedBlueprints, GEngineIni);
bCanCreateBlueprint = !Class->HasAnyClassFlags(CLASS_Deprecated)
&& !Class->HasAnyClassFlags(CLASS_NewerVersionExists)
&& (!Class->ClassGeneratedBy || (bAllowDerivedBlueprints && !IsClassABlueprintSkeleton(Class)));
const bool bIsBPGC = (Cast<UBlueprintGeneratedClass>(Class) != nullptr);
const bool bIsValidClass = Class->GetBoolMetaDataHierarchical(FBlueprintMetadata::MD_IsBlueprintBase)
|| (Class == UObject::StaticClass())
|| (Class == USceneComponent::StaticClass() || Class == UActorComponent::StaticClass())
|| bIsBPGC; // BPs are always considered inheritable
bCanCreateBlueprint &= bIsValidClass;
}
return bCanCreateBlueprint;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,60 @@
# Const
- **功能描述:** 表示本类的内部属性不可在蓝图中被修改,只读不可写。
- **引擎模块:** Blueprint
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中添加[CLASS_Abstract](../../../../Flags/EClassFlags/CLASS_Const.md)
- **常用程度:** ★★★
表示本类的内部属性不可在蓝图中被修改,只读不可写。
继承的蓝图类也是如此。其实就是自动的给本类和子类上添加const的标志。注意只是在蓝图里检查C++依然可以随意改变遵循C++的规则。所以这个const是只给蓝图用的在蓝图里检查。函数依然可以随便调用只是没有属性的Set方法了也不能改变了。
## 示例代码:
```cpp
/*
ClassFlags: CLASS_MatchedSerializers | CLASS_Native | CLASS_Const | CLASS_RequiredAPI | CLASS_TokenStreamAssembled | CLASS_Intrinsic | CLASS_Constructed
*/
UCLASS(Blueprintable, Const)
class INSIDER_API UMyClass_Const :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty = 123;
UFUNCTION(BlueprintCallable)
void MyFunc() { ++MyProperty; }
};
```
## 示例效果:
在蓝图子类中尝试修改属性会报错。
![image](image.png)
跟蓝图Class Settings里打开这个开关设定的一样
![image](image%201.png)
![Untitled](Untitled.png)
## 原理:
Const类生成的实例属性对带有const的标记从而阻止修改自身的属性。
```cpp
void FKCHandler_VariableSet::InnerAssignment(FKismetFunctionContext& Context, UEdGraphNode* Node, UEdGraphPin* VariablePin, UEdGraphPin* ValuePin)
{
if (!(*VariableTerm)->IsTermWritable())
{
CompilerContext.MessageLog.Error(*LOCTEXT("WriteConst_Error", "Cannot write to const @@").ToString(), VariablePin);
}
}
bool FBPTerminal::IsTermWritable() const
{
return !bIsLiteral && !bIsConst;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,108 @@
# HideFunctions
- **功能描述:** 在子类的函数覆盖列表里隐藏掉某些函数。
- **引擎模块:** Blueprint
- **元数据类型:** strings=(abc"d|e""x|y|z")
- **作用机制:** 在Meta中增加[HideFunctions](../../../../Meta/Blueprint/HideFunctions.md)
- **关联项:** [ShowFunctions](../ShowFunctions.md)
- **常用程度:** ★★
在子类的函数覆盖列表里隐藏掉某些函数。
- 在蓝图中鼠标右键依然可以查看到该类下BlueprintCallable的函数依然可以调用本标记只是用在类的函数覆盖列表上。
- HideFunctions其实只能填函数名字想要隐藏一个目录下的函数是需要HideCategories再额外定义的。
源码中只有一个地方用到一个很好的示例是UCameraComponent中定义的SetFieldOfView和SetAspectRatio对UCineCameraComponent 来说是无意义的,因此隐藏掉会更好。
```cpp
class ENGINE_API UCameraComponent : public USceneComponent
{
UFUNCTION(BlueprintCallable, Category = Camera)
virtual void SetFieldOfView(float InFieldOfView) { FieldOfView = InFieldOfView; }
UFUNCTION(BlueprintCallable, Category = Camera)
void SetAspectRatio(float InAspectRatio) { AspectRatio = InAspectRatio; }
}
UCLASS(HideCategories = (CameraSettings), HideFunctions = (SetFieldOfView, SetAspectRatio), Blueprintable, ClassGroup = Camera, meta = (BlueprintSpawnableComponent), Config = Engine)
class CINEMATICCAMERA_API UCineCameraComponent : public UCameraComponent
```
## 示例代码:
```cpp
UCLASS(Blueprintable, HideFunctions = (MyFunc1, MyEvent2),hideCategories= EventCategory2)
class INSIDER_API AMyClass_HideFunctions :public AActor
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
void MyFunc1() {}
UFUNCTION(BlueprintCallable)
void MyFunc2() {}
UFUNCTION(BlueprintCallable, Category = "FuncCategory1")
void MyFuncInCategory1() {}
UFUNCTION(BlueprintCallable, Category = "FuncCategory2")
void MyFuncInCategory2() {}
public:
UFUNCTION(BlueprintImplementableEvent)
void MyEvent1();
UFUNCTION(BlueprintImplementableEvent)
void MyEvent2();
UFUNCTION(BlueprintImplementableEvent, Category = "EventCategory1")
void MyEventInCategory1();
UFUNCTION(BlueprintImplementableEvent, Category = "EventCategory2")
void MyEventInCategory2();
};
UCLASS(Blueprintable, ShowFunctions = (MyEvent2),showCategories= EventCategory2)
class INSIDER_API AMyClass_ShowFunctions :public AMyClass_HideFunctions
{
GENERATED_BODY()
public:
};
```
## 示例效果:
发现Callable的函数是依然可以调用的。
![Untitled](Untitled.png)
在HideFunction子类里函数重载会发现少两个
![Untitled](Untitled%201.png)
在ShowFunction的子类里可以重新打开Event2和EventCategory2
![Untitled](Untitled%202.png)
## 原理:
原理显示HideFunctions其实只能填函数名字想要隐藏一个目录下的函数是需要HideCategories再额外定义的。
```cpp
bool IsFunctionHiddenFromClass( const UFunction* InFunction,const UClass* Class )
{
bool bResult = false;
if( InFunction )
{
bResult = Class->IsFunctionHidden( *InFunction->GetName() );
static const FName FunctionCategory(TEXT("Category")); // FBlueprintMetadata::MD_FunctionCategory
if( !bResult && InFunction->HasMetaData( FunctionCategory ) )
{
FString const& FuncCategory = InFunction->GetMetaData(FunctionCategory);
bResult = FEditorCategoryUtils::IsCategoryHiddenFromClass(Class, FuncCategory);
}
}
return bResult;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@@ -0,0 +1,24 @@
# NeedsDeferredDependencyLoading
- **引擎模块:** Blueprint
- **元数据类型:** bool
- **作用机制:** 在ClassFlags增加[CLASS_NeedsDeferredDependencyLoading](../../../Flags/EClassFlags/CLASS_NeedsDeferredDependencyLoading.md)
## 源码例子:
```cpp
UCLASS(NeedsDeferredDependencyLoading, MinimalAPI)
class UBlueprintGeneratedClass : public UClass, public IBlueprintPropertyGuidProvider
{
}
```
## 原理:
```cpp
if (ClassFlags.HasAnyFlags(EClassFlags.NeedsDeferredDependencyLoading) && !IsChildOf(Session.UClass))
{
// CLASS_NeedsDeferredDependencyLoading can only be set on classes derived from UClass
this.LogError($"'NeedsDeferredDependencyLoading' is set on '{SourceName}' but the flag can only be used with classes derived from UClass.");
}
```

View File

@@ -0,0 +1,8 @@
# NotBlueprintType
- **功能描述:** 不可当做变量类型
- **引擎模块:** Blueprint
- **元数据类型:** bool
- **作用机制:** Meta移除[BlueprintType](../../../Meta/Blueprint/BlueprintType.md)
- **关联项:** [BlueprintType](BlueprintType/BlueprintType.md)
- **常用程度:★★★★**

View File

@@ -0,0 +1,9 @@
# NotBlueprintable
- **功能描述:** 不可在蓝图里继承,隐含作用也不可当作变量
- **引擎模块:** Blueprint
- **元数据类型:** bool
- **作用机制:** 在Meta去除[IsBlueprintBase](../../../Meta/Blueprint/IsBlueprintBase.md)和[BlueprintType](../../../Meta/Blueprint/BlueprintType.md)
- **关联项:** [Blueprintable](Blueprintable/Blueprintable.md)
- **常用程度:★★★★**

View File

@@ -0,0 +1,31 @@
# ShowFunctions
- **功能描述:** 在子类的函数覆盖列表里重新打开某些函数。
- **引擎模块:** Blueprint
- **元数据类型:** strings=(abc"d|e""x|y|z")
- **作用机制:** 在Meta中去除[HideFunctions](../../../Meta/Blueprint/HideFunctions.md)
- **关联项:** [HideFunctions](HideFunctions/HideFunctions.md)
- **常用程度:★★**
在子类的函数覆盖列表里重新打开某些函数。
测试代码和效果图见HideFunctions。
## 原理:
UHT中的代码可见ShowFunctions的作用就是去除掉之前设置的HideFunctions。
```cpp
private void MergeCategories()
{
MergeShowCategories();
// Merge ShowFunctions and HideFunctions
AppendStringListMetaData(SuperClass, UhtNames.HideFunctions, HideFunctions);
foreach (string value in ShowFunctions)
{
HideFunctions.RemoveSwap(value);
}
ShowFunctions.Clear();
}
```

View File

@@ -0,0 +1,263 @@
# SparseClassDataType
- **功能描述:** 让Actor的一些重复不变的数据存放在一个共同的结构里以达到减少内容使用量的目的
- **引擎模块:** Blueprint
- **元数据类型:** string="abc"
- **作用机制:** 在Meta中增加[SparseClassDataTypes](../../../../Meta/Blueprint/SparseClassDataTypes.md)
- **关联项:** [NoGetter](../../../../Meta/SparseDataType/NoGetter/NoGetter.md)
- **常用程度:** ★★★
这是个重构和性能优化的点。在使用SparseClassDataType的时候分为两种情况一是以前的Actor想利用这个特性来优化二是新创建的Actor一开始就想使用这个特性。
## 示例用法:
分为两部分:
旧的Actor存在冗余属性
简而言之是那些不会在BP改变的属性。C++方面如果有修改这些属性也要修改为使用Get函数来获得从而转到SparseDataStruct里去。
```cpp
UCLASS(Blueprintable, BlueprintType)
class INSIDER_API AMyActor_SparseClassDataTypes :public AActor
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly)
int32 MyInt_EditDefaultOnly = 123;
UPROPERTY(BlueprintReadOnly)
int32 MyInt_BlueprintReadOnly = 1024;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
FString MyString_EditDefault_ReadOnly = TEXT("MyName");
UPROPERTY(EditAnywhere)
float MyFloat_EditAnywhere = 555.f;
UPROPERTY(BlueprintReadWrite)
float MyFloat_BlueprintReadWrite = 666.f;
};
```
改为以下的代码。把属性用WITH_EDITORONLY_DATA包起来以示意只在editor下做操作在runtime是已经消除的。加上_DEPRECATED后缀标记也是为了进一步提醒原先BP里的访问要去除。重载MoveDataToSparseClassDataStruct以便把现在BP Class Defaults里配置的值拷贝给新的FMySparseClassData结构数值。
```cpp
USTRUCT(BlueprintType)
struct FMySparseClassData
{
GENERATED_BODY()
UPROPERTY(EditDefaultsOnly)
int32 MyInt_EditDefaultOnly = 123;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
int32 MyInt_BlueprintReadOnly = 1024;
// "GetByRef" means that Blueprint graphs access a const ref instead of a copy.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta=(GetByRef))
FString MyString_EditDefault_ReadOnly = TEXT("MyName");
};
UCLASS(Blueprintable, BlueprintType,SparseClassDataTypes= MySparseClassData)
class INSIDER_API AMyActor_SparseClassDataTypes :public AActor
{
GENERATED_BODY()
public:
#if WITH_EDITOR
// ~ This function transfers existing data into FMySparseClassData.
virtual void MoveDataToSparseClassDataStruct() const override;
#endif // WITH_EDITOR
public:
#if WITH_EDITORONLY_DATA
UPROPERTY()
int32 MyInt_EditDefaultOnly_DEPRECATED = 123;
UPROPERTY()
int32 MyInt_BlueprintReadOnly_DEPRECATED = 1024;
UPROPERTY()
FString MyString_EditDefault_ReadOnly_DEPRECATED = TEXT("MyName");
#endif // WITH_EDITORONLY_DATA
public:
UPROPERTY(EditAnywhere)
float MyFloat_EditAnywhere = 555.f;
UPROPERTY(BlueprintReadWrite)
float MyFloat_BlueprintReadWrite = 666.f;
};
//cpp
#if WITH_EDITOR
void AMyActor_SparseClassDataTypes::MoveDataToSparseClassDataStruct() const
{
// make sure we don't overwrite the sparse data if it has been saved already
UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(GetClass());
if (BPClass == nullptr || BPClass->bIsSparseClassDataSerializable == true)
{
return;
}
Super::MoveDataToSparseClassDataStruct();
#if WITH_EDITORONLY_DATA
// Unreal Header Tool (UHT) will create GetMySparseClassData automatically.
FMySparseClassData* SparseClassData = GetMySparseClassData();
// Modify these lines to include all Sparse Class Data properties.
SparseClassData->MyInt_EditDefaultOnly = MyInt_EditDefaultOnly_DEPRECATED;
SparseClassData->MyInt_BlueprintReadOnly = MyInt_BlueprintReadOnly_DEPRECATED;
SparseClassData->MyString_EditDefault_ReadOnly = MyString_EditDefault_ReadOnly_DEPRECATED;
#endif // WITH_EDITORONLY_DATA
}
#endif // WITH_EDITOR
```
在BP的PostLoad加载之后会自动的调用MoveDataToSparseClassDataStruct所以要在内部检测bIsSparseClassDataSerializable.
```cpp
void UBlueprintGeneratedClass::PostLoadDefaultObject(UObject* Object)
{
FScopeLock SerializeAndPostLoadLock(&SerializeAndPostLoadCritical);
Super::PostLoadDefaultObject(Object);
if (Object == ClassDefaultObject)
{
// Rebuild the custom property list used in post-construct initialization logic. Note that PostLoad() may have altered some serialized properties.
UpdateCustomPropertyListForPostConstruction();
// Restore any property values from config file
if (HasAnyClassFlags(CLASS_Config))
{
ClassDefaultObject->LoadConfig();
}
}
#if WITH_EDITOR
Object->MoveDataToSparseClassDataStruct();
if (Object->GetSparseClassDataStruct())
{
// now that any data has been moved into the sparse data structure we can safely serialize it
bIsSparseClassDataSerializable = true;
}
ConformSparseClassData(Object);
#endif
}
```
在UClass下
```cpp
protected:
/** This is where we store the data that is only changed per class instead of per instance */
void* SparseClassData;
/** The struct used to store sparse class data. */
UScriptStruct* SparseClassDataStruct;
在构造UClass的时候,会SetSparseClassDataStruct来把结构传进去,因此就把结构关联起来。
UClass* Z_Construct_UClass_AMyActor_SparseClassDataTypes()
{
if (!Z_Registration_Info_UClass_AMyActor_SparseClassDataTypes.OuterSingleton)
{
UECodeGen_Private::ConstructUClass(Z_Registration_Info_UClass_AMyActor_SparseClassDataTypes.OuterSingleton, Z_Construct_UClass_AMyActor_SparseClassDataTypes_Statics::ClassParams);
Z_Registration_Info_UClass_AMyActor_SparseClassDataTypes.OuterSingleton->SetSparseClassDataStruct(AMyActor_SparseClassDataTypes::StaticGetMySparseClassDataScriptStruct());
}
return Z_Registration_Info_UClass_AMyActor_SparseClassDataTypes.OuterSingleton;
}
```
注意此时BP里没法blueprint get 那些ReadOnly的变量的因为有_DEPRECATED在占用着。一种方法是自己再额外定义Gettter方法
```cpp
UFUNCTION(BlueprintPure)
int32 GetMyMyInt_BlueprintReadOnly()const
{
return GetMySparseClassData()->MyInt_BlueprintReadOnly;
}
```
另一种方法是在MoveDataToSparseClassDataStruct之后记得要打开编辑器并且打开子类BP蓝图后保存就干脆删除掉AMyActor_SparseClassDataTypes里的冗余属性全部使用FMySparseClassData中的值。从而变成
```cpp
USTRUCT(BlueprintType)
struct FMySparseClassData
{
GENERATED_BODY()
UPROPERTY(EditDefaultsOnly)
int32 MyInt_EditDefaultOnly = 123;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
int32 MyInt_BlueprintReadOnly = 1024;
// "GetByRef" means that Blueprint graphs access a const ref instead of a copy.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta=(GetByRef))
FString MyString_EditDefault_ReadOnly = TEXT("MyName");
};
UCLASS(Blueprintable, BlueprintType,SparseClassDataTypes= MySparseClassData)
class INSIDER_API AMyActor_SparseClassDataTypes :public AActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
float MyFloat_EditAnywhere = 555.f;
UPROPERTY(BlueprintReadWrite)
float MyFloat_BlueprintReadWrite = 666.f;
};
```
这样就达到了最终的效果这个效果也对新的Actor要采用冗余属性的结果也是一样的。注意此时在BP里是依然可以访问BlueprintReadOnly属性的因为UHT和BP系统已经帮我们加了一层访问方便的控制。
## 示例效果:
UHT会帮我们生成C++访问函数:
```cpp
#define FID_Hello_Source_Insider_Class_Trait_MyClass_SparseClassDataTypes_h_30_SPARSE_DATA \
FMySparseClassData* GetMySparseClassData(); \
FMySparseClassData* GetMySparseClassData() const; \
const FMySparseClassData* GetMySparseClassData(EGetSparseClassDataMethod GetMethod) const; \
static UScriptStruct* StaticGetMySparseClassDataScriptStruct(); \
int32 GetMyInt_EditDefaultOnly() \
{ \
return GetMySparseClassData()->MyInt_EditDefaultOnly; \
} \
int32 GetMyInt_EditDefaultOnly() const \
{ \
return GetMySparseClassData()->MyInt_EditDefaultOnly; \
} \
int32 GetMyInt_BlueprintReadOnly() \
{ \
return GetMySparseClassData()->MyInt_BlueprintReadOnly; \
} \
int32 GetMyInt_BlueprintReadOnly() const \
{ \
return GetMySparseClassData()->MyInt_BlueprintReadOnly; \
} \
const FString& GetMyString_EditDefault_ReadOnly() \
{ \
return GetMySparseClassData()->MyString_EditDefault_ReadOnly; \
} \
const FString& GetMyString_EditDefault_ReadOnly() const \
{ \
return GetMySparseClassData()->MyString_EditDefault_ReadOnly; \
}
```
在BP中依然可以访问
![Untitled](Untitled.png)
在Class Defaults里也可以改变值
![Untitled](Untitled%201.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,51 @@
# AdvancedClassDisplay
- **功能描述:** 把该类下的所有属性都默认显示在高级目录下
- **引擎模块:** Category
- **元数据类型:** bool
- **作用机制:** 在Meta增加[AdvancedClassDisplay](../../../../Meta/DetailsPanel/AdvancedClassDisplay.md)
- **常用程度:★★★★**
让这个类的所有属性显示在本身类的Detail面板的“高级”栏目下显示。
但是可以通过在单个属性上使用SimpleDisplay来重载掉。在搜索了一番源码后发现使用AdvancedClassDisplay的只有3个Actor且这3个Actor里都没有再定义属性。
## 示例代码:
```cpp
UCLASS(Blueprintable,AdvancedClassDisplay)
class INSIDER_API UMyClass_AdvancedClassDisplay :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
UPROPERTY(EditAnywhere, BlueprintReadWrite,SimpleDisplay)
int32 MyProperty_Simple;
};
UCLASS(Blueprintable)
class INSIDER_API UMyClass_NoAdvancedClassDisplay :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
UPROPERTY(EditAnywhere, BlueprintReadWrite, SimpleDisplay)
int32 MyProperty_Simple;
};
```
## 示例效果:
MyProperty_Simple即使在AdvancedClassDisplay的类中也依然是简单的显示。
![Untitled](Untitled.png)
## 原理:
```cpp
// 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;
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -0,0 +1,25 @@
# AutoCollapseCategories
- **功能描述:** AutoCollapseCategories说明符使父类上的 AutoExpandCategories 说明符的列出类别的效果无效。
- **引擎模块:** Category
- **元数据类型:** strings=(abc"d|e""x|y|z")
- **作用机制:** 在Meta中增加[AutoCollapseCategories](../../../../Meta/DetailsPanel/AutoCollapseCategories.md),去除[AutoExpandCategories](../../../../Meta/DetailsPanel/AutoExpandCategories.md)
- **关联项:** [DontAutoCollapseCategories](../DontAutoCollapseCategories.md)、[AutoExpandCategories](../AutoExpandCategories/AutoExpandCategories.md)
- **常用程度:★**
## 示例代码:
```cpp
UCLASS(Blueprintable, AutoCollapseCategories = ("MyGroup2|MyGroup22"))
class INSIDER_API UMyClass_AutoCollapseCategories :public UMyClass_AutoExpandCategories
{
GENERATED_BODY()
public:
};
```
## 示例结果:
关闭了Group22的展开但是444的展开依然继承了
![Untitled](Untitled.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -0,0 +1,111 @@
# AutoExpandCategories
- **功能描述:** 指定此类的对象在细节面板中应该自动展开的Category。
- **引擎模块:** Category
- **元数据类型:** strings=(abc"d|e""x|y|z")
- **作用机制:** 在Meta中去除[AutoCollapseCategories](../../../../Meta/DetailsPanel/AutoCollapseCategories.md),增加[AutoExpandCategories](../../../../Meta/DetailsPanel/AutoExpandCategories.md)
- **关联项:** [AutoCollapseCategories](../AutoCollapseCategories/AutoCollapseCategories.md)
- **常用程度:★**
指定此类的对象在细节面板中应该自动展开的Category。
- 这里面的Category可以填多个对应本类中属性身上定义的Category。
- 值得注意的是编辑器会自动的保存属性目录的展开关闭状态。影响属性是否展开还会受到DetailPropertyExpansion的配置的影响在打开窗口后SDetailsViewBase::UpdateFilteredDetails()会保存当前展开的属性项目应该是为了下次打开的时候自动展开。保存的代码为GConfig->SetSingleLineArray(TEXT("DetailPropertyExpansion"), *Struct->GetName(), ExpandedPropertyItems, GEditorPerProjectIni); 从而在\Hello\Saved\Config\WindowsEditor\EditorPerProjectUserSettings.ini下保存。因此为了更好的测试该元数据的作用状态。应该手动先清除一下ini中的保存值后再测试。
```cpp
[DetailCategories]
MyClass_AutoExpandCategories.MyClass_AutoExpandCategories=False
MyClass_AutoExpandCategories.MyGroup1=False
MyClass_AutoExpandCategories.MyGroup2=False
MyClass_AutoExpandCategories.MyGroup3=True
MyClass_AutoExpandCategories.MyGroup4=True
[DetailPropertyExpansion]
GeometryCache="\"Object.GeometryCache.Materials\" \"Object.GeometryCache.Tracks\" "
Object="\"Object.MyGroup2.MyGroup2|MyGroup22\" \"Object.MyGroup4.MyGroup4|MyGroup44\" \"Object.MyGroup4.MyGroup4|MyGroup44.MyGroup4|MyGroup44|MyGroup444\" "
GeometryCacheCodecV1="\"Object.GeometryCache.TopologyRanges\" "
GeometryCacheCodecBase="\"Object.GeometryCache.TopologyRanges\" "
MassSettings="\"Object.Mass\" "
DeveloperSettings=
SmartObjectSettings="\"Object.SmartObject\" "
MyClass_ShowCategories=
MyClass_ShowCategoriesChild=
MyClass_DontCollapseCategories="\"Object.MyGroup2.MyGroup2|MyGroup22\" \"Object.MyGroup3.MyGroup3|MyGroup33\" \"Object.MyGroup3.MyGroup3|MyGroup33.MyGroup3|MyGroup33|MyGroup333\" "
MyClass_CollapseCategories="\"Object.MyGroup2.MyGroup2|MyGroup22\" \"Object.MyGroup3.MyGroup3|MyGroup33\" \"Object.MyGroup3.MyGroup3|MyGroup33.MyGroup3|MyGroup33|MyGroup333\" "
MyClass_AutoExpandCategories="\"Object.MyGroup2.MyGroup2|MyGroup22\" \"Object.MyGroup4.MyGroup4|MyGroup44\" \"Object.MyGroup4.MyGroup4|MyGroup44.MyGroup4|MyGroup44|MyGroup444\" "
MyClass_AutoExpandCategoriesCompare=
MyClass_AutoCollapseCategories="\"Object.MyGroup2.MyGroup2|MyGroup22\" \"Object.MyGroup4.MyGroup4|MyGroup44\" \"Object.MyGroup4.MyGroup4|MyGroup44.MyGroup4|MyGroup44|MyGroup444\" "
```
根据代码搜索规则AutoExpandCategories 和AutoCollapseCategories的值要用空格隔开。顶层目录一开始默认就是打开的所以AutoExpandCategories 一般用在子层目录。而且还有个限制是必须一级一级都打开。直接打开最子目录还不行。因此在示例代码里必须要把中间的二级目录"MyGroup4|MyGroup44"也都得写上。
## 示例代码:
```cpp
UCLASS(Blueprintable, AutoExpandCategories = ("MyGroup2|MyGroup22", "MyGroup4|MyGroup44","MyGroup4|MyGroup44|MyGroup444"))
class INSIDER_API UMyClass_AutoExpandCategories :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int Property_NotInGroup;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup1")
int Property_Group1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup2")
int Property_Group2;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup2|MyGroup22")
int Property_Group22;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup3|MyGroup33")
int Property_Group33;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup4|MyGroup44|MyGroup444")
int Property_Group444;
};
源码里最复杂的样例:
UCLASS(Config = Engine, PerObjectConfig, BlueprintType, AutoCollapseCategories = ("Data Layer|Advanced"), AutoExpandCategories = ("Data Layer|Editor", "Data Layer|Advanced|Runtime"))
class ENGINE_API UDataLayerInstance : public UObject
可以打开子目录:UCLASS(Blueprintable, AutoExpandCategories = ("MyGroup2|MyGroup22", "MyGroup4|MyGroup44","MyGroup4|MyGroup44|MyGroup444"))
不可以打开子目录:UCLASS(Blueprintable, AutoExpandCategories = ("MyGroup2|MyGroup22", "MyGroup4|MyGroup44|MyGroup444"))
```
## 示例效果:
在Saved\EditorPerProjectUserSettings中删除掉DetailCategories以及DetailPropertyExpansion下的MyClass_AutoCollapseCategories值之后再用testprops class=MyClass_AutoExpandCategories来打开该窗口
通过对比可以看出Expand确实可以自动展开子目录方便立马编辑。要求是AutoExpandCategories 里填的目录要和属性上的Category匹配
![Untitled](Untitled.png)
## 原理:
UClass里提取AutoExpandCategories和AutoCollapseCategories的元数据来判断Category是否应该显示。
```cpp
if (BaseClass->IsAutoExpandCategory(*CategoryName.ToString()) && !BaseClass->IsAutoCollapseCategory(*CategoryName.ToString()))
{
NewCategoryNode->SetNodeFlags(EPropertyNodeFlags::Expanded, true);
}
bool UClass::IsAutoExpandCategory(const TCHAR* InCategory) const
{
static const FName NAME_AutoExpandCategories(TEXT("AutoExpandCategories"));
if (const FString* AutoExpandCategories = FindMetaData(NAME_AutoExpandCategories))
{
return !!FCString::StrfindDelim(**AutoExpandCategories, InCategory, TEXT(" "));
}
return false;
}
bool UClass::IsAutoCollapseCategory(const TCHAR* InCategory) const
{
static const FName NAME_AutoCollapseCategories(TEXT("AutoCollapseCategories"));
if (const FString* AutoCollapseCategories = FindMetaData(NAME_AutoCollapseCategories))
{
return !!FCString::StrfindDelim(**AutoCollapseCategories, InCategory, TEXT(" "));
}
return false;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -0,0 +1,51 @@
# ClassGroup
- **功能描述:** 指定组件在Actor的AddComponent面板里的分组以及在蓝图右键菜单中的分组。
- **引擎模块:** Category, Editor
- **元数据类型:** string="a|b|c"
- **作用机制:** 在Meta中增加[ClassGroupNames](../../../../Meta/DetailsPanel/ClassGroupNames.md)
- **常用程度:★★★**
指定组件在Actor的AddComponent面板里的分组以及在蓝图右键菜单中的分组。
## 示例代码:
```cpp
//ClassGroup 必须是BlueprintSpawnableComponent才有效
/*
(BlueprintSpawnableComponent = , BlueprintType = true, ClassGroupNames = MyGroup|MySubGroup, IncludePath = Class/MyComponent_ClassGroup.h, IsBlueprintBase = true, ModuleRelativePath = Class/MyComponent_ClassGroup.h)
*/
UCLASS(Blueprintable,ClassGroup="MyGroup|MySubGroup", meta = (BlueprintSpawnableComponent))
class INSIDER_API UMyComponent_ClassGroup:public UActorComponent
{
GENERATED_BODY()
public:
};
```
## 示例效果:
在添加组件的时候:
![Untitled](Untitled.png)
在蓝图中右键AddComponent该测试只对带有BlueprintSpawnableComponent的UActorComponent起作用因为只有BlueprintSpawnableComponent才可在蓝图中动态添加组件。
![Untitled](Untitled%201.png)
## 原理:
Metadata中的ClassGroupNames被使用方法是UClass::GetClassGroupNames这个又是在BlueprintComponentNodeSpawner中被使用。还有一个使用地方是ComponentTypeRegistry.cpp中也是在判断Component。因此这个ClassGroup确实是只被Component使用的。
```cpp
static FText GetDefaultMenuCategory(const TSubclassOf<UActorComponent> ComponentClass)
{
TArray<FString> ClassGroupNames;
ComponentClass->GetClassGroupNames(ClassGroupNames);
if (FKismetEditorUtilities::IsClassABlueprintSpawnableComponent(Class))
{
TArray<FString> ClassGroupNames;
Class->GetClassGroupNames(ClassGroupNames);
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,64 @@
# CollapseCategories
- **功能描述:** 在类的属性面板里隐藏所有带Category的属性但是只对带有多个嵌套Category的属性才起作用。
- **引擎模块:** Category
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中添加[CLASS_CollapseCategories](../../../../Flags/EClassFlags/CLASS_CollapseCategories.md)
- **关联项:** [DontCollapseCategories](../DontCollapseCategories.md)
- **常用程度:★★**
在类的属性面板里隐藏所有带Category的属性但是只对带有多个嵌套Category的属性才起作用。
## 示例代码:
```cpp
/*
ClassFlags: CLASS_MatchedSerializers | CLASS_Native | CLASS_CollapseCategories | CLASS_RequiredAPI | CLASS_TokenStreamAssembled | CLASS_Intrinsic | CLASS_Constructed
*/
UCLASS(Blueprintable, CollapseCategories)
class INSIDER_API UMyClass_CollapseCategories :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int Property_NotInGroup;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup1")
int Property_Group1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup2|MyGroup22")
int Property_Group22;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup3|MyGroup33|MyGroup333")
int Property_Group333;
};
/*
ClassFlags: CLASS_MatchedSerializers | CLASS_Native | CLASS_RequiredAPI | CLASS_TokenStreamAssembled | CLASS_Intrinsic | CLASS_Constructed
*/
UCLASS(Blueprintable, dontCollapseCategories)
class INSIDER_API UMyClass_DontCollapseCategories :public UMyClass_CollapseCategories
{
GENERATED_BODY()
public:
};
```
## 示例效果:
第一个是UMyClass_CollapseCategories 的效果第二个是UMyClass_DontCollapseCategories 的效果,可见一些属性被隐藏了起来。
![Untitled](Untitled.png)
## 原理:
```cpp
if (Specifier == TEXT("collapseCategories"))
{
// Class' properties should not be shown categorized in the editor.
ClassFlags |= CLASS_CollapseCategories;
}
else if (Specifier == TEXT("dontCollapseCategories"))
{
// Class' properties should be shown categorized in the editor.
ClassFlags &= ~CLASS_CollapseCategories;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -0,0 +1,90 @@
# ComponentWrapperClass
- **功能描述:** 指定该类为一个简单的封装类忽略掉子类的Category相关设置。
- **引擎模块:** Category
- **元数据类型:** bool
- **作用机制:** 在Meta中增加[IgnoreCategoryKeywordsInSubclasses](../../../../Meta/DetailsPanel/IgnoreCategoryKeywordsInSubclasses.md)
- **常用程度:★★**
指定该类为一个简单的封装类忽略掉子类的Category相关设置。
如名字所说为一个组件的包装类其实就是一个Actor简单的只包含一个Component。这种简单的包装关系典型的例子是ALight包装ULightComponentASkeletalMeshActor包装USkeletalMeshComponent。
控制子类上面定义的hideCategories和showCategories都被忽略而直接采用基类上的目录定义也就是本组件包装类上的目录设置。当前源码里只有BlueprintEditorUtils.cpp在用而这是蓝图打开的过程因此这个只有在双击打开一个蓝图的时候才起作用。普通的UObject类直接用testprops创建的窗口因为不是双击打开蓝图因此是无法生效的。
ComponentWrapperClass在源码里搜了一下只有一些Actor在用。
## 示例代码:
```cpp
UCLASS(Blueprintable, BlueprintType, ComponentWrapperClass, hideCategories = MyGroup3) //依然会显示出Property_Group3
class AMyActor_ComponentWrapperClass : public AActor
{
GENERATED_UCLASS_BODY()
public:
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
class UPointLightComponent* PointLightComponent;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = MyGroup3)
int Property_Group3;
};
UCLASS(Blueprintable, BlueprintType, hideCategories = MyGroup3)
class AMyActor_NoComponentWrapperClass : public AActor //Property_Group3会被隐藏
{
GENERATED_UCLASS_BODY()
public:
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
class UPointLightComponent* PointLightComponent;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = MyGroup3)
int Property_Group3;
};
```
## 子类的实际作用效果:
子类里的MyGroup3即使被隐藏了起来也还是显示了出来。
![Untitled](Untitled.png)
## 原理:
ComponentWrapperClass实际会造成IgnoreCategoryKeywordsInSubclasses=true的元数据添加。因此在有了IgnoreCategoryKeywordsInSubclasses元数据之后就不会判断之后的ShowCategories等设置了。
当前源码里只有BlueprintEditorUtils.cpp在用而这是蓝图打开的过程因此这个只有在双击打开一个蓝图的时候才起作用。普通的UObject类直接用testprops创建的窗口因为不是双击打开蓝图因此是无法生效的。
```cpp
case EClassMetadataSpecifier::ComponentWrapperClass:
MetaData.Add(NAME_IgnoreCategoryKeywordsInSubclasses, TEXT("true")); //"IgnoreCategoryKeywordsInSubclasses"
break;
////////////////////////////////////////////////////////////////////////////////
E:\P4V\Engine\Source\Editor\UnrealEd\Private\Kismet2\BlueprintEditorUtils.cpp
void FBlueprintEditorUtils::RecreateClassMetaData(UBlueprint* Blueprint, UClass* Class, bool bRemoveExistingMetaData)
if (!ParentClass->HasMetaData(FBlueprintMetadata::MD_IgnoreCategoryKeywordsInSubclasses)) //如果没有这个设置
{
// we want the categories just as they appear in the parent class
// (set bHomogenize to false) - especially since homogenization
// could inject spaces
//以下这些操作是当没有这个设置的时候,子类会继承父类的目录设置。
FEditorCategoryUtils::GetClassHideCategories(ParentClass, AllHideCategories, /*bHomogenize =*/false);
if (ParentClass->HasMetaData(TEXT("ShowCategories")))
{
Class->SetMetaData(TEXT("ShowCategories"), *ParentClass->GetMetaData("ShowCategories"));
}
if (ParentClass->HasMetaData(TEXT("AutoExpandCategories")))
{
Class->SetMetaData(TEXT("AutoExpandCategories"), *ParentClass->GetMetaData("AutoExpandCategories"));
}
if (ParentClass->HasMetaData(TEXT("AutoCollapseCategories")))
{
Class->SetMetaData(TEXT("AutoCollapseCategories"), *ParentClass->GetMetaData("AutoCollapseCategories"));
}
if (ParentClass->HasMetaData(TEXT("PrioritizeCategories")))
{
Class->SetMetaData(TEXT("PrioritizeCategories"), *ParentClass->GetMetaData("PrioritizeCategories"));
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View File

@@ -0,0 +1,64 @@
# DontAutoCollapseCategories
- **功能描述:** 使列出的类别的继承自父类的AutoCollapseCategories说明符无效。
- **引擎模块:** Category
- **元数据类型:** strings="abc"
- **作用机制:** 在Meta中去除[AutoCollapseCategories](../../../Meta/DetailsPanel/AutoCollapseCategories.md)
- **关联项:** [AutoCollapseCategories](AutoCollapseCategories/AutoCollapseCategories.md)
- **常用程度:★**
根据代码只是简单的移除AutoCollapseCategories和AutoExpandCategories的区别就是不会自动加一个展开。在源码里搜了一下并没有使用到。而且当前的源码实现有bug做不到去除。
```cpp
case EClassMetadataSpecifier::AutoExpandCategories:
FHeaderParser::RequireSpecifierValue(*this, PropSpecifier);
for (FString& Value : PropSpecifier.Values)
{
AutoCollapseCategories.RemoveSwap(Value);
AutoExpandCategories.AddUnique(MoveTemp(Value));
}
break;
case EClassMetadataSpecifier::AutoCollapseCategories:
FHeaderParser::RequireSpecifierValue(*this, PropSpecifier);
for (FString& Value : PropSpecifier.Values)
{
AutoExpandCategories.RemoveSwap(Value);
AutoCollapseCategories.AddUnique(MoveTemp(Value));
}
break;
case EClassMetadataSpecifier::DontAutoCollapseCategories:
FHeaderParser::RequireSpecifierValue(*this, PropSpecifier);
for (const FString& Value : PropSpecifier.Values)
{
AutoCollapseCategories.RemoveSwap(Value);//当前AutoCollapseCategories的值还是空的。去除是没有用的
}
break;
改动:
FUnrealClassDefinitionInfo::MergeClassCategories()放最后:
// Merge DontAutoCollapseCategories and AutoCollapseCategories
for (const FString& Value : DontAutoCollapseCategories)
{
AutoCollapseCategories.RemoveSwap(Value);
}
DontAutoCollapseCategories.Empty();
改为:
case EClassMetadataSpecifier::DontAutoCollapseCategories:
FHeaderParser::RequireSpecifierValue(*this, PropSpecifier);
for (FString& Value : PropSpecifier.Values)
{
DontAutoCollapseCategories.AddUnique(MoveTemp(Value));
//AutoCollapseCategories.RemoveSwap(Value);
}
break;
```

View File

@@ -0,0 +1,10 @@
# DontCollapseCategories
- **功能描述:** 使继承自基类的CollapseCatogories说明符无效。
- **引擎模块:** Category
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中去除[CLASS_CollapseCategories](../../../Flags/EClassFlags/CLASS_CollapseCategories.md)
- **关联项:** [CollapseCategories](CollapseCategories/CollapseCategories.md)
- **常用程度:★★**
理论上是去除类标志上的CLASS_CollapseCategories标志。可以重新打开所有的属性显示。

View File

@@ -0,0 +1,100 @@
# HideCategories
- **功能描述:** 在类的ClassDefaults属性面板里隐藏某些Category的属性。
- **引擎模块:** Category
- **元数据类型:** strings=(abc"d|e""x|y|z")
- **关联项:** [ShowCategories](../ShowCategories/ShowCategories.md)
- **常用程度:★★★★**
在类的ClassDefaults属性面板里隐藏某些Category的属性。
注意要先在类里定义属性然后设置它的Category。HideCategories的信息会被UHT分析并保存到UClass的元数据里去。HideCategories的信息可以被子类继承下来。
## 示例代码:
```cpp
UCLASS(Blueprintable, hideCategories = MyGroup1)
class INSIDER_API UMyClass_HideCategories :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = MyGroup1)
int Property_Group1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup2 | MyGroup3")
int Property_Group23;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int Property_NotInGroup;
};
/*
(BlueprintType = true, HideCategories = MyGroup2 | MyGroup3, IncludePath = Class/Display/MyClass_ShowCategories.h, IsBlueprintBase = true, ModuleRelativePath = Class/Display/MyClass_ShowCategories.h)
*/
UCLASS(Blueprintable, showCategories = MyGroup1, hideCategories = "MyGroup2 | MyGroup3")
class INSIDER_API UMyClass_HideCategoriesChild :public UMyClass_ShowCategories
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup2")
int Property_Group2;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup3")
int Property_Group3;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = MyGroup4)
int Property_Group4;
};
```
## 示例效果:
注意这里单独的MyGroup2和MyGroup3也都没有显示。所以判断的标准只要目录符合某个目录名字匹配就行。
![Untitled](Untitled.png)
## 原理:
在GetClassHideCategories中检查ClassHideCategoriesMetaKey元数据。
```cpp
void FEditorCategoryUtils::GetClassShowCategories(const UStruct* Class, TArray<FString>& CategoriesOut)
{
CategoriesOut.Empty();
using namespace FEditorCategoryUtilsImpl;
if (Class->HasMetaData(ClassShowCategoriesMetaKey))
{
const FString& ShowCategories = Class->GetMetaData(ClassShowCategoriesMetaKey);
ShowCategories.ParseIntoArray(CategoriesOut, TEXT(" "), /*InCullEmpty =*/true);
for (FString& Category : CategoriesOut)
{
Category = GetCategoryDisplayString(FText::FromString(Category)).ToString();
}
}
}
void FEditorCategoryUtils::GetClassHideCategories(const UStruct* Class, TArray<FString>& CategoriesOut, bool bHomogenize)
{
CategoriesOut.Empty();
using namespace FEditorCategoryUtilsImpl;
if (Class->HasMetaData(ClassHideCategoriesMetaKey))
{
const FString& HideCategories = Class->GetMetaData(ClassHideCategoriesMetaKey);
HideCategories.ParseIntoArray(CategoriesOut, TEXT(" "), /*InCullEmpty =*/true);
if (bHomogenize)
{
for (FString& Category : CategoriesOut)
{
Category = GetCategoryDisplayString(Category);
}
}
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -0,0 +1,51 @@
# PrioritizeCategories
- **功能描述:** 把指定的属性目录优先显示在细节面板的前面。
- **引擎模块:** Category
- **元数据类型:** strings=(abc"d|e""x|y|z")
- **作用机制:** 在Meta中增加[PrioritizeCategories](../../../../Meta/DetailsPanel/PrioritizeCategories.md)
- **常用程度:★★★**
把指定的属性目录优先显示在细节面板的前面。
## 示例代码:
```cpp
UCLASS(Blueprintable, PrioritizeCategories= ("MyGroup3|MyGroup33|MyGroup333","MyGroup1"))
class INSIDER_API UMyClass_PrioritizeCategories :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int Property_NotInGroup;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup1")
int Property_Group1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup2|MyGroup22")
int Property_Group22;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup3|MyGroup33|MyGroup333")
int Property_Group333;
};
```
## 示例结果:
可见Property_Group333排到了最前面。
![Untitled](Untitled.png)
## 原理:
在UClass::GetPrioritizeCategories(TArray<FString>& OutPrioritizedCategories)中获取优先级目录。原理是按照指定的顺序放到SortedCategories里所以就会被首先创建出属性目录来。
```cpp
TArray<FString> ClassPrioritizeCategories;
Class->GetPrioritizeCategories(ClassPrioritizeCategories);
for (const FString& ClassPrioritizeCategory : ClassPrioritizeCategories)
{
FName PrioritizeCategoryName = FName(ClassPrioritizeCategory);
SortedCategories.AddUnique(PrioritizeCategoryName);
PrioritizeCategories.AddUnique(PrioritizeCategoryName);
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -0,0 +1,102 @@
# ShowCategories
- **功能描述:** 在类的ClassDefaults属性面板里显示某些Category的属性。
- **引擎模块:** Category
- **元数据类型:** strings=(abc"d|e""x|y|z")
- **作用机制:** 在Meta中增加[HideCategories](../../../../Meta/DetailsPanel/HideCategories.md)
- **关联项:** [HideCategories](../HideCategories/HideCategories.md)
- **常用程度:★★★**
在类的ClassDefaults属性面板里显示某些Category的属性。使列出的类别的继承自基类的HideCategories说明符无效。
ShowCategories会被UHT分析但不会被保存到UClass的元数据里去。它作用的方式是可以抹去之前基类设置的HideCategories的属性。ShowCategories可以被子类继承下来。
## 示例代码:
```cpp
/*
(BlueprintType = true, HideCategories = MyGroup1, IncludePath = Class/Display/MyClass_ShowCategories.h, IsBlueprintBase = true, ModuleRelativePath = Class/Display/MyClass_ShowCategories.h)
*/
UCLASS(Blueprintable, hideCategories = MyGroup1)
class INSIDER_API UMyClass_ShowCategories :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = MyGroup1)
int Property_Group1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup2 | MyGroup3")
int Property_Group23;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int Property_NotInGroup;
};
/*
(BlueprintType = true, HideCategories = MyGroup2 | MyGroup3, IncludePath = Class/Display/MyClass_ShowCategories.h, IsBlueprintBase = true, ModuleRelativePath = Class/Display/MyClass_ShowCategories.h)
*/
UCLASS(Blueprintable, showCategories = MyGroup1, hideCategories = "MyGroup2 | MyGroup3")
class INSIDER_API UMyClass_ShowCategoriesChild :public UMyClass_ShowCategories
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup2")
int Property_Group2;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup3")
int Property_Group3;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = MyGroup4)
int Property_Group4;
};
```
## 示例效果:
![Untitled](./Image/Untitled.png)
## 原理:
其实实际上UHT保存的只在HideCategories里这点通过对类的元数据查看就可知。
```cpp
void FEditorCategoryUtils::GetClassShowCategories(const UStruct* Class, TArray<FString>& CategoriesOut)
{
CategoriesOut.Empty();
using namespace FEditorCategoryUtilsImpl;
if (Class->HasMetaData(ClassShowCategoriesMetaKey))
{
const FString& ShowCategories = Class->GetMetaData(ClassShowCategoriesMetaKey);
ShowCategories.ParseIntoArray(CategoriesOut, TEXT(" "), /*InCullEmpty =*/true);
for (FString& Category : CategoriesOut)
{
Category = GetCategoryDisplayString(FText::FromString(Category)).ToString();
}
}
}
void FEditorCategoryUtils::GetClassHideCategories(const UStruct* Class, TArray<FString>& CategoriesOut, bool bHomogenize)
{
CategoriesOut.Empty();
using namespace FEditorCategoryUtilsImpl;
if (Class->HasMetaData(ClassHideCategoriesMetaKey))
{
const FString& HideCategories = Class->GetMetaData(ClassHideCategoriesMetaKey);
HideCategories.ParseIntoArray(CategoriesOut, TEXT(" "), /*InCullEmpty =*/true);
if (bHomogenize)
{
for (FString& Category : CategoriesOut)
{
Category = GetCategoryDisplayString(Category);
}
}
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -0,0 +1,58 @@
# Config
- **功能描述:** 指定配置文件的名字把该对象的值保存到ini配置文件中。
- **引擎模块:** Config
- **元数据类型:** string="abc"
- **作用机制:** Config文件名存在FName UClass::ClassConfigName这个参数里
- **关联项:** [PerObjectConfig](PerObjectConfig.md)、[ConfigDoNotCheckDefaults](ConfigDoNotCheckDefaults.md)、[DefaultConfig](DefaultConfig/DefaultConfig.md)、[GlobalUserConfig](GlobalUserConfig/GlobalUserConfig.md)、[ProjectUserConfig](ProjectUserConfig/ProjectUserConfig.md)
- **常用程度:★★★★★**
指定配置文件的名字把该对象的值保存到ini配置文件中。
- 一整个类在ini中只有一个节的值因此一般是保存的CDO对象但也可以用普通对象。
- Config文件名称的元数据值保存在FName UClass::ClassConfigName。
- 默认是保存在Saved/XXX.ini的Local文件中。
- 此说明符会传播到所有子类并且无法使此说明符无效但是子类可通过重新声明config说明符并提供不同的ConfigName来更改配置文件。
- 常见的ConfigName值是“Engine”、“Editor”、“Input”和“Game”。
- 可以自己手动调用SaveConfig和LoadConfig来读写配置值。CDO的值会被引擎自己的从配置中读取而更新。
- 想保存到配置文件里的属性要相应的用UPROPERTY(config)修饰。
## 示例代码:
```cpp
UCLASS(Config = Game)
class INSIDER_API UMyClass_Config :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Config)
int32 MyPropertyWithConfig = 123;
};
//测试代码
UMyClass_Config* testObject = NewObject<UMyClass_Config>(GetTransientPackage(),TEXT("testObject"));
testObject->SaveConfig();
//生成
\Hello\Saved\Config\WindowsEditor\Game.ini
[/Script/Insider.MyClass_Config]
MyPropertyWithConfig=123
```
## 原理:
在引擎启动的时候UObjectLoadAllCompiledInDefaultProperties会加载所有Class的CDO在多个调用链条之后会自动的调用CDO的LoadConfig来初始化CDO的值。
```cpp
static void UObjectLoadAllCompiledInDefaultProperties(TArray<UClass*>& OutAllNewClasses)
{
for (UClass* Class : NewClasses)
{
UE_LOG(LogUObjectBootstrap, Verbose, TEXT("GetDefaultObject Begin %s %s"), *Class->GetOutermost()->GetName(), *Class->GetName());
Class->GetDefaultObject();
UE_LOG(LogUObjectBootstrap, Verbose, TEXT("GetDefaultObject End %s %s"), *Class->GetOutermost()->GetName(), *Class->GetName());
}
}
```

View File

@@ -0,0 +1,93 @@
# ConfigDoNotCheckDefaults
- **功能描述:** 指定在保存配置值的时候忽略上一级的配置值的一致性检查。
- **引擎模块:** Config
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中增加[CLASS_ConfigDoNotCheckDefaults](../../../Flags/EClassFlags/CLASS_ConfigDoNotCheckDefaults.md)
- **关联项:** [Config](Config.md)
- **常用程度:★**
指定在保存配置值的时候忽略上一级的配置值的一致性检查。
- 在保存配置的时候决定是否要先根据Base或Default的配置来检查属性是否一致如果一致就不用序列化写入下来。但加上这个标志后即使同上一个层级的配置值相同也无论如何都要保存下来。
UCLASS(config=XXX,configdonotcheckdefaults)表示这个类对应的配置文件不会检查XXX层级上层的DefaultXXX配置文件是否有该信息后面会解释层级就直接存储到Saved目录下。
## 示例代码:
```cpp
UCLASS(Config = Game)
class INSIDER_API UMyClass_Config :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Config)
int32 MyPropertyWithConfig = 123;
};
UCLASS(Config = Game,configdonotcheckdefaults)
class INSIDER_API UMyClass_ConfigDoNotCheckDefaults :public UMyClass_Config
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Config)
int32 MyPropertyWithConfigSub = 123;
};
UCLASS(Config = Game)
class INSIDER_API UMyClass_ConfigDefaultChild :public UMyClass_Config
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Config)
int32 MyPropertyWithConfigSub = 123;
};
```
## 示例效果:
```cpp
void UMyClass_Config_Test::TestConfigCheckDefaultSave()
{
auto* testObject = NewObject<UMyClass_ConfigDoNotCheckDefaults>(GetTransientPackage(), TEXT("testObjectCheckDefault"));
auto* testObject2 = NewObject<UMyClass_ConfigDefaultChild>(GetTransientPackage(), TEXT("testObjectDefaultChild"));
testObject->SaveConfig();
testObject2->SaveConfig();
}
生成:
[/Script/Insider.MyClass_Config]
MyPropertyWithConfig=777
[/Script/Insider.MyClass_ConfigDoNotCheckDefaults]
MyPropertyWithConfigSub=123
MyPropertyWithConfig=777
[/Script/Insider.MyClass_ConfigDefaultChild]
MyPropertyWithConfigSub=123
```
由此可见MyClass_ConfigDoNotCheckDefaults中的MyPropertyWithConfig的值默认跟UMyClass_Config中的777值一致但是依然会写入进来。在MyClass_ConfigDefaultChild类中MyPropertyWithConfig的值因为没有改变就会被略过。
在源码里搜configdonotcheckdefaults的时候发现常常和defaultconfig配合使用。什么时候应该使用configdonotcheckdefaults感觉是为了保持自己的完整性无论如何都要全部写入进去。在defaultConfig的时候就可以不管Base里的值都写入一份到Default配置里这样在编辑上更加的完整。
## 原理:
```cpp
const bool bShouldCheckIfIdenticalBeforeAdding = !GetClass()->HasAnyClassFlags(CLASS_ConfigDoNotCheckDefaults) && !bPerObject && bIsPropertyInherited;
//简单的示例判断
if (!bPropDeprecated && (!bShouldCheckIfIdenticalBeforeAdding || !Property->Identical_InContainer(this, SuperClassDefaultObject, Index)))
{
FString Value;
Property->ExportText_InContainer( Index, Value, this, this, this, PortFlags );
Config->SetString( *Section, *Key, *Value, PropFileName );
}
else
{
// If we are not writing it to config above, we should make sure that this property isn't stagnant in the cache.
Config->RemoveKey( *Section, *Key, PropFileName );
}
```

View File

@@ -0,0 +1,89 @@
# DefaultConfig
- **功能描述:** 指定保存到的配置文件层级是Project/Config/DefaultXXX.ini。
- **引擎模块:** Config
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中增加[CLASS_DefaultConfig](../../../../Flags/EClassFlags/CLASS_DefaultConfig.md)
- **关联项:** [Config](../Config.md)
- **常用程度:★★★**
指定保存到的配置文件层级是Project/Config/DefaultXXX.ini。
- 而不是默认的Saved/XXX.ini
- 一般用在编辑器里把Settings自动保存到Project/Config/DefaultXXX.ini里去
## 示例代码:
```cpp
UCLASS(Config = MyGame,DefaultConfig)
class INSIDER_API UMyClass_DefaultConfig :public UDeveloperSettings
{
GENERATED_BODY()
public:
/** Gets the settings container name for the settings, either Project or Editor */
virtual FName GetContainerName() const override { return TEXT("Project"); }
/** Gets the category for the settings, some high level grouping like, Editor, Engine, Game...etc. */
virtual FName GetCategoryName() const override { return TEXT("MyGame"); }
/** The unique name for your section of settings, uses the class's FName. */
virtual FName GetSectionName() const override { return TEXT("MyGame"); }
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Config)
int32 MyPropertyWithConfig = 123;
};
//保存的结果:
//Config/DefaultMyGame.ini
[/Script/Insider.MyClass_DefaultConfig]
MyPropertyWithConfig=888
```
## 示例结果:
![Untitled](Untitled.png)
## 原理:
代码里要使用Settings->TryUpdateDefaultConfigFile();但发现TryUpdateDefaultConfigFile不管有没有DefaultConfig都可以调用都可以保存到Default里。因此应该调用哪个SaveConfigTryUpdateDefaultConfigFileUpdateGlobalUserConfigFileUpdateProjectUserConfigFile是可以手动指定的。
但是在编辑器里编辑的时候则可以通过写好的代码来处理好逻辑。如SSettingsEditor.cpp里NotifyPostChange中调用Section->Save();则可以在内部再调用如下代码:
```cpp
bool FSettingsSection::Save()
{
if (ModifiedDelegate.IsBound() && !ModifiedDelegate.Execute())
{
return false;
}
if (SaveDelegate.IsBound())
{
return SaveDelegate.Execute();
}
//更新到正确的文件里
if (SettingsObject.IsValid())
{
if (SettingsObject->GetClass()->HasAnyClassFlags(CLASS_DefaultConfig))
{
SettingsObject->TryUpdateDefaultConfigFile();
}
else if (SettingsObject->GetClass()->HasAnyClassFlags(CLASS_GlobalUserConfig))
{
SettingsObject->UpdateGlobalUserConfigFile();
}
else if (SettingsObject->GetClass()->HasAnyClassFlags(CLASS_ProjectUserConfig))
{
SettingsObject->UpdateProjectUserConfigFile();
}
else
{
SettingsObject->SaveConfig();
}
return true;
}
return false;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -0,0 +1,70 @@
# EditorConfig
- **功能描述:** 用来在编辑器状态下保存信息。
- **引擎模块:** Config, Editor
- **元数据类型:** string="abc"
- **作用机制:** 在Meta中增加[EditorConfig](../../../../Meta/Config/EditorConfig.md)
- **常用程度:★**
用来在编辑器状态下保存信息。
一般用在EditorTarget的Module里用于配置相应编辑器的信息比如列宽收藏夹之类的用json保存。
保存在C:\Users\{user name}\AppData\Local\UnrealEngine\Editor。当前有
![Untitled](Untitled.png)
在源码里搜索后,使用的时候必须继承于基类:
```cpp
/** Inherit from this class to simplify saving and loading properties from editor configs. */
UCLASS()
class EDITORCONFIG_API UEditorConfigBase : public UObject
{
GENERATED_BODY()
public:
/** Load any properties of this class into properties marked with metadata tag "EditorConfig" from the class's EditorConfig */
bool LoadEditorConfig();
/** Save any properties of this class in properties marked with metadata tag "EditorConfig" into the class's EditorConfig. */
bool SaveEditorConfig() const;
};
```
## 示例代码:
```cpp
UCLASS(EditorConfig = "MyEditorGame")
class INSIDER_API UMyClass_EditorConfig : public UEditorConfigBase
{
public:
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditorConfig))
int32 MyPropertyWithConfig = 123;
};
void UMyClass_EditorConfig_Test::TestConfigSave()
{
//must run after editor initialization
auto* testObject = NewObject<UMyClass_EditorConfig>(GetTransientPackage(), TEXT("testObject_EditorConfig"));
testObject->MyPropertyWithConfig = 777;
testObject->SaveEditorConfig();
}
void UMyClass_EditorConfig_Test::TestConfigLoad()
{
auto* testObject = NewObject<UMyClass_EditorConfig>(GetTransientPackage(), TEXT("testObject_EditorConfig"));
testObject->LoadEditorConfig();
}
//运行Save后的保存结果C:\Users\jack.fu\AppData\Local\UnrealEngine\Editor\MyEditorGame.json
{
"$type": "MyClass_EditorConfig",
"MyPropertyWithConfig": 777
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

View File

@@ -0,0 +1,74 @@
# GlobalUserConfig
- **功能描述:** 指定保存到的配置文件层级是全局用户设置 Engine/Config/UserXXX.ini。
- **引擎模块:** Config
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中增加[CLASS_GlobalUserConfig](../../../../Flags/EClassFlags/CLASS_GlobalUserConfig.md)
- **关联项:** [Config](../Config.md)
- **常用程度:★★★**
指定保存到的配置文件层级是全局用户设置 Engine/Config/UserXXX.ini。
## 示例代码:
属性用Config或者GlobalConfig都是可以的。
```cpp
UCLASS(Config = MyGame, GlobalUserConfig)
class INSIDER_API UMyClass_GlobalUserConfig:public UDeveloperSettings
{
GENERATED_BODY()
public:
/** Gets the settings container name for the settings, either Project or Editor */
virtual FName GetContainerName() const override { return TEXT("Project"); }
/** Gets the category for the settings, some high level grouping like, Editor, Engine, Game...etc. */
virtual FName GetCategoryName() const override { return TEXT("MyGame"); }
/** The unique name for your section of settings, uses the class's FName. */
virtual FName GetSectionName() const override { return TEXT("MyGlobalGame"); }
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Config)
int32 MyPropertyWithConfig = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, GlobalConfig)
int32 MyPropertyWithGlobalConfig = 456;
};
保存到C:\Users\jack.fu\AppData\Local\Unreal Engine\Engine\Config\UserMyGame.ini
[/Script/Insider.UMyClass_GlobalUserConfig]
MyPropertyWithGlobalConfig=999
```
## 示例效果:
![Untitled](Untitled.png)
## 源码例子:
```cpp
UCLASS(config=Engine, globaluserconfig)
class ANDROIDPLATFORMEDITOR_API UAndroidSDKSettings : public UObject
{
public:
GENERATED_UCLASS_BODY()
// Location on disk of the Android SDK (falls back to ANDROID_HOME environment variable if this is left blank)
UPROPERTY(GlobalConfig, EditAnywhere, Category = SDKConfig, Meta = (DisplayName = "Location of Android SDK (the directory usually contains 'android-sdk-')"))
FDirectoryPath SDKPath;
// Location on disk of the Android NDK (falls back to NDKROOT environment variable if this is left blank)
UPROPERTY(GlobalConfig, EditAnywhere, Category = SDKConfig, Meta = (DisplayName = "Location of Android NDK (the directory usually contains 'android-ndk-')"))
FDirectoryPath NDKPath;
// Location on disk of Java (falls back to JAVA_HOME environment variable if this is left blank)
UPROPERTY(GlobalConfig, EditAnywhere, Category = SDKConfig, Meta = (DisplayName = "Location of JAVA (the directory usually contains 'jdk')"))
FDirectoryPath JavaPath;
// Which SDK to package and compile Java with (a specific version or (without quotes) 'latest' for latest version on disk, or 'matchndk' to match the NDK API Level)
UPROPERTY(GlobalConfig, EditAnywhere, Category = SDKConfig, Meta = (DisplayName = "SDK API Level (specific version, 'latest', or 'matchndk' - see tooltip)"))
FString SDKAPILevel;
// Which NDK to compile with (a specific version or (without quotes) 'latest' for latest version on disk). Note that choosing android-21 or later won't run on pre-5.0 devices.
UPROPERTY(GlobalConfig, EditAnywhere, Category = SDKConfig, Meta = (DisplayName = "NDK API Level (specific version or 'latest' - see tooltip)"))
FString NDKAPILevel;
};
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -0,0 +1,73 @@
# PerObjectConfig
- **功能描述:** 在已经有config配置文件名字的情况下指定应该按每个对象实例来存储值而不是一个类一个存储值。
- **引擎模块:** Config
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中增加[CLASS_PerObjectConfig](../../../Flags/EClassFlags/CLASS_PerObjectConfig.md)
- **关联项:** [Config](Config.md)
- **常用程度:★★★★★**
在已经有config配置文件名字的情况下指定应该按每个对象实例来存储值而不是一个类一个存储值。
- 此类的配置信息将按对象存储,在.ini文件中每个对象都有一个分段根据对象命名格式为[ObjectName ClassName]。
- 此说明符会传播到子类。指定该配置是对每个对象都单独保存。
## 示例代码:
注意ObjectName必须一致
```cpp
UCLASS(Config = Game,PerObjectConfig)
class INSIDER_API UMyClass_PerObjectConfig :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Config)
int32 MyPropertyWithConfig = 123;
};
void UMyClass_Config_Test::TestPerObjectConfigSave()
{
UMyClass_PerObjectConfig* testObject1 = NewObject<UMyClass_PerObjectConfig>(GetTransientPackage(), TEXT("testObject1"));
testObject1->MyPropertyWithConfig = 456;
testObject1->SaveConfig();
UMyClass_PerObjectConfig* testObject2 = NewObject<UMyClass_PerObjectConfig>(GetTransientPackage(), TEXT("testObject2"));
testObject2->MyPropertyWithConfig = 789;
testObject2->SaveConfig();
}
void UMyClass_Config_Test::TestPerObjectConfigLoad()
{
UMyClass_PerObjectConfig* testObject1 = NewObject<UMyClass_PerObjectConfig>(GetTransientPackage(), TEXT("testObject1"));
//testObject1->LoadConfig(); //不需要显式调用LoadConfig
UMyClass_PerObjectConfig* testObject2 = NewObject<UMyClass_PerObjectConfig>(GetTransientPackage(), TEXT("testObject2"));
//testObject2->LoadConfig();
}
//\Saved\Config\WindowsEditor\Game.ini
[testObject1 MyClass_PerObjectConfig]
MyPropertyWithConfig=456
[testObject2 MyClass_PerObjectConfig]
MyPropertyWithConfig=789
```
## 原理:
对象构造的末期会尝试去读取配置。
```cpp
void FObjectInitializer::PostConstructInit()
{
//在NewObject构造中后面会调用
if (bIsCDO || Class->HasAnyClassFlags(CLASS_PerObjectConfig))
{
Obj->LoadConfig(NULL, NULL, bIsCDO ? UE::LCPF_ReadParentSections : UE::LCPF_None);
}
}
```

View File

@@ -0,0 +1,53 @@
# ProjectUserConfig
- **功能描述:** 指定保存到的配置文件层级是项目用户设置 Project/Config/UserXXX.ini。
- **引擎模块:** Config
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中增加[CLASS_ProjectUserConfig](../../../../Flags/EClassFlags/CLASS_ProjectUserConfig.md)
- **关联项:** [Config](../Config.md)
- **常用程度:★★★**
指定保存到的配置文件层级是项目用户设置 Project/Config/UserXXX.ini。
## 示例代码:
保存的目录是\Hello\Config\UserMyGame.ini
```cpp
UCLASS(Config = MyGame, ProjectUserConfig)
class INSIDER_API UMyClass_ProjectUserConfig :public UDeveloperSettings
{
GENERATED_BODY()
public:
/** Gets the settings container name for the settings, either Project or Editor */
virtual FName GetContainerName() const override { return TEXT("Project"); }
/** Gets the category for the settings, some high level grouping like, Editor, Engine, Game...etc. */
virtual FName GetCategoryName() const override { return TEXT("MyGame"); }
/** The unique name for your section of settings, uses the class's FName. */
virtual FName GetSectionName() const override { return TEXT("MyProjectGame"); }
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Config)
int32 MyPropertyWithConfig = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, GlobalConfig)
int32 MyPropertyWithGlobalConfig = 456;
};
//结果:\Hello\Config\UserMyGame.ini
[/Script/Insider.MyClass_ProjectUserConfig]
MyPropertyWithConfig=777
MyPropertyWithGlobalConfig=888
```
## 示例效果:
![Untitled](Untitled.png)
## 在源码中搜索:
```cpp
UCLASS(config = Engine, projectuserconfig, meta = (DisplayName = "Rendering Overrides (Local)"))
class ENGINE_API URendererOverrideSettings : public UDeveloperSettings
{
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View File

@@ -0,0 +1,114 @@
# Deprecated
- **功能描述:** 标明该类已经弃用。
- **引擎模块:** Development
- **元数据类型:** bool
- **作用机制:** 在ClassFlags添加[CLASS_Deprecated](../../../../Flags/EClassFlags/CLASS_Deprecated.md)、[CLASS_NotPlaceable](../../../../Flags/EClassFlags/CLASS_NotPlaceable.md)在Meta添加[DeprecationMessage](../../../../Meta/Development/DeprecationMessage.md)、[DeprecatedProperty](../../../../Meta/Development/DeprecatedProperty/DeprecatedProperty.md)
- **常用程度:★★★**
标明该类已经弃用。
弃用会导致不可被创建不可被序列化保存在继承列表里被过滤掉。此说明符子类会继承下来标明子类也是废弃的。标上Deprecated 的类需要在类名前加上UDEPRECATED_的显眼前缀但是类名不会变Actor加ADEPRECATED_UObject加UDEPRECATED_。ClassFLags里会标上CLASS_Deprecated和CLASS_NotPlaceable。注意还是可以正常NewObject使用的。而SpawnActor会失败报错 failed because class %s is deprecated。EditInline也都会被禁止。
## 示例代码1
```cpp
UCLASS(Blueprintable)
class INSIDER_API UMyClass_Deprecated :public UObject
{
GENERATED_BODY()
};
//改为:
UCLASS(Blueprintable, Deprecated)
class INSIDER_API UDEPRECATED_MyClass_Deprecated :public UObject
{
GENERATED_BODY()
};
```
## 示例效果1
依然可以NewObject。
![Untitled](Untitled.png)
## 示例代码2
但要注意这个是UE的标记。源码里还看见很多UE_DEPRECATED宏的使用则是在VS编译器级别的标记会根据使用引用情况在编译的步骤中生成警告。
```cpp
UCLASS(Blueprintable, BlueprintType)
class INSIDER_API UMyClass_Deprecated_Test :public UObject
{
GENERATED_BODY()
public:
UE_DEPRECATED(5.2, "MyClass_Deprecated has been deprecated, please remove it.")
UDEPRECATED_MyClass_Deprecated* MyProperty_Deprecated;
UE_DEPRECATED(5.2, "MyIntProperty has been deprecated, please remove it.")
UPROPERTY(EditAnywhere, BlueprintReadWrite,meta=(DeprecatedProperty, DeprecationMessage = "MyIntProperty has been deprecated."))
int MyIntProperty;
UE_DEPRECATED(5.2, "MyClass_Deprecated has been deprecated, please remove it.")
void MyFunc(UDEPRECATED_MyClass_Deprecated* obj){}
UFUNCTION(BlueprintCallable, meta = (DeprecatedProperty, DeprecationMessage="MyVoidFunc has been deprecated."))
void MyVoidFunc(){}
};
UCLASS(Blueprintable, BlueprintType)
class INSIDER_API UMyClass_Deprecated_Usage :public UObject
{
GENERATED_BODY()
public:
void MyFunc()
{
UMyClass_Deprecated_Test* obj=NewObject<UMyClass_Deprecated_Test>();
UDEPRECATED_MyClass_Deprecated* obj2 = NewObject<UDEPRECATED_MyClass_Deprecated>();
obj->MyProperty_Deprecated= obj2;
obj->MyProperty_Deprecated->MyFunc();
obj->MyIntProperty++;
obj->MyFunc(obj2);
obj->MyVoidFunc();
}
};
编译警告:
warning C4996: 'UMyClass_Deprecated_Test::MyProperty_Deprecated': MyClass_Deprecated has been deprecated, please remove it. Please update your code to the new API before upgrading to the next release, otherwise your project will no longer compile.
warning C4996: 'UMyClass_Deprecated_Test::MyProperty_Deprecated': MyClass_Deprecated has been deprecated, please remove it. Please update your code to the new API before upgrading to the next release, otherwise your project will no longer compile.
warning C4996: 'UMyClass_Deprecated_Test::MyIntProperty': MyIntProperty has been deprecated, please remove it. Please update your code to the new API before upgrading to the next release, otherwise your project will no longer compile.
warning C4996: 'UMyClass_Deprecated_Test::MyFunc': MyClass_Deprecated has been deprecated, please remove it. Please update your code to the new API before upgrading to the next release, otherwise your project will no longer compile.
注意如果没有UE_DEPRECATED标记,则不会生成编译警告。
UPROPERTY(EditAnywhere, BlueprintReadWrite) int MyInt2Property_DEPRECATED;
会触发:
warning : Member variable declaration: Deprecated property 'MyInt2Property_DEPRECATED' should not be marked as blueprint visible without having a BlueprintGetter
warning : Member variable declaration: Deprecated property 'MyInt2Property_DEPRECATED' should not be marked as blueprint writable without having a BlueprintSetter
warning : Member variable declaration: Deprecated property 'MyInt2Property_DEPRECATED' should not be marked as visible or editable
因此只能改成:
UPROPERTY() int MyInt2Property_DEPRECATED;
```
## 示例效果2
属性和函数上加上Deprecated标记后会在BP编译的时候生成警告。注意函数是先有一个正常的函数在BP里连接完成之后再在C++里标记DeprecatedFunction才会生成警告否则已经Deprecated的函数是无法再在BP里调用的。
![Untitled](Untitled%201.png)
## 原理:
源码中有众多CLASS_Deprecated的判断比如SpawnActor
```cpp
AActor* UWorld::SpawnActor( UClass* Class, FTransform const* UserTransformPtr, const FActorSpawnParameters& SpawnParameters )
{
if( Class->HasAnyClassFlags(CLASS_Deprecated) )
{
UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because class %s is deprecated"), *Class->GetName() );
return NULL;
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,31 @@
# EarlyAccessPreview
- **功能描述:** 标明该类是早期预览版,比试验版要更完善一些,但还是没到产品级。
- **引擎模块:** Development
- **元数据类型:** bool
- **作用机制:** 在Meta中添加[DevelopmentStatus](../../../../Meta/Development/DevelopmentStatus.md)将类标记为EarlyAccess
- **常用程度:★★★**
标明该类是早期预览版,比试验版要更完善一些,但还是没到产品级。
这个标记会在类的元数据上加上{ "DevelopmentStatus", "EarlyAccess" }。
## 示例代码:
```cpp
//(BlueprintType = true, DevelopmentStatus = EarlyAccess, IncludePath = Class/Display/MyClass_Deprecated.h, IsBlueprintBase = true, ModuleRelativePath = Class/Display/MyClass_Deprecated.h)
UCLASS(Blueprintable, EarlyAccessPreview)
class INSIDER_API UMyClass_EarlyAccessPreview :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
UFUNCTION(BlueprintCallable)
void MyFunc() {}
};
```
## 示例结果:
![Untitled](Untitled.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,33 @@
# Experimental
- **功能描述:** 标明该类是试验性版本,当前没有文档描述,之后有可能废弃掉。
- **引擎模块:** Development
- **元数据类型:** bool
- **作用机制:** 在Meta中添加[DevelopmentStatus](../../../../Meta/Development/DevelopmentStatus.md)将类标记为Experimental
- **常用程度:★★★**
标明该类是试验性版本,当前没有文档描述,之后有可能废弃掉。
源码里的例子是Paper2D的类。这个标记会在类的元数据上加上{ "DevelopmentStatus", "Experimental" }。
## 示例代码:
```cpp
/*
(BlueprintType = true, DevelopmentStatus = Experimental, IncludePath = Class/Display/MyClass_Deprecated.h, IsBlueprintBase = true, ModuleRelativePath = Class/Display/MyClass_Deprecated.h)
*/
UCLASS(Blueprintable, Experimental)
class INSIDER_API UMyClass_Experimental :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
UFUNCTION(BlueprintCallable)
void MyFunc() {}
};
```
## 示例效果:
![Untitled](Untitled.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,112 @@
# DefaultToInstanced
- **功能描述:** 指定该类的所有实例属性都默认是UPROPERTY(instanced),即都默认创建新的实例,而不是对对象的引用。
- **引擎模块:** Instance
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中添加[CLASS_DefaultToInstanced](../../../../Flags/EClassFlags/CLASS_DefaultToInstanced.md)
- **常用程度:★★★★**
指定该类的所有实例属性都默认是UPROPERTY(instanced),即都默认创建新的实例,而不是对对象的引用。
UPROPERTY(instanced)的含义是造成Property的CPF_InstancedReference即为该属性创建对象实例。
所谓实例指的是为该UObject指针创建一个对象而不是默认的去找到引擎内已有的对象的来引用。
也常常和EditInlineNew配合使用以便在细节面板中可以创建对象实例。
UActorComponent本身就是带有DefaultToInstanced的。
## 示例代码:
```cpp
UCLASS(Blueprintable)
class INSIDER_API UMyClass_NotDefaultToInstanced :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
};
// ClassFlags: CLASS_MatchedSerializers | CLASS_Native | CLASS_RequiredAPI | CLASS_DefaultToInstanced | CLASS_TokenStreamAssembled | CLASS_Intrinsic | CLASS_Constructed
UCLASS(Blueprintable, DefaultToInstanced)
class INSIDER_API UMyClass_DefaultToInstanced :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
};
// ClassFlags: CLASS_MatchedSerializers | CLASS_Native | CLASS_EditInlineNew | CLASS_RequiredAPI | CLASS_DefaultToInstanced | CLASS_TokenStreamAssembled | CLASS_Intrinsic | CLASS_Constructed
UCLASS(Blueprintable, DefaultToInstanced, EditInlineNew)
class INSIDER_API UMyClass_DefaultToInstanced_EditInlineNew :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
};
UCLASS(Blueprintable, EditInlineNew)
class INSIDER_API UMyClass_NotDefaultToInstanced_EditInlineNew :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
};
UCLASS(Blueprintable, BlueprintType)
class INSIDER_API UMyClass_DefaultToInstanced_Test :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "NormalProperty")
UMyClass_NotDefaultToInstanced* MyObject_NotDefaultToInstanced;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "NormalProperty")
UMyClass_DefaultToInstanced* MyObject_DefaultToInstanced;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, Category = "NormalProperty | Instanced")
UMyClass_NotDefaultToInstanced* MyObject_NotDefaultToInstanced_Instanced;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, Category = "NormalProperty | Instanced")
UMyClass_DefaultToInstanced* MyObject_DefaultToInstanced_Instanced;
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "EditInlineNew")
UMyClass_NotDefaultToInstanced_EditInlineNew* MyObject_NotDefaultToInstanced_EditInlineNew;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "EditInlineNew")
UMyClass_DefaultToInstanced_EditInlineNew* MyObject_DefaultToInstanced_EditInlineNew;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, Category = "EditInlineNew | Instanced")
UMyClass_NotDefaultToInstanced_EditInlineNew* MyObject_NotDefaultToInstanced_EditInlineNew_Instanced;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, Category = "EditInlineNew | Instanced")
UMyClass_DefaultToInstanced_EditInlineNew* MyObject_DefaultToInstanced_EditInlineNew_Instanced;
};
```
## 示例效果:
- MyObject_NotDefaultToInstanced和MyObject_NotDefaultToInstanced_EditInlineNew因为属性没有instanced的标记因此打开是一个选择对象引用的列表。
- MyObject_DefaultToInstanced因为类上有DefaultToInstanced因此该属性是Instanced。当然我们也可以手动给属性加上Instanced标记正如MyObject_NotDefaultToInstanced_Instanced和MyObject_DefaultToInstanced_Instanced。出现了创建实例的窗口但是还不能创建在细节面板里直接创建对象。
- MyObject_DefaultToInstanced_EditInlineNewMyObject_NotDefaultToInstanced_EditInlineNew_InstancedMyObject_DefaultToInstanced_EditInlineNew_Instanced这3个都可以直接在细节面板创建对象实例。是因为这个类本身要有EditInlineNew另外这个属性要有Instanced要嘛在该类上设置DefaultToInstanced以此该类的所有属性都自动是Instanced或者在属性上单个设置Instanced
![image](image.png)
## 原理:
```cpp
UObject* FObjectInstancingGraph::InstancePropertyValue(UObject* SubObjectTemplate, UObject* CurrentValue, UObject* Owner, EInstancePropertyValueFlags Flags)
{
if (CurrentValue->GetClass()->HasAnyClassFlags(CLASS_DefaultToInstanced))
{
bCausesInstancing = true; // these are always instanced no matter what
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

View File

@@ -0,0 +1,80 @@
# EditInlineNew
- **功能描述:** 指定该类的对象可以在属性细节面板里直接内联创建要和属性的Instanced配合。
- **引擎模块:** Instance
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中添加[CLASS_EditInlineNew](../../../../Flags/EClassFlags/CLASS_EditInlineNew.md)
- **关联项:** NotEditInlineNew (NotEditInlineNew.md)
- **常用程度:★★★★★**
指定该类的对象可以在属性细节面板里直接内联创建。
如果想在细节面板里直接创建对象属性上也必须先标记Instanced或ShowInnerProperties。
EditInlineNew主要是用在UObject的子类上一般不标EditInlineNew的是用在Actor或资产的引用上。注意EditInlineNew是表明增加从属性细节面板里直接创建对象实例的能力而非限制只能在属性细节面板里创建当然也可以自己手动NewObject再赋值给对象引用属性。
这个跟UPROPERTY上的Instanced能力是独立的。如果UCLASS上不加EditInlineNew但是属性上加上Instanced则在手动NewObject赋值该属性后该属性也会展开内部属性来提供编辑功能。因为Instanced的属性会自动的在property上加上EditInline的meta。
此说明符会传播到所有子类;子类可通过 NotEditInlineNew 说明符覆盖它。
## 示例代码:
```cpp
UCLASS(Blueprintable, EditInlineNew)
class INSIDER_API UMyClass_EditInlineNew :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
};
UCLASS(Blueprintable, NotEditInlineNew)
class INSIDER_API UMyClass_NotEditInlineNew :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
};
UCLASS(Blueprintable, BlueprintType)
class INSIDER_API UMyClass_Edit_Test :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, Category = InstancedProperty)
UMyClass_EditInlineNew* MyEditInlineNew;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, Category = InstancedProperty)
UMyClass_NotEditInlineNew* MyNotEditInlineNew;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = NormalProperty)
UMyClass_EditInlineNew* MyEditInlineNew_NotInstanced;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = NormalProperty)
UMyClass_NotEditInlineNew* MyNotEditInlineNew_NotInstanced;
};
```
## 示例效果:
EditInlineNew支持直接C++或BP子类创建对象实例然后在上面编辑实例。
而NotEditInlineNew的属性则无法找到支持的类来创建对象。
如果属性上没有Instanced则只能尝试去引用找不到对象
![image](image.png)
## 原理:
判断该类是否有CLASS_EditInlineNew来决定是否可内联创建编辑。
```cpp
template <typename TClass, typename TIsChildOfFunction>
bool FPropertyEditorInlineClassFilter::IsClassAllowedHelper(TClass InClass, TIsChildOfFunction IsClassChildOf, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs)
{
const bool bMatchesFlags = InClass->HasAnyClassFlags(CLASS_EditInlineNew) &&
!InClass->HasAnyClassFlags(CLASS_Hidden | CLASS_HideDropDown | CLASS_Deprecated) &&
(bAllowAbstract || !InClass->HasAnyClassFlags(CLASS_Abstract));
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

View File

@@ -0,0 +1,8 @@
# NotEditInlineNew
- **功能描述:** 不能通过EditInline按钮创建
- **引擎模块:** Instance
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中移除[CLASS_EditInlineNew](../../../Flags/EClassFlags/CLASS_EditInlineNew.md)
- **关联项:** EditInlineNew (EditInlineNew.md)
- **常用程度:★**

View File

@@ -0,0 +1,70 @@
# Within
- **功能描述:** 指定对象创建的时候必须依赖于OuterClassName的对象作为Outer。
- **引擎模块:** Instance
- **元数据类型:** string="abc"
- **作用机制:** 保存在UClass* UClass::ClassWithin=XXX的XXX中
- **常用程度:★★★**
指定对象创建的时候必须依赖于OuterClassName的对象作为Outer。
此类的对象无法在OuterClassName对象的实例之外存在。这意味着要创建此类的对象需要提供OuterClassName的一个实例作为其Outer对象。
本类在这种情况一般是用来当做子对象来使用的。
## 示例代码:
```cpp
UCLASS(Within= MyClass_Within_Outer)
class INSIDER_API UMyClass_Within :public UObject
{
GENERATED_BODY()
};
UCLASS()
class INSIDER_API UMyClass_Within_Outer :public UObject
{
GENERATED_BODY()
public:
};
```
## 示例结果:
```cpp
//错误Fatal error: Object MyClass_Within None created in Package instead of MyClass_Within_Outer
UMyClass_Within* obj=NewObject<UMyClass_Within>();
//正确:
UMyClass_Within_Outer* objOuter = NewObject<UMyClass_Within_Outer>();
UMyClass_Within* obj=NewObject<UMyClass_Within>(objOuter);
```
## 原理:
生成的UClass的字段UClass* ClassWithin会保存这个信息然后在创建的时候StaticAllocateObject会测试 check(bCreatingCDO || !InOuter || InOuter->IsA(InClass->ClassWithin))。因此需要先创建Within的对象。
```cpp
bool StaticAllocateObjectErrorTests( const UClass* InClass, UObject* InOuter, FName InName, EObjectFlags InFlags)
{
if ( (InFlags & (RF_ClassDefaultObject|RF_ArchetypeObject)) == 0 )
{
if ( InOuter != NULL && !InOuter->IsA(InClass->ClassWithin) )
{
UE_LOG(LogUObjectGlobals, Fatal, TEXT("%s"), *FString::Printf( TEXT("Object %s %s created in %s instead of %s"), *InClass->GetName(), *InName.ToString(), *InOuter->GetClass()->GetName(), *InClass->ClassWithin->GetName()) );
return true;
}
}
}
```
在源码里可以搜索到很多Within的用法
UCLASS(Within=Engine, config=Engine, transient)
class ENGINE_API ULocalPlayer
UCLASS(Abstract, DefaultToInstanced, Within=UserWidget)
class UMG_API UUserWidgetExtension : public UObject
{

View File

@@ -0,0 +1,69 @@
# ConversionRoot
- **功能描述:** 在场景编辑器里允许Actor在自身以及子类之间做转换
- **引擎模块:** Scene
- **元数据类型:** bool
- **作用机制:** 在Meta中增加[IsConversionRoot](../../../../Meta/Blueprint/IsConversionRoot.md)
- **常用程度:★**
一般是用在Actor上在Actor转换的时候用来限制转换的级别。比如ASkeletalMeshActorAStaticMeshActor等。
常常ComponentWrapperClass一起出现。
根据代码来说meta中的IsConversionRoot会限制只传达到这一层不继续往根上查找。
只有配有ConversionRoot的Actor才会允许Convert Actor否则是禁用的。
## 示例代码:
```cpp
//(BlueprintType = true, IncludePath = Class/Trait/MyClass_ConversionRoot.h, IsBlueprintBase = true, IsConversionRoot = true, ModuleRelativePath = Class/Trait/MyClass_ConversionRoot.h)
UCLASS(Blueprintable,BlueprintType, ConversionRoot)
class INSIDER_API AMyActor_ConversionRoot :public AActor
{
GENERATED_BODY()
};
```
## 示例效果:
在蓝图中创建其子类BP_ConversionRoot_Child1和BP_ConversionRoot_Child2。然后把BP_ConversionRoot_Child1拖放进场景里创建个Actor也创建个普通的蓝图Actor作为对比。
![Untitled](Untitled.png)
在关卡中选择Child1会允许ConvertActor在ConverstionRoot的自身以及所有子类之间做转换。
![Untitled](Untitled%201.png)
如果是普通的Actor因为没有定义ConversionRoot则不能做转换。
![Untitled](Untitled%202.png)
## 原理:
在关卡中的Actor选择关卡中选择一个Actor然后DetailsPanel里会显示ConverActor属性栏可以选择另外一个Actor来进行改变。
TSharedRef<SWidget> FActorDetails::MakeConvertMenu( const FSelectedActorInfo& SelectedActorInfo )
这个函数就是用来创建Select Type的Combo Button的菜单的。内部会调用CreateClassPickerConvertActorFilter
```cpp
UClass* FActorDetails::GetConversionRoot( UClass* InCurrentClass ) const
{
UClass* ParentClass = InCurrentClass;
while(ParentClass)
{
if( ParentClass->GetBoolMetaData(FName(TEXT("IsConversionRoot"))) )
{
break;
}
ParentClass = ParentClass->GetSuperClass();
}
return ParentClass;
}
void FActorDetails::CreateClassPickerConvertActorFilter(const TWeakObjectPtr<AActor> ConvertActor, class FClassViewerInitializationOptions* ClassPickerOptions)
Filter->AllowedChildOfRelationship.Add(RootConversionClass);//限定这个基类以下的其他子类
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

View File

@@ -0,0 +1,65 @@
# NotPlaceable
- **功能描述:** 标明该Actor不可被放置在关卡里
- **引擎模块:** Behavior
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中添加[CLASS_NotPlaceable](../../../../Flags/EClassFlags/CLASS_NotPlaceable.md)
- **关联项:** Placeable (Placeable.md)
- **常用程度:★★★**
标明该Actor不可被放置在关卡里没法拖放到场景里。使继承自基类的Placeable说明符无效。会在ClassFlagss里标记上CLASS_NotPlaceable这个标记是可以继承的意味着其所有的子类默认都不可放置。例如AWorldSettings其实就是一个notplaceable的Actor。
但是注意该类依然可以通过SpawnActor动态生成到关卡中。
NotPlaceable的类是不出现在PlaceMode的类选择里去的。
## 示例代码:
```cpp
UCLASS(Blueprintable,BlueprintType, NotPlaceable)
class INSIDER_API AMyActor_NotPlaceable :public AActor
{
GENERATED_BODY()
};
```
## 示例效果:
拖动到场景里会发现不能创建Actor。
![Untitled](Untitled.png)
## 原理:
如果直接是C++类AMyActor_NotPlaceable 是可以直接从ContentBrowser拖到场景里去的。看源码可知只有BP继承下来的子类才有受到这个限制。
```cpp
TArray<AActor*> FLevelEditorViewportClient::TryPlacingActorFromObject( ULevel* InLevel, UObject* ObjToUse, bool bSelectActors, EObjectFlags ObjectFlags, UActorFactory* FactoryToUse, const FName Name, const FViewportCursorLocation* Cursor )
{
bool bPlace = true;
if (ObjectClass->IsChildOf(UBlueprint::StaticClass()))
{
UBlueprint* BlueprintObj = StaticCast<UBlueprint*>(ObjToUse);
bPlace = BlueprintObj->GeneratedClass != NULL;
if(bPlace)
{
check(BlueprintObj->ParentClass == BlueprintObj->GeneratedClass->GetSuperClass());
if (BlueprintObj->GeneratedClass->HasAnyClassFlags(CLASS_NotPlaceable | CLASS_Abstract))
{
bPlace = false;
}
}
}
if (bPlace)
{
PlacedActor = FActorFactoryAssetProxy::AddActorForAsset( ObjToUse, bSelectActors, ObjectFlags, FactoryToUse, Name );
if ( PlacedActor != NULL )
{
PlacedActors.Add(PlacedActor);
PlacedActor->PostEditMove(true);
}
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

View File

@@ -0,0 +1,33 @@
# Placeable
- **功能描述:** 标明该Actor可以放置在关卡里。
- **引擎模块:** Scene
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中移除[CLASS_NotPlaceable](../../../../Flags/EClassFlags/CLASS_NotPlaceable.md)
- **关联项:** [NotPlaceable](../NotPlaceable/NotPlaceable.md)
- **常用程度:★★★**
标明该Actor可以放置在关卡里。
默认情况下是placeable的因此源码里目前没有用到Placeable的地方。
子类可使用NotPlaceable说明符覆盖此标志正如AInfo之类的上面自己设置NotPlaceable。
指示可在编辑器中创建此类而且可将此类放置到关卡、UI场景或蓝图取决于类类型中。此标志会传播到所有子类
placeable没法清除父类的notplaceable标记。
## 示例代码:
```cpp
UCLASS(Blueprintable, BlueprintType,placeable)
class INSIDER_API AMyActor_Placeable :public AMyActor_NotPlaceable
{
GENERATED_BODY()
};
error : The 'placeable' specifier cannot override a 'nonplaceable' base class. Classes are assumed to be placeable by default. Consider whether using the 'abstract' specifier on the base class would work.
```
## 示例效果:
![Untitled](Untitled.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View File

@@ -0,0 +1,154 @@
# MatchedSerializers
- **功能描述:** 指定类支持文本结构序列化
- **引擎模块:** Serialization
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中增加[CLASS_MatchedSerializers](../../../../Flags/EClassFlags/CLASS_MatchedSerializers.md)在Meta中添加[MatchedSerializers](../../../../Meta/Serialization/MatchedSerializers.md)
- **常用程度:** 0
该标识符只允许在NoExportTypes.h中使用属于是引擎自用的内部标识符。
基本上大部分的类都拥有该标记除了自身不导出的类一般包括NoExportTypes.h定义的除非手动加上MatchedSerializers比如UObject或者靠DECLARE_CLASS_INTRINSIC直接在源码里定义的元数据。
因此实际上大部分的类都拥有该标记。因为在UHT中只要不是NoExport的就会自动的加上这个标记。
```cpp
// Force the MatchedSerializers on for anything being exported
if (!ClassExportFlags.HasAnyFlags(UhtClassExportFlags.NoExport))
{
ClassFlags |= EClassFlags.MatchedSerializers;
}
```
## 结构化序列化器:
如果一个类支持文本格式则StructuredArchive的结构的意思是会把类里的字段树形展开来序列化展示出来从而方便人类理解。而如果不支持文本格式则会把所有的字段值压进一个二进制buffer里Data字段这也是runtime时候用的方式。
测试代码:
```cpp
UCLASS(Blueprintable, BlueprintType,editinlinenew)
class INSIDER_API UMyClass_MatchedSerializersSub :public UObject
{
public:
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyInt_Default = 123;
};
UCLASS(Blueprintable, BlueprintType)
class INSIDER_API UMyClass_MatchedSerializersTestAsset:public UDataAsset
{
public:
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyInt_Default = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite,Instanced)
UMyClass_MatchedSerializersSub* SubObject;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UStruct* MyStructType;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UClass* MyClassType;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UEnum* MyEnumType;
};
void UMyClass_MatchedSerializers_Test::ApplyClassFlag()
{
UMyClass_MatchedSerializersTestAsset::StaticClass()->ClassFlags |= CLASS_MatchedSerializers;
UMyClass_MatchedSerializersSub::StaticClass()->ClassFlags |= CLASS_MatchedSerializers;
}
void UMyClass_MatchedSerializers_Test::RemoveClassFlag()
{
UMyClass_MatchedSerializersTestAsset::StaticClass()->ClassFlags &= ~CLASS_MatchedSerializers;
UMyClass_MatchedSerializersSub::StaticClass()->ClassFlags &= ~CLASS_MatchedSerializers;
}
```
在编辑器中创建测试数据Asset
![Untitled](Untitled.png)
然后在Editor选项里打开TextAssetFormatSupport(UEditorExperimentalSettings::bTextAssetFormatSupport)
![Untitled](Untitled%201.png)
然后在资产上就出现3个菜单支持把资产导出为文本。
![Untitled](Untitled%202.png)
ExportToTextFormat会在蓝图资产的同目录生成一个.utxt的文件格式为json。通过动态的增删CLASS_MatchedSerializers这个标记来对比这个标记产生的差异
![Untitled](Untitled%203.png)
可以发现序列化出来的内容有明显的差异不带有CLASS_MatchedSerializers标记的产生的右侧结果把所有的字段值压进一个二进制buffer里Data字段
## 内部机制原理:
CLASS_MatchedSerializers这个标记在UClass::IsSafeToSerializeToStructuredArchives中被使用标明采用结构序列化器。是否支持文本导入导出只在编辑器情况下使用。
在发生作用的只有SavePackage2.cpp和LinkerLoad.cpp因此是只发生在保存UPackage的时候作为子类对象。所以不能用简单的内存里Archive序列化来进行测试。
```cpp
bool UClass::IsSafeToSerializeToStructuredArchives(UClass* InClass)
{
while (InClass)
{
if (!InClass->HasAnyClassFlags(CLASS_MatchedSerializers))
{
return false;
}
InClass = InClass->GetSuperClass();
}
return true;
}
//LinkerLoad.cpp
bool bClassSupportsTextFormat = UClass::IsSafeToSerializeToStructuredArchives(Object->GetClass());
if (IsTextFormat())//如果Ar序列化是文本格式
{
FStructuredArchiveSlot ExportSlot = GetExportSlot(Export.ThisIndex);
if (bClassSupportsTextFormat) //如果类本身支持文本格式
{
Object->GetClass()->SerializeDefaultObject(Object, ExportSlot);
}
else
{
FStructuredArchiveChildReader ChildReader(ExportSlot);
FArchiveUObjectFromStructuredArchive Adapter(ChildReader.GetRoot());
Object->GetClass()->SerializeDefaultObject(Object, Adapter.GetArchive());
}
}
//SavePackage2.cpp
#if WITH_EDITOR
bool bSupportsText = UClass::IsSafeToSerializeToStructuredArchives(Export.Object->GetClass());
#else
bool bSupportsText = false;
#endif
if (bSupportsText)
{
Export.Object->GetClass()->SerializeDefaultObject(Export.Object, ExportSlot);
}
else
{
FArchiveUObjectFromStructuredArchive Adapter(ExportSlot);
Export.Object->GetClass()->SerializeDefaultObject(Export.Object, Adapter.GetArchive());
Adapter.Close();
}
```
文本格式只在编辑器环境下生效。
可以从源码看到如果类本身支持文本格式序列化则在Ar是文本格式的时候直接可以序列化采用默认的SerializeTaggedProperties。否则得采用FArchiveUObjectFromStructuredArchive 来适配一下把对象指针转换为object path+ int32 Index的组合。
在引擎中打印出所有包含或不包含CLASS_MatchedSerializers的类发现UStruct继承链下面的类开始包含但是UClass却不包含而上面UField的类则不包含比如各种Property。类列表见Doc下txt文件。

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -0,0 +1,8 @@
# NonTransient
- **功能描述:** 使继承自基类的Transient说明符无效。
- **引擎模块:** Serialization
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中移除[CLASS_Transient](../../../Flags/EClassFlags/CLASS_Transient.md)
- **关联项:** [Transient](Transient/Transient.md)
- **常用程度:** ★★★

View File

@@ -0,0 +1,275 @@
# Optional
- **功能描述:** 标记该类的对象是可选的在Cooking的时候可以选择是否要忽略保存它们。
- **引擎模块:** Serialization
- **作用机制:** 在ClassFlags中添加[CLASS_Optional](../../../../Flags/EClassFlags/CLASS_Optional.md)
- **常用程度:** ★
标记该类的对象是可选的在Cooking的时候可以选择是否要忽略保存它们。
- 一般为EditorOnly的数据如MetaData等在游戏运行时不存在保存在其他的特定文件中。
- Optional的对象一般也包在WITH_EDITORONLY_DATA宏里只在编辑器下使用。
- 引擎在cook的时候会根据EDITOROPTIONAL的配置来加上SAVE_Optional从而选择是否一起序列化该对象值比如metadata。
## 示例代码:
```cpp
//ClassFlags: CLASS_Optional | CLASS_MatchedSerializers | CLASS_Native | CLASS_RequiredAPI | CLASS_TokenStreamAssembled | CLASS_Intrinsic | CLASS_Constructed
UCLASS(Optional)
class INSIDER_API UMyClass_Optional :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty = 123;
};
UCLASS()
class INSIDER_API UMyClass_NotOptional :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty = 123;
};
UCLASS()
class INSIDER_API UMyClass_Optional_Test :public UObject
{
GENERATED_BODY()
public:
#if WITH_EDITORONLY_DATA
UPROPERTY()
UMyClass_Optional* MyOptionalObject;
#endif // WITH_EDITORONLY_DATA
public:
UPROPERTY()
UMyClass_NotOptional* MyNotOptionalObject;
public:
static void CreatePackageAndSave();
static void LoadPackageAndTest();
};
void UMyClass_Optional_Test::CreatePackageAndSave()
{
FString packageName = TEXT("/Game/MyOptionTestPackage");
FString assetPath = FPackageName::LongPackageNameToFilename(packageName, FPackageName::GetAssetPackageExtension());
IFileManager::Get().Delete(*assetPath, false, true);
UPackage* package = CreatePackage(*packageName);
FSavePackageArgs saveArgs{};
//saveArgs.TopLevelFlags = EObjectFlags::RF_Public | EObjectFlags::RF_Standalone;
saveArgs.Error = GError;
saveArgs.SaveFlags=SAVE_NoError;
//SAVE_Optional = 0x00008000, ///< Indicate that we to save optional exports. This flag is only valid while cooking. Optional exports are filtered if not specified during cooking.
UMyClass_Optional_Test* testObject = NewObject<UMyClass_Optional_Test>(package, TEXT("testObject"));
#if WITH_EDITORONLY_DATA
testObject->MyOptionalObject = NewObject<UMyClass_Optional>(testObject, TEXT("MyOptionalObject"));
testObject->MyOptionalObject->MyProperty = 456;
#endif
testObject->MyNotOptionalObject = NewObject<UMyClass_NotOptional>(testObject, TEXT("MyNotOptionalObject"));
testObject->MyNotOptionalObject->MyProperty = 456;
FString str = UInsiderSubsystem::Get().PrintObject(package, EInsiderPrintFlags::All);
FString str2 = UInsiderSubsystem::Get().PrintObject(testObject, EInsiderPrintFlags::All);
FString str3 = UInsiderSubsystem::Get().PrintObject(UMyClass_Optional::StaticClass(), EInsiderPrintFlags::All);
FString str4 = UInsiderSubsystem::Get().PrintObject(UMyClass_NotOptional::StaticClass(), EInsiderPrintFlags::All);
bool result = UPackage::SavePackage(package, testObject, *assetPath, saveArgs);
}
void UMyClass_Optional_Test::LoadPackageAndTest()
{
FString packageName = TEXT("/Game/MyOptionTestPackage");
FString assetPath = FPackageName::LongPackageNameToFilename(packageName, FPackageName::GetAssetPackageExtension());
UPackage* package = LoadPackage(nullptr, *assetPath, LOAD_None);
package->FullyLoad();
UMyClass_Optional_Test* newTestObject = LoadObject<UMyClass_Optional_Test>(package, TEXT("testObject"), *assetPath);
//UMyClass_Transient_Test* newTestObject = nullptr;
/*const TArray<FObjectExport>& exportMap = package->GetLinker()->ExportMap;
for (const auto& objExport : exportMap)
{
if (objExport.ObjectName == TEXT("testObject"))
{
newTestObject = Cast<UMyClass_Transient_Test>(objExport.Object);
break;
}
}*/
FString str = UInsiderSubsystem::Get().PrintObject(package, EInsiderPrintFlags::All);
}
```
## 示例效果:
正常的SavePackage发现是没有作用的依然会序列化保存。特殊的保存方式在Cook阶段本例就没有专门测试了。
![Untitled](Untitled.png)
在源码里搜索Optional可以看到一般是EditorOnlyData和CookedMetaData类在使用。
```cpp
UCLASS(Optional, Within=Enum)
class ENGINE_API UEnumCookedMetaData : public UObject
UCLASS(Optional, Within=ScriptStruct)
class ENGINE_API UStructCookedMetaData : public UObject
UCLASS(Optional, Within=Class)
class ENGINE_API UClassCookedMetaData : public UObject
UMaterialInterfaceEditorOnlyData* UMaterialInterface::CreateEditorOnlyData()
{
const UClass* EditorOnlyClass = GetEditorOnlyDataClass();
check(EditorOnlyClass);
check(EditorOnlyClass->HasAllClassFlags(CLASS_Optional));
const FString EditorOnlyName = MaterialInterface::GetEditorOnlyDataName(*GetName());
const EObjectFlags EditorOnlyFlags = GetMaskedFlags(RF_PropagateToSubObjects);
return NewObject<UMaterialInterfaceEditorOnlyData>(this, EditorOnlyClass, *EditorOnlyName, EditorOnlyFlags);
}
```
引擎里也有一些验证:
```cpp
UnrealTypeDefinitionInfo.cpp:
// Validate if we are using editor only data in a class or struct definition
if (HasAnyClassFlags(CLASS_Optional))
{
for (TSharedRef<FUnrealPropertyDefinitionInfo> PropertyDef : GetProperties())
{
if (PropertyDef->GetPropertyBase().IsEditorOnlyProperty())
{
PropertyDef->LogError(TEXT("Cannot specify editor only property inside an optional class."));
}
else if (PropertyDef->GetPropertyBase().ContainsEditorOnlyProperties())
{
PropertyDef->LogError(TEXT("Do not specify struct property containing editor only properties inside an optional class."));
}
}
}
```
通过源码发现:
//SAVE_Optional = 0x00008000, ///< Indicate that we to save optional exports. This flag is only valid while cooking. Optional exports are filtered if not specified during cooking.
这个SAVE_Optional 作用于UUserDefinedEnumUUserDefinedStructUBlueprintGeneratedClass的MetaData对象上
```cpp
void UUserDefinedStruct::PreSaveRoot(FObjectPreSaveRootContext ObjectSaveContext)
{
Super::PreSaveRoot(ObjectSaveContext);
if (ObjectSaveContext.IsCooking() && (ObjectSaveContext.GetSaveFlags() & SAVE_Optional))
{
//这个对象是以this为Outer的标记RF_Standalone | RF_Public会造成该子对象被序列化下来
UStructCookedMetaData* CookedMetaData = NewCookedMetaData();
CookedMetaData->CacheMetaData(this);
if (!CookedMetaData->HasMetaData())
{
PurgeCookedMetaData();//清理掉这个CookedMetaData对象
}
}
else
{
PurgeCookedMetaData();
}
}
```
另外在cook的时候如果指定
bCookEditorOptional = Switches.Contains(TEXT("EDITOROPTIONAL")); // Produce the optional editor package data alongside the cooked data.
则会加上CookEditorOptional 的标识
CookFlags |= bCookEditorOptional ? ECookInitializationFlags::CookEditorOptional : ECookInitializationFlags::None;
再之后则会传达SAVE_Optional 给Package的SaveFlags
SaveFlags |= COTFS.IsCookFlagSet(ECookInitializationFlags::CookEditorOptional) ? SAVE_Optional : SAVE_None;
从而在包括Package的时候IsSaveOptional()的判断会造成是否创建Optional的Realm
```cpp
TArray<ESaveRealm> FSaveContext::GetHarvestedRealmsToSave()
{
TArray<ESaveRealm> HarvestedContextsToSave;
if (IsCooking())
{
HarvestedContextsToSave.Add(ESaveRealm::Game);
if (IsSaveOptional())
{
HarvestedContextsToSave.Add(ESaveRealm::Optional);
}
}
else
{
HarvestedContextsToSave.Add(ESaveRealm::Editor);
}
return HarvestedContextsToSave;
}
```
还有如果发现Object有CLASS_Optional则不把它当做Export(子对象)而是当做Import(引用的对象)Optional对象有可能放在外部独立的文件中
```cpp
ESavePackageResult HarvestPackage(FSaveContext& SaveContext)
{
// If we have a valid optional context and we are saving it,
// transform any harvested non optional export into imports
// Mark other optional import package as well
if (!SaveContext.IsSaveAutoOptional() &&
SaveContext.IsSaveOptional() &&
SaveContext.IsCooking() &&
SaveContext.GetHarvestedRealm(ESaveRealm::Optional).GetExports().Num() &&
SaveContext.GetHarvestedRealm(ESaveRealm::Game).GetExports().Num())
{
bool bHasNonOptionalSelfReference = false;
FHarvestedRealm& OptionalContext = SaveContext.GetHarvestedRealm(ESaveRealm::Optional);
for (auto It = OptionalContext.GetExports().CreateIterator(); It; ++It)
{
if (!It->Obj->GetClass()->HasAnyClassFlags(CLASS_Optional))
{
// Make sure the export is found in the game context as well
if (FTaggedExport* GameExport = SaveContext.GetHarvestedRealm(ESaveRealm::Game).GetExports().Find(It->Obj))
{
// Flag the export in the game context to generate it's public hash
GameExport->bGeneratePublicHash = true;
// Transform the export as an import
OptionalContext.AddImport(It->Obj);
// Flag the package itself to be an import
bHasNonOptionalSelfReference = true;
}
// if not found in the game context and the reference directly came from an optional object, record an illegal reference
else if (It->bFromOptionalReference)
{
SaveContext.RecordIllegalReference(nullptr, It->Obj, EIllegalRefReason::ReferenceFromOptionalToMissingGameExport);
}
It.RemoveCurrent();
}
}
// Also add the current package itself as an import if we are referencing any non optional export
if (bHasNonOptionalSelfReference)
{
OptionalContext.AddImport(SaveContext.GetPackage());
}
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -0,0 +1,184 @@
# Transient
- **功能描述:** 指定该类的所有对象都略过序列化。
- **引擎模块:** Serialization
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中添加[CLASS_Transient](../../../../Flags/EClassFlags/CLASS_Transient.md)
- **关联项:** [NonTransient](../NonTransient.md)
- **常用程度:** ★★★
指定该类的所有对象都略过序列化。
- 从不将属于此类的对象保存到磁盘。此说明符会传播到子类但是可由NonTransient说明符覆盖。可以在子类被改写.
- 会造成相应Object的RF_Transient标记。
- 注意UPROPERTY(Transient)只是指定这一个特定的属性不序列化。而UCLASS(Transient)是作用于该类的所有对象。
## 示例代码:
```cpp
UCLASS(Blueprintable, Transient)
class INSIDER_API UMyClass_Transient :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty = 123;
};
UCLASS(Blueprintable, NonTransient)
class INSIDER_API UMyClass_NonTransient :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty = 123;
};
UCLASS(Blueprintable, BlueprintType)
class INSIDER_API UMyClass_Transient_Test :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UMyClass_Transient* MyTransientObject;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UMyClass_NonTransient* MyNonTransientObject;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyInt_Normal=123;
UPROPERTY(EditAnywhere, BlueprintReadWrite,Transient)
int32 MyInt_Transient =123;
};
```
序列化对象指针关键是采用FObjectProperty里的SerializeItem方法Slot << ObjectValue;
不能采用Actor的Class Default来测试因为
Transient properties are serialized for (Blueprint) Class Default Objects but should not be serialized for any 'normal' instances of classes.
```cpp
UCLASS(Blueprintable, BlueprintType)
class INSIDER_API AMyActor_Transient_Test :public AActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UMyClass_Transient* MyTransientObject;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UMyClass_NonTransient* MyNonTransientObject;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyInt_Normal=123;
UPROPERTY(EditAnywhere, BlueprintReadWrite,Transient)
int32 MyInt_Transient =123;
};
```
也不能用FObjectAndNameAsStringProxyArchive测试因为FObjectAndNameAsStringProxyArchive内部在序列化对象的时候会先查找名字
```cpp
FArchive& FObjectAndNameAsStringProxyArchive::operator<<(UObject*& Obj)
{
if (IsLoading())
{
// load the path name to the object
FString LoadedString;
InnerArchive << LoadedString;
// look up the object by fully qualified pathname
Obj = FindObject<UObject>(nullptr, *LoadedString, false);
// If we couldn't find it, and we want to load it, do that
if(!Obj && bLoadIfFindFails)
{
Obj = LoadObject<UObject>(nullptr, *LoadedString);
}
}
else
{
// save out the fully qualified object name
FString SavedString(Obj->GetPathName());
InnerArchive << SavedString;
}
return *this;
}
```
因此采用Package的测试
```cpp
FString packageName = TEXT("/Game/MyTestPackage");
FString assetPath = FPackageName::LongPackageNameToFilename(packageName, FPackageName::GetAssetPackageExtension());
UPackage* package = CreatePackage(*packageName);
FSavePackageArgs saveArgs{};
saveArgs.Error = GError;
//ObjectFlags: RF_NoFlags
UMyClass_Transient_Test* testObject = NewObject<UMyClass_Transient_Test>(package, TEXT("testObject"));
//ObjectFlags: RF_Transient
testObject->MyTransientObject = NewObject<UMyClass_Transient>(testObject, TEXT("MyTransientObject"));
//ObjectFlags: RF_NoFlags
testObject->MyNonTransientObject = NewObject<UMyClass_NonTransient>(testObject, TEXT("MyNonTransientObject"));
testObject->MyTransientObject->MyProperty = 456;
testObject->MyNonTransientObject->MyProperty = 456;
testObject->MyInt_Normal = 456;
testObject->MyInt_Transient = 456;
bool result = UPackage::SavePackage(package, testObject, *assetPath, saveArgs);
```
在保存完成之后重新加载Package:
```cpp
FString packageName = TEXT("/Game/MyTestPackage");
FString assetPath = FPackageName::LongPackageNameToFilename(packageName, FPackageName::GetAssetPackageExtension());
UPackage* package = LoadPackage(nullptr, *assetPath, LOAD_None);
package->FullyLoad();
UMyClass_Transient_Test* newTestObject=LoadObject<UMyClass_Transient_Test>(package, TEXT("testObject"),*assetPath);
```
## 示例效果:
可以看到MyTransientObject 并没有被序列化到磁盘上因此不会加载出来
![Untitled](Untitled.png)
## 原理:
在SavePackage的时候RF_Transient会导致这个对象不会被HarvestExport不会被放进SaveContext里
```cpp
void FPackageHarvester::TryHarvestExport(UObject* InObject)
{
// Those should have been already validated
check(InObject && InObject->IsInPackage(SaveContext.GetPackage()));
// Get the realm in which we should harvest this export
EIllegalRefReason Reason = EIllegalRefReason::None;
ESaveRealm HarvestContext = GetObjectHarvestingRealm(InObject, Reason);
if (!SaveContext.GetHarvestedRealm(HarvestContext).IsExport(InObject))
{
SaveContext.MarkUnsaveable(InObject);
bool bExcluded = false;
if (!InObject->HasAnyFlags(RF_Transient))
{
bExcluded = ConditionallyExcludeObjectForTarget(SaveContext, InObject, HarvestContext);
}
if (!InObject->HasAnyFlags(RF_Transient) && !bExcluded)
{
// It passed filtering so mark as export
HarvestExport(InObject, HarvestContext);
}
// If we have a illegal ref reason, record it
if (Reason != EIllegalRefReason::None)
{
SaveContext.RecordIllegalReference(CurrentExportDependencies.CurrentExport, InObject, Reason);
}
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,81 @@
# HideDropDown
- **功能描述:** 在类选择器中隐藏此类
- **引擎模块:** TypePicker
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中添加[CLASS_HideDropDown](../../../../Flags/EClassFlags/CLASS_HideDropDown.md)
- **常用程度:★★**
在类选择器中隐藏此类通常是TSubClassOf触发或者Class变量触发的类选择窗口。这个时候这个标识符可以阻止其出现。在源码里的使用通常是一些旧的废弃的类或者Test类Abstract类和基类。
## 示例代码:
```cpp
UCLASS(Blueprintable)
class INSIDER_API UMyClass_HideDropDownBase :public UObject
{
GENERATED_BODY()
public:
};
UCLASS(Blueprintable, hidedropdown)
class INSIDER_API UMyClass_HideDropDown :public UMyClass_HideDropDownBase
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
};
UCLASS(Blueprintable, hidedropdown)
class INSIDER_API UMyClass_NoHideDropDown :public UMyClass_HideDropDownBase
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
};
UCLASS(Blueprintable)
class INSIDER_API UMyClass_HideDropDown_Test :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<UMyClass_HideDropDownBase> DropDownClass;
};
```
## 示例结果:
![Untitled](Untitled.png)
## 原理:
HideDropDown会造成CLASS_HideDropDown标记从而在类型UI定制化的列表里把该类剔除出去。
```cpp
template <typename TClass>
bool FPropertyEditorClassFilter::IsClassAllowedHelper(TClass InClass)
{
bool bMatchesFlags = !InClass->HasAnyClassFlags(CLASS_Hidden | CLASS_HideDropDown | CLASS_Deprecated) &&
(bAllowAbstract || !InClass->HasAnyClassFlags(CLASS_Abstract));
if (bMatchesFlags && InClass->IsChildOf(ClassPropertyMetaClass)
&& (!InterfaceThatMustBeImplemented || InClass->ImplementsInterface(InterfaceThatMustBeImplemented)))
{
auto PredicateFn = [InClass](const UClass* Class)
{
return InClass->IsChildOf(Class);
};
if (DisallowedClassFilters.FindByPredicate(PredicateFn) == nullptr &&
(AllowedClassFilters.Num() == 0 || AllowedClassFilters.FindByPredicate(PredicateFn) != nullptr))
{
return true;
}
}
return false;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -0,0 +1,14 @@
# CustomConstructor
- **功能描述:** 阻止构造函数声明自动生成。
- **引擎模块:** UHT
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中添加[CLASS_CustomConstructor](../../../Flags/EClassFlags/CLASS_CustomConstructor.md)
UHT不会生成 NO_API UMyClass_ModuleAPI(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());的默认构造函数。但是这个一般都是配合GENERATED_UCLASS_BODY使用的因为GENERATED_BODY会自动生成默认构造函数。一般在自己需要自定义这个函数的时候使用。但其实用GENERATED_BODY也行
当前已经弃用:
```cpp
CLASS_CustomConstructor UE_DEPRECATED(5.1, "CLASS_CustomConstructor should no longer be used. It is no longer being set by engine code.") = 0x00008000u,
```

View File

@@ -0,0 +1,56 @@
# CustomFieldNotify
- **功能描述:** 阻止UHT为该类生成FieldNotify的相关代码。
- **引擎模块:** UHT
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中增加[HasCustomFieldNotify](../../../Flags/EClassFlags/HasCustomFieldNotify.md)
- **常用程度:** 0
阻止UHT为该类生成FieldNotify的相关代码。
在源码里只在UWidget上使用例如该类里面的bIsEnabled是FieldNotify正常来说UHT要为其生成代码。但如果该类想自己手动书写这些UHT代码则可以加上CustomFieldNotify来阻止UHT生成。UWidget的cpp里因为要用别的方式UE_FIELD_NOTIFICATION_IMPLEMENT_CLASS_DESCRIPTOR因此要拒绝UHT生成。
如果自己的类也要自己UE_FIELD_NOTIFICATION_IMPLEMENT_CLASS_DESCRIPTOR则可以用上CustomFieldNotify。
## 源码例子:
```cpp
//E:\P4V\Engine\Source\Runtime\UMG\Public\FieldNotification\FieldNotificationDeclaration.h
UCLASS(Abstract, BlueprintType, Blueprintable, CustomFieldNotify)
class UMG_API UWidget : public UVisual, public INotifyFieldValueChanged
{
GENERATED_UCLASS_BODY()
public:
UE_FIELD_NOTIFICATION_DECLARE_CLASS_DESCRIPTOR_BASE_BEGIN(UMG_API)
UE_FIELD_NOTIFICATION_DECLARE_FIELD(ToolTipText)
UE_FIELD_NOTIFICATION_DECLARE_FIELD(Visibility)
UE_FIELD_NOTIFICATION_DECLARE_FIELD(bIsEnabled)
UE_FIELD_NOTIFICATION_DECLARE_ENUM_FIELD_BEGIN(ToolTipText)
UE_FIELD_NOTIFICATION_DECLARE_ENUM_FIELD(Visibility)
UE_FIELD_NOTIFICATION_DECLARE_ENUM_FIELD(bIsEnabled)
UE_FIELD_NOTIFICATION_DECLARE_ENUM_FIELD_END()
UE_FIELD_NOTIFICATION_DECLARE_CLASS_DESCRIPTOR_BASE_END();
UPROPERTY(EditAnywhere, BlueprintReadWrite, FieldNotify, Getter="GetIsEnabled", Setter="SetIsEnabled", BlueprintGetter="GetIsEnabled", BlueprintSetter="SetIsEnabled", Category="Behavior")
uint8 bIsEnabled:1;
//cpp
UE_FIELD_NOTIFICATION_IMPLEMENT_CLASS_DESCRIPTOR_ThreeFields(UWidget, ToolTipText, Visibility, bIsEnabled);
```
## 原理:
在判断条件上可见HasCustomFieldNotify的判断。
```cpp
protected static bool NeedFieldNotifyCodeGen(UhtClass classObj)
{
return
!classObj.ClassExportFlags.HasAnyFlags(UhtClassExportFlags.HasCustomFieldNotify) &&
classObj.ClassExportFlags.HasAnyFlags(UhtClassExportFlags.HasFieldNotify);
}
```

View File

@@ -0,0 +1,8 @@
# CustomThunkTemplates
- **功能描述:** Specifies the struct that contains the CustomThunk implementations
- **引擎模块:** UHT
- **元数据类型:** bool
在源码里找不到引用的地方

View File

@@ -0,0 +1,34 @@
# Interface
- **功能描述:** 标识这个Class是个Interface。
- **引擎模块:** UHT
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中添加[CLASS_Interface](../../../Flags/EClassFlags/CLASS_Interface.md)
- **常用程度:** 0
标识这个Class是个Interface。
只用在NoExportTypes.h中我们自己的UInterface不需要手动设置。
是UHT在为UInterface生成的时候设置在.generated.h里的。
## 源码例子:
```cpp
UCLASS(abstract, noexport, intrinsic, interface, Config = Engine)
class UInterface : public UObject
{}
```
## 原理:
```cpp
bool FKismetEditorUtilities::IsClassABlueprintInterface(const UClass* Class)
{
if (Class->HasAnyClassFlags(CLASS_Interface) && !Class->HasAnyClassFlags(CLASS_NewerVersionExists))
{
return true;
}
return false;
}
```

View File

@@ -0,0 +1,36 @@
# Intrinsic
- **功能描述:** 指定UHT完全不为此类生成代码需要自己手写。
- **引擎模块:** UHT
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中增加[CLASS_Intrinsic](../../../Flags/EClassFlags/CLASS_Intrinsic.md)
- **常用程度:** 0
指定UHT完全不为此类生成代码需要自己手写。
只在C++直接设定一般新类不设定这个标记这个的都是UE4内部原生的那些类相当于已经在源码中手写了元数据代码。
noexport至少还会解析生成元数据只是缺少注册。因此instric类的所有元数据flags要自己手动标记。但是intrinsic完全不生成代码。其generated.h和.gen.cpp里面都是空的。noexporttyps.h里的目前采用intrinsic的类只有UCLASS(noexport, Intrinsic)class UModel{}这还是被cpp不编译的。
```cpp
//UCLASS(Intrinsic)
//class INSIDER_API UMyClass_Intrinsic :public UObject //syntax error: missing ';' before '<class-head>'
//{
// GENERATED_BODY()
//
//};
//.h
class INSIDER_API UMyClass_Intrinsic :public UObject
{
DECLARE_CLASS_INTRINSIC(UMyClass_Intrinsic, UObject, CLASS_MatchedSerializers, TEXT("/Script/Insider"))
};
//.cpp
IMPLEMENT_INTRINSIC_CLASS(UMyClass_Intrinsic, INSIDER_API, UObject, INSIDER_API, "/Script/Insider", {})
class COREUOBJECT_API UInterface : public UObject
{
DECLARE_CLASS_INTRINSIC(UInterface, UObject, CLASS_Interface | CLASS_Abstract, TEXT("/Script/CoreUObject"))
};
```

View File

@@ -0,0 +1,132 @@
# MinimalAPI
- **功能描述:** 不dll导出该类的函数只导出类型信息当作变量。
- **引擎模块:** DllExport
- **元数据类型:** bool
- **作用机制:** 在ClassFlags增加[CLASS_MinimalAPI](../../../../Flags/EClassFlags/CLASS_MinimalAPI.md)
- **常用程度:** ★★★
不dll导出该类的函数只导出类型信息当作变量。
- 其他引用的模块可以利用指针来做转换,但是不能调用上面的函数。但是蓝图里依然可以访问。
- 好处是可以缩短编译信息和加快链接速度因为没有了那么多dllexport函数。
- 注意MinimalAPI不能和MODULENAME_API一起使用因为MinimalAPI就是用来不导出的而MODULENAME_API就是用来导出的。但是MinimalAPI的效果并不等价于不写MODULENAME_API的效果因为MinimalAPI还会导出GetPrivateStaticClass用来允许NewObject。所以如果一个类完全不想让另一个模块知道则不需要写任何导出。而如果想让另一个模块知道类型但是完全不能调用函数则可以用MinimalAPI来防止。
- 游戏的模块推荐不导出。插件的模块外部的推荐导出内部的基类可以考虑MinimalAPI私有类则可以完全不导出。引擎里使用MinimalAPI还是非常多的生成的效果是这些类可以作为变量使用但不能继承和调用方法。
- 一般是配合BlueprintType使用这样就可以在蓝图中作为变量。
- 可以正常在蓝图中调用函数和属性。因为蓝图调用是只需要反射信息就可以的,因为是自己模块把函数和属性的指针注册到系统里。
## 示例代码:
```cpp
UCLASS()
class UMyClass_NotMinimalAPI :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
UFUNCTION(BlueprintCallable)
void MyFunc();
};
UCLASS(MinimalAPI)
class UMyClass_MinimalAPI :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
UFUNCTION(BlueprintCallable)
void MyFunc();
};
UCLASS(MinimalAPI, BlueprintType)
class UMyClass_MinimalAPI_BlueprintType :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MyProperty;
UFUNCTION(BlueprintCallable)
void MyFunc() {}
};
UCLASS(MinimalAPI)
class UMyClass_MinimalAPI_BlueprintFunctionLibary :public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
static void MyFuncInMinimalAPI();
UFUNCTION(BlueprintCallable)
static INSIDER_API void MyFuncInMinimalAPIWithAPI();
};
```
## 示例效果:
可以正常在蓝图中调用函数和属性。蓝图函数库中的方法也可以调用说明UHT对MinimalAPI还是依然生成反射的调用信息的蓝图调用是只需要反射信息就可以的因为是自己模块把函数和属性的指针注册到系统里因此并不需要dll导出。只不过在dll导出工具里查看dll导出的函数列表并没有该函数。
![Untitled](Untitled.png)
查看dll导出函数列表
```cpp
class UClass * __ptr64 __cdecl StaticClass<class UMyClass_MinimalAPI>(void)
class UClass * __ptr64 __cdecl StaticClass<class UMyClass_MinimalAPI_BlueprintFunctionLibary>(void)
class UClass * __ptr64 __cdecl StaticClass<class UMyClass_MinimalAPI_BlueprintType>(void)
class UClass * __ptr64 __cdecl StaticClass<class UMyClass_NotMinimalAPI>(void)
class UClass * __ptr64 __cdecl Z_Construct_UClass_UMyClass_MinimalAPI(void)
class UClass * __ptr64 __cdecl Z_Construct_UClass_UMyClass_MinimalAPI_BlueprintFunctionLibary(void)
class UClass * __ptr64 __cdecl Z_Construct_UClass_UMyClass_MinimalAPI_BlueprintFunctionLibary_NoRegister(void)
class UClass * __ptr64 __cdecl Z_Construct_UClass_UMyClass_MinimalAPI_BlueprintType(void)
class UClass * __ptr64 __cdecl Z_Construct_UClass_UMyClass_MinimalAPI_BlueprintType_NoRegister(void)
class UClass * __ptr64 __cdecl Z_Construct_UClass_UMyClass_MinimalAPI_NoRegister(void)
class UClass * __ptr64 __cdecl Z_Construct_UClass_UMyClass_NotMinimalAPI(void)
class UClass * __ptr64 __cdecl Z_Construct_UClass_UMyClass_NotMinimalAPI_NoRegister(void)
private: static class UClass * __ptr64 __cdecl UMyClass_MinimalAPI::GetPrivateStaticClass(void)
private: static class UClass * __ptr64 __cdecl UMyClass_MinimalAPI_BlueprintFunctionLibary::GetPrivateStaticClass(void)
private: static class UClass * __ptr64 __cdecl UMyClass_MinimalAPI_BlueprintType::GetPrivateStaticClass(void)
public: __cdecl UMyClass_MinimalAPI::UMyClass_MinimalAPI(class FObjectInitializer const & __ptr64) __ptr64
public: __cdecl UMyClass_MinimalAPI::UMyClass_MinimalAPI(class FVTableHelper & __ptr64) __ptr64
public: __cdecl UMyClass_MinimalAPI_BlueprintFunctionLibary::UMyClass_MinimalAPI_BlueprintFunctionLibary(class FObjectInitializer const & __ptr64) __ptr64
public: __cdecl UMyClass_MinimalAPI_BlueprintFunctionLibary::UMyClass_MinimalAPI_BlueprintFunctionLibary(class FVTableHelper & __ptr64) __ptr64
public: __cdecl UMyClass_MinimalAPI_BlueprintType::UMyClass_MinimalAPI_BlueprintType(class FObjectInitializer const & __ptr64) __ptr64
public: __cdecl UMyClass_MinimalAPI_BlueprintType::UMyClass_MinimalAPI_BlueprintType(class FVTableHelper & __ptr64) __ptr64
public: static void __cdecl UMyClass_MinimalAPI_BlueprintFunctionLibary::MyFuncInMinimalAPIWithAPI(void)
public: virtual __cdecl UMyClass_MinimalAPI::~UMyClass_MinimalAPI(void) __ptr64
public: virtual __cdecl UMyClass_MinimalAPI_BlueprintFunctionLibary::~UMyClass_MinimalAPI_BlueprintFunctionLibary(void) __ptr64
public: virtual __cdecl UMyClass_MinimalAPI_BlueprintType::~UMyClass_MinimalAPI_BlueprintType(void) __ptr64
public: void __cdecl UMyClass_MinimalAPI::`default constructor closure'(void) __ptr64
public: void __cdecl UMyClass_MinimalAPI_BlueprintFunctionLibary::`default constructor closure'(void) __ptr64
public: void __cdecl UMyClass_MinimalAPI_BlueprintType::`default constructor closure'(void) __ptr64
```
![Untitled](Untitled%201.png)
在跨模块调用的时候因为没有dll导出因此会触发链接错误。
```cpp
UMyClass_MinimalAPI* a = NewObject<UMyClass_MinimalAPI>();
//第一种错误
//error LNK2019: unresolved external symbol "public: void __cdecl UMyClass_MinimalAPI::MyFunc(void)" (?MyFunc@UMyClass_MinimalAPI@@QEAAXXZ) referenced in function "public: void __cdecl UMyClass_UseMinimalAPI::TestFunc(void)" (?TestFunc@UMyClass_UseMinimalAPI@@QEAAXXZ)
//a->MyFunc();
a->MyProperty++;
//第二种错误
//error LNK2019: unresolved external symbol "private: static class UClass * __cdecl UMyClass_NotMinimalAPI::GetPrivateStaticClass(void)" (?GetPrivateStaticClass@UMyClass_NotMinimalAPI@@CAPEAVUClass@@XZ)
//referenced in function "class UMyClass_NotMinimalAPI * __cdecl NewObject<class UMyClass_NotMinimalAPI>(class UObject *)" (??$NewObject@VUMyClass_NotMinimalAPI@@@@YAPEAVUMyClass_NotMinimalAPI@@PEAVUObject@@@Z)
auto* a = NewObject<UMyClass_NotMinimalAPI>();
//第三种错误
//error LNK2019: unresolved external symbol "public: static void __cdecl UMyClass_MinimalAPI_BlueprintFunctionLibary::MyFuncInMinimalAPI(void)" (?MyFuncInMinimalAPI@UMyClass_MinimalAPI_BlueprintFunctionLibary@@SAXXZ)
//referenced in function "public: void __cdecl UMyClass_UseMinimalAPI::TestFunc(void)" (?TestFunc@UMyClass_UseMinimalAPI@@QEAAXXZ)
UMyClass_MinimalAPI_BlueprintFunctionLibary::MyFuncInMinimalAPI();
UMyClass_MinimalAPI_BlueprintFunctionLibary::MyFuncInMinimalAPIWithAPI();
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -0,0 +1,32 @@
# NoExport
- **功能描述:** 指定UHT不要用来自动生成注册的代码而只是进行词法分析提取元数据。
- **引擎模块:** UHT
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中增加EClassFlags: [CLASS_NoExport](../../../Flags/EClassFlags/CLASS_NoExport.md)
- **常用程度:** 0
指定UHT不要用来自动生成注册的代码而只是进行词法分析提取元数据。
引擎的NoExportTypes.h里大量都是这种类型专门提供给UHT来提取信息的。一般会用#if !CPP //noexport class来包裹来避免编译。同时在另一个地方会定义这个类。因为StaticRegisterNatives##TClass没有生成所以GetPrivateStaticClass不能调用成功所以不能NewObject。一般noexport和Intrinsic都是配合使用的。因为DECLARE_CLASS_INTRINSIC内部会声明static void StaticRegisterNatives##TClass() {} 来允许成功调用。
引擎里的结构倒是经常用noexport来阻止生成UHT注册。因为结构其实不需要调用GetPrivateStaticClass来创建元数据。只要有Z_Construct_UScriptStruct_XXX来生成构造相应的UScriptStruct对象就行。
## 测试代码:
```cpp
UCLASS(noexport)
class INSIDER_API UMyClass_NoExport :public UObject
{
GENERATED_BODY()
public:
};
```
## 测试结果:
```cpp
编译的时候生成错误:
error LNK2019: unresolved external symbol "private: static void __cdecl UMyClass_NoExport::StaticRegisterNativesUMyClass_NoExport(void)" (?StaticRegisterNativesUMyClass_NoExport@UMyClass_NoExport@@CAXXZ) referenced in function "private: static class UClass * __cdecl UMyClass_NoExport::GetPrivateStaticClass(void)" (?GetPrivateStaticClass@UMyClass_NoExport@@CAPEAVUClass@@XZ)
```

View File

@@ -0,0 +1,37 @@
# UCLASS()
- **功能描述:** 留空的默认行为是不能在蓝图中被继承,不能在蓝图中定义变量,但拥有反射的功能。
- **引擎模块:** UHT
- **元数据类型:** bool
- **作用机制:** 在ClassFlags中增加[CLASS_MatchedSerializers](../../../Flags/EClassFlags/CLASS_MatchedSerializers.md), [CLASS_Native](../../../Flags/EClassFlags/CLASS_Native.md), [CLASS_RequiredAPI](../../../Flags/EClassFlags/CLASS_RequiredAPI.md), [CLASS_TokenStreamAssembled](../../../Flags/EClassFlags/CLASS_TokenStreamAssembled.md), [CLASS_Intrinsic](../../../Flags/EClassFlags/CLASS_Intrinsic.md), [CLASS_Constructed](../../../Flags/EClassFlags/CLASS_Constructed.md)
- **关联项:** [不写UCLASS()](不写UCLASS().md)
- **常用程度:★★★★★**
不能在蓝图中被继承,不能在蓝图中定义变量。
但依然都可以通过蓝图ConstructObject创建出来。对于想要拥有反射功能但是并不想在蓝图中被使用会挺适合。
## 示例代码:
```cpp
/*
[MyClass_Default Class->Struct->Field->Object /Script/Insider.MyClass_Default] [IncludePath = Class/MyClass_Default.h, ModuleRelativePath = Class/MyClass_Default.h]
ObjectFlags: RF_Public | RF_Standalone | RF_Transient
Outer: Package /Script/Insider
ClassFlags: CLASS_MatchedSerializers | CLASS_Native | CLASS_RequiredAPI | CLASS_TokenStreamAssembled | CLASS_Intrinsic | CLASS_Constructed
Size: 48
{
public: void ExecuteUbergraph(int32 EntryPoint);
};
*/
UCLASS()
class INSIDER_API UMyClass_Default :public UObject
{
GENERATED_BODY()
public:
};
```
默认的拥有这些标志CLASS_MatchedSerializers | CLASS_Native | CLASS_RequiredAPI | CLASS_TokenStreamAssembled | CLASS_Intrinsic | CLASS_Constructed

View File

@@ -0,0 +1,21 @@
# 不写UCLASS()
- **功能描述:** 只是作为一个普通的C++对象,没有反射功能。
- **引擎模块:** UHT
- **元数据类型:** bool
- **关联项:** [UCLASS()](UCLASS().md)
- **常用程度:** ★
只是作为一个普通的C++对象,没有反射功能。
一般情况继承自UObject的最少也会有一个UCLASS()这样才有反射功能。但是注意如果调用UMyClass_NoUCLASS::StaticClass()会返回基类UObject的Class因为子类没有覆盖。因此也可以说本类是没有生成自己的UClass元数据对象。
```cpp
class INSIDER_API UMyClass_NoUCLASS :public UObject
{
};
```
UObject的Class默认的标记是CLASS_Abstract | CLASS_MatchedSerializers | CLASS_Native | CLASS_TokenStreamAssembled | CLASS_Intrinsic | CLASS_Constructed。因此不能被NewObject生成对象。在手动去掉CLASS_Abstract后可以正常new但是对象的名称依然是Object显然这是因为使用的就是Object的Class。

Some files were not shown because too many files have changed in this diff Show More