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,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