.obsidian
.vs
00-MOC
01-Diary
02-Note
03-UnrealEngine
Animation
Editor
Gameplay
AI
Animation
Code
Debug
GAS
Gameplay
Http
Lyra
Mass
Online
Other
PuerTS
UObject
UnrealSpecifiers
Flags
Meta
Actor
AnimationGraph
Asset
Blueprint
AdvancedDisplay
AllowPrivateAccess
BlueprintAutocast
BlueprintPrivate
BlueprintProtected
BlueprintProtected.md
Untitled 1.png
Untitled 2.png
Untitled 3.png
Untitled 4.png
Untitled 5.png
Untitled.png
BlueprintThreadSafe
CallableWithoutWorldContext
CommutativeAssociativeBinaryOperator
CompactNodeTitle
DefaultToSelf
DontUseGenericSpawnObject
Exec
ExposeOnSpawn
ExposedAsyncProxy
ForceAsFunction
HasDedicatedAsyncNode
HiddenNode
HideThen
IgnoreTypePromotion
Keywords
KismetHideOverrides
Latent
NativeMakeFunc
NotInputConfigurable
ObjectSetType
Param
ProhibitedInterfaces
RestrictedToClasses
ReturnDisplayName
SetParam
ShowWorldContextPin
UnsafeDuringActorConstruction
WorldContext
BlueprintGetter.md
BlueprintInternalUseOnly.md
BlueprintInternalUseOnlyHierarchical.md
BlueprintSetter.md
BlueprintType.md
CPP_Default_XXX.md
CallInEditor.md
CannotImplementInterfaceInBlueprint.md
DisplayName.md
GetByRef.md
HideFunctions.md
IsBlueprintBase.md
IsConversionRoot.md
NativeBreakFunc.md
NativeConst.md
NotBlueprintThreadSafe.md
SparseClassDataTypes.md
Variadic.md
Component
Config
Container
Debug
DetailsPanel
Development
Enum
GAS
Material
Niagara
Numeric
Object
Path
Pin
RigVM
Scene
Script
Sequencer
Serialization
SparseDataType
String
Struct
TypePicker
UHT
Widget
Meta.md
Specifier
UnrealSpecifiers.md
Ue4 c++ UProperty反射 PostEditChangeProperty.md
Ue4Object生命周期.jpg
大钊提供的一种获取UE Private函数的方法.md
LevelScene
Math
Mobile
Physical
Plugins
Rendering
Sequence
UI
VirtualProduction
VisualEffect
卡通渲染相关资料
性能优化
流程管理与部署
.keep
03-UnrealEngine.md
04-ComputerGraphics
05-SDHGame
06-DCC
07-Other
08-Assets
09-Templates
.gitattributes
.gitignore
.gitmodules
LICENSE
332 lines
14 KiB
Markdown
332 lines
14 KiB
Markdown
|
# BlueprintProtected
|
|||
|
|
|||
|
- **功能描述:** 指定该函数或属性只能在本类以及子类中被调用或读写,类似C++中的protected作用域限制。不可在别的蓝图类里访问。
|
|||
|
- **使用位置:** UFUNCTION, UPROPERTY
|
|||
|
- **引擎模块:** Blueprint
|
|||
|
- **元数据类型:** bool
|
|||
|
- **关联项:** [BlueprintPrivate](../BlueprintPrivate/BlueprintPrivate.md), [AllowPrivateAccess](../AllowPrivateAccess/AllowPrivateAccess.md)
|
|||
|
- **常用程度:** ★★★
|
|||
|
|
|||
|
作用在函数上:
|
|||
|
|
|||
|
标记该函数只能在本类以及子类中被调用,类似C++中的protected函数的作用域限制。不可在别的蓝图类里调用。
|
|||
|
|
|||
|
作用在属性上时,标明该属性只能在本类或派生类里进行读写,但不能在别的蓝图类里访问。
|
|||
|
|
|||
|
指定该函数或属性只能在本类以及子类中被调用或读写,类似C++中的protected函数的作用域限制。不可在别的蓝图类里访问。
|
|||
|
|
|||
|
## 测试代码:
|
|||
|
|
|||
|
```cpp
|
|||
|
UCLASS(Blueprintable, BlueprintType)
|
|||
|
class INSIDER_API AMyFunction_Access :public AActor
|
|||
|
{
|
|||
|
public:
|
|||
|
GENERATED_BODY()
|
|||
|
public:
|
|||
|
//(BlueprintProtected = true, ModuleRelativePath = Function/MyFunction_Access.h)
|
|||
|
//FUNC_Final | FUNC_Native | FUNC_Public | FUNC_BlueprintCallable
|
|||
|
UFUNCTION(BlueprintCallable, meta = (BlueprintProtected = "true"))
|
|||
|
void MyNative_HasProtected() {}
|
|||
|
|
|||
|
//(BlueprintPrivate = true, ModuleRelativePath = Function/MyFunction_Access.h)
|
|||
|
//FUNC_Final | FUNC_Native | FUNC_Public | FUNC_BlueprintCallable
|
|||
|
UFUNCTION(BlueprintCallable, meta = (BlueprintPrivate = "true"))
|
|||
|
void MyNative_HasPrivate() {}
|
|||
|
public:
|
|||
|
//FUNC_Final | FUNC_Native | FUNC_Public | FUNC_BlueprintCallable
|
|||
|
UFUNCTION(BlueprintCallable)
|
|||
|
void MyNative_NativePublic() {}
|
|||
|
protected:
|
|||
|
//FUNC_Final | FUNC_Native | FUNC_Protected | FUNC_BlueprintCallable
|
|||
|
UFUNCTION(BlueprintCallable)
|
|||
|
void MyNative_NativeProtected() {}
|
|||
|
private:
|
|||
|
//FUNC_Final | FUNC_Native | FUNC_Private | FUNC_BlueprintCallable
|
|||
|
UFUNCTION(BlueprintCallable)
|
|||
|
void MyNative_NativePrivate() {}
|
|||
|
};
|
|||
|
|
|||
|
```
|
|||
|
|
|||
|
## 测试效果:
|
|||
|
|
|||
|
蓝图中的子类(BPA_Access_Base继承自AMyFunction_Access )效果:
|
|||
|
|
|||
|
可见BlueprintProtected可以被子类调用,但是BlueprintPrivate只能在本类(C++类中定义的只能在C++中调用,蓝图中定义的只能在蓝图本类中调用)。而在C++中用protected或private修饰的函数会相应的增加FUNC_Protected和FUNC_Private,但是实际上并不会发生作用。因为机制的设计目的就是如此(详见后文解释)。
|
|||
|
|
|||
|
而在BPA_Access_Base中直接定义的MyBPProtected和MyBPPrivate通过在函数细节面板上直接设置AccessSpecifier,可以在本类都可以调用,但是MyBPPrivate在更加的蓝图子类无法被调用。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
蓝图中的子类(BPA_Access_Child继承自BPA_Access_Base)效果:
|
|||
|
|
|||
|
可见MyNative函数的访问一样。而MyBPPrivate则不能被调用了,这和我们预想的规则一样。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
而在外部类中(BPA_Access_Other,继承自Actor),通过BPA_Access_Base或BPA_Access_Child对象实例访问函数的时候,发现带有BlueprintProtected和BlueprintPrivate都不能被调用。BP的函数也只有AccessSpecifier为默认Public的可以调用。这个规则也很符合预期。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
## 原理:
|
|||
|
|
|||
|
在蓝图右键上是否可以选择该函数的过滤逻辑:
|
|||
|
|
|||
|
如果是static函数,则总是可以。否则必须没有BlueprintProtected或BlueprintPrivate才可以是Public可以被选择出来的。
|
|||
|
|
|||
|
如果是Private,则外部类必须是定义的类本身。
|
|||
|
|
|||
|
如果是Protected,则外部类只需要是定义的类或子类。
|
|||
|
|
|||
|
```cpp
|
|||
|
static bool BlueprintActionFilterImpl::IsFieldInaccessible(FBlueprintActionFilter const& Filter, FBlueprintActionInfo& BlueprintAction)
|
|||
|
{
|
|||
|
bool const bIsProtected = Field.HasMetaData(FBlueprintMetadata::MD_Protected);
|
|||
|
bool const bIsPrivate = Field.HasMetaData(FBlueprintMetadata::MD_Private);
|
|||
|
bool const bIsPublic = !bIsPrivate && !bIsProtected;
|
|||
|
|
|||
|
if( !bIsPublic )
|
|||
|
{
|
|||
|
UClass const* ActionOwner = BlueprintAction.GetOwnerClass();
|
|||
|
for (UBlueprint const* Blueprint : FilterContext.Blueprints)
|
|||
|
{
|
|||
|
UClass const* BpClass = GetAuthoritativeBlueprintClass(Blueprint);
|
|||
|
if (!ensureMsgf(BpClass != nullptr
|
|||
|
, TEXT("Unable to resolve IsFieldInaccessible() - Blueprint (%s) missing an authoratative class (skel: %s, generated: %s, parent: %s)")
|
|||
|
, *Blueprint->GetName()
|
|||
|
, Blueprint->SkeletonGeneratedClass ? *Blueprint->SkeletonGeneratedClass->GetName() : TEXT("[NULL]")
|
|||
|
, Blueprint->GeneratedClass ? *Blueprint->GeneratedClass->GetName() : TEXT("[NULL]")
|
|||
|
, Blueprint->ParentClass ? *Blueprint->ParentClass->GetName() : TEXT("[NULL]")))
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// private functions are only accessible from the class they belong to
|
|||
|
if (bIsPrivate && !IsClassOfType(BpClass, ActionOwner, /*bNeedsExactMatch =*/true))
|
|||
|
{
|
|||
|
bIsFilteredOut = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
else if (bIsProtected && !IsClassOfType(BpClass, ActionOwner))
|
|||
|
{
|
|||
|
bIsFilteredOut = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool UEdGraphSchema_K2::ClassHasBlueprintAccessibleMembers(const UClass* InClass) const
|
|||
|
{
|
|||
|
// @TODO Don't show other blueprints yet...
|
|||
|
UBlueprint* ClassBlueprint = UBlueprint::GetBlueprintFromClass(InClass);
|
|||
|
if (!InClass->HasAnyClassFlags(CLASS_Deprecated | CLASS_NewerVersionExists) && (ClassBlueprint == NULL))
|
|||
|
{
|
|||
|
// Find functions
|
|||
|
for (TFieldIterator<UFunction> FunctionIt(InClass, EFieldIteratorFlags::IncludeSuper); FunctionIt; ++FunctionIt)
|
|||
|
{
|
|||
|
UFunction* Function = *FunctionIt;
|
|||
|
const bool bIsBlueprintProtected = Function->GetBoolMetaData(FBlueprintMetadata::MD_Protected);
|
|||
|
const bool bHidden = FObjectEditorUtils::IsFunctionHiddenFromClass(Function, InClass);
|
|||
|
if (UEdGraphSchema_K2::CanUserKismetCallFunction(Function) && !bIsBlueprintProtected && !bHidden)
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Find vars
|
|||
|
for (TFieldIterator<FProperty> PropertyIt(InClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt)
|
|||
|
{
|
|||
|
FProperty* Property = *PropertyIt;
|
|||
|
if (CanUserKismetAccessVariable(Property, InClass, CannotBeDelegate))
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
在BP中定义的函数如果通过AccessSpecifier设置为Protected或Private,也会相应把该函数加上FUNC_Protected或FUNC_Private。从而实际上影响该函数的作用域。但源码中很多判断会先判断是否Native函数,如果是就不继续做限制。因此我们可以理解这是UE机制的有意为之,故意不把C++里的protected和private作用域算进去,而要求你必须自己手动显式的写上BlueprintProtected或BlueprintPrivate,这样避免有时的模糊不清。
|
|||
|
|
|||
|
```cpp
|
|||
|
bool UEdGraphSchema_K2::CanFunctionBeUsedInGraph(const UClass* InClass, const UFunction* InFunction, const UEdGraph* InDestGraph, uint32 InAllowedFunctionTypes, bool bInCalledForEach, FText* OutReason) const
|
|||
|
{
|
|||
|
const bool bIsNotNative = !FBlueprintEditorUtils::IsNativeSignature(InFunction);
|
|||
|
if(bIsNotNative)
|
|||
|
{
|
|||
|
// Blueprint functions visibility flags can be enforced in blueprints - native functions
|
|||
|
// are often using these flags to only hide functionality from other native functions:
|
|||
|
const bool bIsProtected = (InFunction->FunctionFlags & FUNC_Protected) != 0;
|
|||
|
}
|
|||
|
|
|||
|
bool UK2Node_CallFunction::IsActionFilteredOut(FBlueprintActionFilter const& Filter)
|
|||
|
{
|
|||
|
bool bIsFilteredOut = false;
|
|||
|
for(UEdGraph* TargetGraph : Filter.Context.Graphs)
|
|||
|
{
|
|||
|
bIsFilteredOut |= !CanPasteHere(TargetGraph);
|
|||
|
}
|
|||
|
|
|||
|
if(const UFunction* TargetFunction = GetTargetFunction())
|
|||
|
{
|
|||
|
const bool bIsProtected = (TargetFunction->FunctionFlags & FUNC_Protected) != 0;
|
|||
|
const bool bIsPrivate = (TargetFunction->FunctionFlags & FUNC_Private) != 0;
|
|||
|
const UClass* OwningClass = TargetFunction->GetOwnerClass();
|
|||
|
if( (bIsProtected || bIsPrivate) && !FBlueprintEditorUtils::IsNativeSignature(TargetFunction) && OwningClass)
|
|||
|
{
|
|||
|
OwningClass = OwningClass->GetAuthoritativeClass();
|
|||
|
// we can filter private and protected blueprints that are unrelated:
|
|||
|
bool bAccessibleInAll = true;
|
|||
|
for (const UBlueprint* Blueprint : Filter.Context.Blueprints)
|
|||
|
{
|
|||
|
UClass* AuthoritativeClass = Blueprint->GeneratedClass;
|
|||
|
if(!AuthoritativeClass)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if(bIsPrivate)
|
|||
|
{
|
|||
|
bAccessibleInAll = bAccessibleInAll && AuthoritativeClass == OwningClass;
|
|||
|
}
|
|||
|
else if(bIsProtected)
|
|||
|
{
|
|||
|
bAccessibleInAll = bAccessibleInAll && AuthoritativeClass->IsChildOf(OwningClass);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if(!bAccessibleInAll)
|
|||
|
{
|
|||
|
bIsFilteredOut = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return bIsFilteredOut;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
# 作用在属性上:
|
|||
|
|
|||
|
作用在属性上时,标明该属性只能在本类或派生类里进行读写,但不能在别的蓝图类里访问。
|
|||
|
|
|||
|
测试代码:
|
|||
|
|
|||
|
```cpp
|
|||
|
UCLASS(Blueprintable, BlueprintType)
|
|||
|
class INSIDER_API AMyFunction_Access :public AActor
|
|||
|
{
|
|||
|
public:
|
|||
|
GENERATED_BODY()
|
|||
|
public:
|
|||
|
//(BlueprintProtected = true, Category = MyFunction_Access, ModuleRelativePath = Function/MyFunction_Access.h)
|
|||
|
//CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
|||
|
UPROPERTY(BlueprintReadWrite,meta = (BlueprintProtected = "true"))
|
|||
|
int32 MyNativeInt_HasProtected;
|
|||
|
|
|||
|
//(BlueprintPrivate = true, Category = MyFunction_Access, ModuleRelativePath = Function/MyFunction_Access.h)
|
|||
|
//CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
|||
|
UPROPERTY(BlueprintReadWrite,meta = (BlueprintPrivate = "true"))
|
|||
|
int32 MyNativeInt_HasPrivate;
|
|||
|
|
|||
|
public:
|
|||
|
//CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPublic
|
|||
|
UPROPERTY(BlueprintReadWrite)
|
|||
|
int32 MyNativeInt_NativePublic;
|
|||
|
protected:
|
|||
|
//CPF_BlueprintVisible | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_Protected | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierProtected
|
|||
|
UPROPERTY(BlueprintReadOnly)
|
|||
|
int32 MyNativeInt_NativeProtected;
|
|||
|
private:
|
|||
|
//CPF_Edit | CPF_ZeroConstructor | CPF_IsPlainOldData | CPF_NoDestructor | CPF_HasGetValueTypeHash | CPF_NativeAccessSpecifierPrivate
|
|||
|
//error : BlueprintReadWrite should not be used on private members
|
|||
|
UPROPERTY(EditAnywhere)
|
|||
|
int32 MyNativeInt_NativePrivate;
|
|||
|
};
|
|||
|
```
|
|||
|
|
|||
|
蓝图效果:
|
|||
|
|
|||
|
在其子类BPA_Access_Base测试,发现除了MyNativeInt_HasPrivate都可以访问。这符合逻辑,毕竟Private的含义就是只有在本类才可以访问。
|
|||
|
|
|||
|
而在本蓝图类定义的MyBPIntPrivate因为勾上了Private,会导致该属性增加了BlueprintPrivate = true的meta,但因为是本类里定义的,所以在本类里也依然可以读写访问。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
继续在蓝图中的子类(BPA_Access_Child继承自BPA_Access_Base)效果:
|
|||
|
|
|||
|
Protected的属性依然都可以访问,但是MyBPIntPrivate属性因为是Private的,因此都不能读写,如果强制粘贴节点,会在编译的时候报错。Private的含义是只在本类中才可以访问。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
而在外部类中(BPA_Access_Other,继承自Actor),通过BPA_Access_Base或BPA_Access_Child对象实例访问属性的时候:带有BlueprintProtected和BlueprintPrivate都不能访问。而C++中的protected修饰并无影响。
|
|||
|
|
|||
|
而MyBPIntPrivate因为是Private所以不能访问。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
## 原理:
|
|||
|
|
|||
|
在源码里搜索CPF_NativeAccessSpecifierProtected,发现并无使用的地方。
|
|||
|
|
|||
|
而CPF_NativeAccessSpecifierPrivate只在IsPropertyPrivate中引用,后者也只在蓝图编译检测线程安全的时候被检测到。因此CPF_NativeAccessSpecifierPrivate也实际上并无真正的被用来做作用域的限制。
|
|||
|
|
|||
|
综合二者,这也是在C++中protected和private并不在蓝图中造成影响的原因。但UHT会阻止private变量上的BlueprintReadWrite或BlueprintReadOnly,造成事实上的无法在蓝图中访问,达成了无法在蓝图子类里访问C++基类private变量的效果。
|
|||
|
|
|||
|
因此实际上在蓝图中的变量作用域控制,采用的元数据BlueprintProtected 和BlueprintPrivate,在蓝图右键能否创建属性读写节点的逻辑在上面的BlueprintActionFilterImpl::IsFieldInaccessible函数中体现。而编译的时候判断一个属性是否可读写的逻辑在IsPropertyWritableInBlueprint和IsPropertyReadableInBlueprint这两个函数,如果最终的状态结果是Private,则说明不可访问。在UK2Node_VariableGet和UK2Node_VariableSet的ValidateNodeDuringCompilation,会检测出来并报错。
|
|||
|
|
|||
|
```cpp
|
|||
|
bool FBlueprintEditorUtils::IsPropertyPrivate(const FProperty* Property)
|
|||
|
{
|
|||
|
return Property->HasAnyPropertyFlags(CPF_NativeAccessSpecifierPrivate) || Property->GetBoolMetaData(FBlueprintMetadata::MD_Private);
|
|||
|
}
|
|||
|
|
|||
|
FBlueprintEditorUtils::EPropertyWritableState FBlueprintEditorUtils::IsPropertyWritableInBlueprint(const UBlueprint* Blueprint, const FProperty* Property)
|
|||
|
{
|
|||
|
if (Property)
|
|||
|
{
|
|||
|
if (!Property->HasAnyPropertyFlags(CPF_BlueprintVisible))
|
|||
|
{
|
|||
|
return EPropertyWritableState::NotBlueprintVisible;
|
|||
|
}
|
|||
|
if (Property->HasAnyPropertyFlags(CPF_BlueprintReadOnly))
|
|||
|
{
|
|||
|
return EPropertyWritableState::BlueprintReadOnly;
|
|||
|
}
|
|||
|
if (Property->GetBoolMetaData(FBlueprintMetadata::MD_Private))
|
|||
|
{
|
|||
|
const UClass* OwningClass = Property->GetOwnerChecked<UClass>();
|
|||
|
if (OwningClass->ClassGeneratedBy.Get() != Blueprint)
|
|||
|
{
|
|||
|
return EPropertyWritableState::Private;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return EPropertyWritableState::Writable;
|
|||
|
}
|
|||
|
|
|||
|
FBlueprintEditorUtils::EPropertyReadableState FBlueprintEditorUtils::IsPropertyReadableInBlueprint(const UBlueprint* Blueprint, const FProperty* Property)
|
|||
|
{
|
|||
|
if (Property)
|
|||
|
{
|
|||
|
if (!Property->HasAnyPropertyFlags(CPF_BlueprintVisible))
|
|||
|
{
|
|||
|
return EPropertyReadableState::NotBlueprintVisible;
|
|||
|
}
|
|||
|
if (Property->GetBoolMetaData(FBlueprintMetadata::MD_Private))
|
|||
|
{
|
|||
|
const UClass* OwningClass = Property->GetOwnerChecked<UClass>();
|
|||
|
if (OwningClass->ClassGeneratedBy.Get() != Blueprint)
|
|||
|
{
|
|||
|
return EPropertyReadableState::Private;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return EPropertyReadableState::Readable;
|
|||
|
}
|
|||
|
```
|