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,17 @@
# AllowedParamType
- **使用位置:** UFUNCTION
- **引擎模块:** AnimationGraph
- **元数据类型:** string="abc"
```cpp
// Sets a parameter's value in the supplied scope.
// @param Scope Scopes corresponding to an existing scope in a schedule, or "None". Passing "None" will apply the parameter to the whole schedule.
// @param Ordering Where to apply the parameter in relation to the supplied scope. Ignored for scope "None".
// @param Name The name of the parameter to apply
// @param Value The value to set the parameter to
UFUNCTION(BlueprintCallable, Category = "AnimNext", CustomThunk, meta = (CustomStructureParam = Value, UnsafeDuringActorConstruction))
void SetParameterInScope(UPARAM(meta = (CustomWidget = "ParamName", AllowedParamType = "FAnimNextScope")) FName Scope, EAnimNextParameterScopeOrdering Ordering, UPARAM(meta = (CustomWidget = "ParamName")) FName Name, int32 Value);
```
查了一下只在AnimNext中用到。

View File

@@ -0,0 +1,65 @@
# AlwaysAsPin
- **功能描述:** 在动画蓝图中使得动画节点的某个属性总是暴露出来成为引脚
- **使用位置:** UPROPERTY
- **引擎模块:** Pin
- **元数据类型:** bool
- **限制类型:** FAnimNode_Base
- **关联项:** [PinShownByDefault](../PinShownByDefault/PinShownByDefault.md)
- **常用程度:** ★★★
和PinShownByDefault的区别是前者会导致只能一直显示为引脚。而PinShownByDefault默认显示为引脚当也之后也可以改变。
## 测试代码:
```cpp
USTRUCT(BlueprintInternalUseOnly)
struct INSIDEREDITOR_API FAnimNode_MyTestPinShown : public FAnimNode_Base
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PinShownByDefaultTest)
int32 MyInt_NotShown = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PinShownByDefaultTest, meta = (PinShownByDefault))
int32 MyInt_PinShownByDefault = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PinShownByDefaultTest, meta = (AlwaysAsPin))
int32 MyInt_AlwaysAsPin = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PinShownByDefaultTest, meta = (NeverAsPin))
int32 MyInt_NeverAsPin = 123;
};
```
## 测试效果:
![PinShown](PinShown.gif)
## 原理:
根据源码的里的逻辑可见bAlwaysShow 会导致bShowPin和PinShownByDefault的区别是前者会导致只能一直显示为引脚。而PinShownByDefault默认显示为引脚当也之后也可以改变。
```cpp
void FAnimBlueprintNodeOptionalPinManager::GetRecordDefaults(FProperty* TestProperty, FOptionalPinFromProperty& Record) const
{
const UAnimationGraphSchema* Schema = GetDefault<UAnimationGraphSchema>();
// Determine if this is a pose or array of poses
FArrayProperty* ArrayProp = CastField<FArrayProperty>(TestProperty);
FStructProperty* StructProp = CastField<FStructProperty>(ArrayProp ? ArrayProp->Inner : TestProperty);
const bool bIsPoseInput = (StructProp && StructProp->Struct->IsChildOf(FPoseLinkBase::StaticStruct()));
//@TODO: Error if they specified two or more of these flags
const bool bAlwaysShow = TestProperty->HasMetaData(Schema->NAME_AlwaysAsPin) || bIsPoseInput;
const bool bOptional_ShowByDefault = TestProperty->HasMetaData(Schema->NAME_PinShownByDefault);
const bool bOptional_HideByDefault = TestProperty->HasMetaData(Schema->NAME_PinHiddenByDefault);
const bool bNeverShow = TestProperty->HasMetaData(Schema->NAME_NeverAsPin);
const bool bPropertyIsCustomized = TestProperty->HasMetaData(Schema->NAME_CustomizeProperty);
const bool bCanTreatPropertyAsOptional = CanTreatPropertyAsOptional(TestProperty);
Record.bCanToggleVisibility = bCanTreatPropertyAsOptional && (bOptional_ShowByDefault || bOptional_HideByDefault);
Record.bShowPin = bAlwaysShow || bOptional_ShowByDefault;
Record.bPropertyIsCustomized = bPropertyIsCustomized;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

View File

@@ -0,0 +1,9 @@
# AnimBlueprintFunction
- **功能描述:** 标明是动画蓝图里的内部纯存根函数,只在动画蓝图编译时设置
- **使用位置:** UFUNCTION
- **引擎模块:** AnimationGraph
- **元数据类型:** bool
- **限制类型:** Anim BP
只是在内部使用,在动画蓝图编译的时候设置。但是没有在代码里显式的编写。

View File

@@ -0,0 +1,78 @@
# AnimGetter
- **功能描述:** 指定UAnimInstance及子类的该函数成为一个AnimGetter函数。
- **使用位置:** UFUNCTION
- **引擎模块:** AnimationGraph
- **元数据类型:** bool
- **限制类型:** UAnimInstance及子类的函数
- **关联项:** [GetterContext](../GetterContext/GetterContext.md)
- **常用程度:** ★★★
指定UAnimInstance及子类的该函数成为一个AnimGetter函数。
- 在一些情况下会继承UAnimInstance创建自己的动画蓝图子类然后里面可以自己做一些优化或者添加一些自己的功能函数。
- 所谓的AnimGetter其实就是会被UK2Node_AnimGetter识别并包装成该蓝图节点的函数。识别的范围是在UAnimInstance及子类就是动画蓝图的C++函数。
- AnimGetter还有两个额外功能一是会自动根据当前上下文填充函数里的AssetPlayerIndexMachineIndexStateIndexTransitionIndex和参数。二是会根据GetterContext把该函数限定只能在某些蓝图里调用。普通的蓝图函数不具有这些便利的功能和检查用起来就不够智能。
- 要成为AnimGetter还必须具有
- AnimGetter自然不必说
- BlueprintThreadSafe才能在动画蓝图里调用多线程安全
- BlueprintPure成为一个存获取值的函数
- BlueprintInternalUseOnly = "true”避免再生成一个默认的蓝图节点只用UK2Node_AnimGetter包装而成的那个。
## 测试代码:
```cpp
UCLASS(BlueprintType)
class INSIDER_API UMyAnimInstance :public UAnimInstance
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, Category = "Animation|Insider", meta = (BlueprintInternalUseOnly = "true", AnimGetter, BlueprintThreadSafe))
float MyGetAnimationLength_AnimGetter(int32 AssetPlayerIndex);
UFUNCTION(BlueprintPure, Category = "Animation|Insider", meta = (BlueprintThreadSafe))
float MyGetAnimationLength(int32 AssetPlayerIndex);
public:
UFUNCTION(BlueprintPure, Category = "Animation|Insider", meta = (BlueprintInternalUseOnly = "true", AnimGetter, BlueprintThreadSafe))
float MyGetStateWeight_AnimGetter(int32 MachineIndex, int32 StateIndex);
UFUNCTION(BlueprintPure, Category = "Animation|Insider", meta = (BlueprintThreadSafe))
float MyGetStateWeight(int32 MachineIndex, int32 StateIndex);
public:
UFUNCTION(BlueprintPure, Category = "Animation|Insider", meta = (BlueprintInternalUseOnly = "true", AnimGetter, BlueprintThreadSafe))
float MyGetTransitionTimeElapsed_AnimGetter(int32 MachineIndex, int32 TransitionIndex);
UFUNCTION(BlueprintPure, Category = "Animation|Insider", meta = (BlueprintThreadSafe))
float MyGetTransitionTimeElapsed(int32 MachineIndex, int32 TransitionIndex);
};
```
## 测试效果:
分别定义使用了AssetPlayerIndexMachineIndexStateIndexTransitionIndex的AnimGetter函数以及普通蓝图函数作为对比。分别查看在动画蓝图里几个作用域里的用法。
- 可见在不管什么作用域普通蓝图函数都可以调用毕竟没有做Context的检查。另外AssetPlayerIndex等参数都没有被自动填充这几乎是没法用的因为用户其实并不太懂如何去手填这些Index最好是交给编译器来填充。
- 图里高亮的是可以调用的AnimGetter函数。细看的话可以分析发现规则是只有能正确填充AssetPlayerIndex等参数的才能调用。因此在Transition里能调用的最多因为这个时候最叶子节点有动画又有状态机和Transition节点。
![Untitled](Untitled.png)
## 原理:
分析函数上的AnimGetter标记并且生成蓝图节点的功能基本都在UK2Node_AnimGetter这个类里。大家可自行查看。
```cpp
void UK2Node_AnimGetter::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
TArray<UFunction*> AnimGetters;
for(TFieldIterator<UFunction> FuncIter(BPClass) ; FuncIter ; ++FuncIter)
{
UFunction* Func = *FuncIter;
if(Func->HasMetaData(TEXT("AnimGetter")) && Func->HasAnyFunctionFlags(FUNC_Native))
{
AnimGetters.Add(Func);
}
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 KiB

View File

@@ -0,0 +1,89 @@
# AnimNotifyBoneName
- **功能描述:** 使得UAnimNotify或UAnimNotifyState下的FName属性作为BoneName的作用。
- **使用位置:** UPROPERTY
- **引擎模块:** AnimationGraph
- **元数据类型:** bool
- **限制类型:** UAnimNotify或UAnimNotifyState子类下的FName属性
- **常用程度:** ★★
使得UAnimNotify或UAnimNotifyState下的FName属性作为BoneName的作用。
在动画通知的时候也常常需要一个传递骨骼名字参数用普通的字符串参数显然不够定制化。因此给一个FName属性标上AnimNotifyBoneName就可以在配合的细节面板定制化里为它创建专门的更便于填写BoneName的UI。
## 源码中例子:
```cpp
UCLASS(const, hidecategories = Object, collapsecategories, meta = (DisplayName = "Play Niagara Particle Effect"), MinimalAPI)
class UAnimNotify_PlayNiagaraEffect : public UAnimNotify
{
// SocketName to attach to
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AnimNotify", meta = (AnimNotifyBoneName = "true"))
FName SocketName;
}
UCLASS(Blueprintable, meta = (DisplayName = "Timed Niagara Effect"), MinimalAPI)
class UAnimNotifyState_TimedNiagaraEffect : public UAnimNotifyState
{
// The socket within our mesh component to attach to when we spawn the Niagara component
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = NiagaraSystem, meta = (ToolTip = "The socket or bone to attach the system to", AnimNotifyBoneName = "true"))
FName SocketName;
}
```
## 测试代码:
```cpp
UCLASS(BlueprintType)
class INSIDER_API UAnimNotify_MyTest:public UAnimNotify
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FName MyName;
UPROPERTY(EditAnywhere, BlueprintReadWrite,meta=(AnimNotifyBoneName="true"))
FName MyName_Bone;
};
UCLASS(BlueprintType)
class INSIDER_API UAnimNotifyState_MyTest:public UAnimNotifyState
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FName MyName;
UPROPERTY(EditAnywhere, BlueprintReadWrite,meta=(AnimNotifyBoneName="true"))
FName MyName_Bone;
};
```
## 测试效果:
在一个动画序列里加上动画通知可以加两种UAnimNotify或UAnimNotifyState。首先引擎里的自带例子UAnimNotify_PlayNiagaraEffect 和UAnimNotifyState_TimedNiagaraEffect 可以看见在右侧的细节面板上的SocketName的UI不是普通的字符串。
我们自己定义的MyBoneName的动画通知也可以达成同样的效果。MyName_Bone因为加了AnimNotifyBoneName就和普通的MyName不一样了。
![Untitled](Untitled.png)
## 原理:
在定制化的时候根据AnimNotify下的属性是否有这个标记生成专门的的UI。
```cpp
bool FAnimNotifyDetails::CustomizeProperty(IDetailCategoryBuilder& CategoryBuilder, UObject* Notify, TSharedPtr<IPropertyHandle> Property)
{
else if (InPropertyHandle->GetBoolMetaData(TEXT("AnimNotifyBoneName")))
{
// Convert this property to a bone name property
AddBoneNameProperty(CategoryBuilder, Notify, InPropertyHandle);
}
if (bIsBoneName)
{
AddBoneNameProperty(CategoryBuilder, Notify, Property);
return true;
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

View File

@@ -0,0 +1,73 @@
# AnimNotifyExpand
- **功能描述:** 使得UAnimNotify或UAnimNotifyState下的属性直接展开到细节面板里。
- **使用位置:** UPROPERTY
- **引擎模块:** AnimationGraph
- **元数据类型:** bool
- **限制类型:** UAnimNotify或UAnimNotifyState子类下的FName属性
使得UAnimNotify或UAnimNotifyState下的属性直接展开到细节面板里。
在源码里也没有找到应用的例子。
## 原理:
看源码,里面写死了只对有限的引擎自带的几个类起效,因此自己的测试代码并不能生效。
这种写法确实不应该这么写死,希望以后改进吧。到时应该就有源码里的例子了。
```cpp
PropertyModule.RegisterCustomClassLayout( "EditorNotifyObject", FOnGetDetailCustomizationInstance::CreateStatic(&FAnimNotifyDetails::MakeInstance));
bool FAnimNotifyDetails::CustomizeProperty(IDetailCategoryBuilder& CategoryBuilder, UObject* Notify, TSharedPtr<IPropertyHandle> Property)
{
if(Notify && Notify->GetClass() && Property->IsValidHandle())
{
FString ClassName = Notify->GetClass()->GetName();
FString PropertyName = Property->GetProperty()->GetName();
bool bIsBoneName = Property->GetBoolMetaData(TEXT("AnimNotifyBoneName"));
if(ClassName.Find(TEXT("AnimNotify_PlayParticleEffect")) != INDEX_NONE && PropertyName == TEXT("SocketName"))
{
AddBoneNameProperty(CategoryBuilder, Notify, Property);
return true;
}
else if(ClassName.Find(TEXT("AnimNotifyState_TimedParticleEffect")) != INDEX_NONE && PropertyName == TEXT("SocketName"))
{
AddBoneNameProperty(CategoryBuilder, Notify, Property);
return true;
}
else if(ClassName.Find(TEXT("AnimNotify_PlaySound")) != INDEX_NONE && PropertyName == TEXT("AttachName"))
{
AddBoneNameProperty(CategoryBuilder, Notify, Property);
return true;
}
else if (ClassName.Find(TEXT("_SoundLibrary")) != INDEX_NONE && PropertyName == TEXT("SoundContext"))
{
CategoryBuilder.AddProperty(Property);
FixBoneNamePropertyRecurse(Property);
return true;
}
else if (ClassName.Find(TEXT("AnimNotifyState_Trail")) != INDEX_NONE)
{
if(PropertyName == TEXT("FirstSocketName") || PropertyName == TEXT("SecondSocketName"))
{
AddBoneNameProperty(CategoryBuilder, Notify, Property);
return true;
}
else if(PropertyName == TEXT("WidthScaleCurve"))
{
AddCurveNameProperty(CategoryBuilder, Notify, Property);
return true;
}
}
else if (bIsBoneName)
{
AddBoneNameProperty(CategoryBuilder, Notify, Property);
return true;
}
}
}
```

View File

@@ -0,0 +1,91 @@
# BlueprintCompilerGeneratedDefaults
- **功能描述:** 指定该属性的值是编译器生成的,因此在编译后无需复制,可以加速一些编译性能。
- **使用位置:** UPROPERTY
- **引擎模块:** AnimationGraph
- **元数据类型:** bool
- **限制类型:** FAnimNode里的属性
指定该属性的值是编译器生成的,因此在编译后无需复制,可以加速一些编译性能。
在源码里寻找例子可以看到基本是在FAnimNode下的属性在使用。在动画蓝图编译后会调用UEngine::CopyPropertiesForUnrelatedObjects来把之前编译的旧对象里的值复制到新对象其中FCopyPropertiesForUnrelatedObjectsParams的bSkipCompilerGeneratedDefaults决定是否要赋值这个属性的值。如果有标上这个值就说明不要复制。这个值会在别的地方由编译器来填充值。
UAnimGraphNode_Base::OnProcessDuringCompilation函数就是编译后回调的函数。
## 测试代码:
```cpp
USTRUCT(BlueprintInternalUseOnly)
struct INSIDER_API FAnimNode_MyCompilerDefaults : public FAnimNode_Base
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links)
FPoseLink Source;
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CompilerDefaultsTest)
FString MyString;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CompilerDefaultsTest, meta = (BlueprintCompilerGeneratedDefaults))
FString MyString_CompilerDefaults;
};
UCLASS()
class INSIDEREDITOR_API UAnimGraphNode_MyCompilerDefaults : public UAnimGraphNode_Base
{
GENERATED_UCLASS_BODY()
public:
~UAnimGraphNode_MyCompilerDefaults();
UPROPERTY(EditAnywhere, Category = Settings)
FAnimNode_MyCompilerDefaults Node;
protected:
virtual void OnProcessDuringCompilation(IAnimBlueprintCompilationContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData)
{
Node.MyString=TEXT("This is generated by compiler.");
Node.MyString_CompilerDefaults=TEXT("This is generated by compiler.");
}
};
```
## 测试效果:
这个因为是序列化的过程,因此并没有直观的示意图。
可验证的结果是在FCPFUOWriter::ShouldSkipProperty可以见到MyString_CompilerDefaults属性跳过了复制。
## 原理:
蓝图编译的过程核心思想是生成一个新的Graph对象然后把上一次编译的结果对象里的属性和只对象复制到这个新的对象里去。这一步操作是用UEngine::CopyPropertiesForUnrelatedObjects来完成的再内部会继续用FCPFUOWriter::ShouldSkipProperty来判断是否该复制某个属性。而对于一些属性的值只是由编译器生成的临时值反正下一次编译也会再生成因此就不需要复制了标上之后可以略微加速一些性能虽然其实也不多。
```cpp
void UK2Node_PropertyAccess::CreateClassVariablesFromBlueprint(IAnimBlueprintVariableCreationContext& InCreationContext)
{
GeneratedPropertyName = NAME_None;
const bool bRequiresCachedVariable = !bWasResolvedThreadSafe || UAnimBlueprintExtension_PropertyAccess::ContextRequiresCachedVariable(ContextId);
if(ResolvedPinType != FEdGraphPinType() && ResolvedPinType.PinCategory != UEdGraphSchema_K2::PC_Wildcard && bRequiresCachedVariable)
{
// Create internal generated destination property (only if we were not identified as thread safe)
if(FProperty* DestProperty = InCreationContext.CreateUniqueVariable(this, ResolvedPinType))
{
GeneratedPropertyName = DestProperty->GetFName();
DestProperty->SetMetaData(TEXT("BlueprintCompilerGeneratedDefaults"), TEXT("true"));
}
}
}
/* Serializes and stores property data from a specified 'source' object. Only stores data compatible with a target destination object. */
struct FCPFUOWriter : public FObjectWriter, public FCPFUOArchive
{
#if WITH_EDITOR
virtual bool ShouldSkipProperty(const class FProperty* InProperty) const override
{
return (bSkipCompilerGeneratedDefaults && InProperty->HasMetaData(BlueprintCompilerGeneratedDefaultsName));
}
#endif
}
```

View File

@@ -0,0 +1,18 @@
# CustomWidget
- **使用位置:** UFUNCTION, UPROPERTY
- **引擎模块:** AnimationGraph
- **元数据类型:** string="abc"
也可以放在属性上
```cpp
// @param Scope Scopes corresponding to an existing scope in a schedule, or "None". Passing "None" will apply the parameter to the whole schedule.
// @param Ordering Where to apply the parameter in relation to the supplied scope. Ignored for scope "None".
// @param Name The name of the parameter to apply
// @param Value The value to set the parameter to
UFUNCTION(BlueprintCallable, Category = "AnimNext", CustomThunk, meta = (CustomStructureParam = Value, UnsafeDuringActorConstruction))
void SetParameterInScope(UPARAM(meta = (CustomWidget = "ParamName", AllowedParamType = "FAnimNextScope")) FName Scope, EAnimNextParameterScopeOrdering Ordering, UPARAM(meta = (CustomWidget = "ParamName")) FName Name, int32 Value);
```
只在AnimNext和RigVM里用到。

View File

@@ -0,0 +1,144 @@
# CustomizeProperty
- **功能描述:** 使用在FAnimNode的成员属性上告诉编辑器不要为它生成默认Details面板控件后续会在DetailsCustomization里自定义创建相应的编辑控件。
- **使用位置:** UPROPERTY
- **引擎模块:** AnimationGraph
- **元数据类型:** bool
- **限制类型:** FAnimNode里的属性
- **常用程度:** ★
使用在FAnimNode的成员属性上告诉编辑器不要为它生成默认Details面板控件后续会在DetailsCustomization里自定义创建相应的编辑控件。
和AllowEditInlineCustomization的作用有点像都只是做个标记提示编辑器会在别的地方进行自定义不用为它生成默认Details面板控件。
## 源码中例子:
在源码里能见到挺多例子常见的就是在AnimBP中的节点上的属性其在细节面板需要专门的定制化编辑。最常见的例子是Slot这个节点其SlotName只是个FString类型但是在细节面板里显示的却是个ComboString。这是因为它标上了CustomizeProperty告知默认的动画节点细节面板生成器*FAnimGraphNodeDetails先不要为这个属性创建编辑控件之后会在自己的定制化FAnimGraphNodeSlotDetails里为SlotName再创建自定义UI。
```cpp
struct FAnimNode_Slot : public FAnimNode_Base
{
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Settings, meta=(CustomizeProperty))
FName SlotName;
}
void FPersonaModule::CustomizeBlueprintEditorDetails(const TSharedRef<class IDetailsView>& InDetailsView, FOnInvokeTab InOnInvokeTab)
{
InDetailsView->RegisterInstancedCustomPropertyLayout(UAnimGraphNode_Slot::StaticClass(),
FOnGetDetailCustomizationInstance::CreateStatic(&FAnimGraphNodeSlotDetails::MakeInstance, InOnInvokeTab));
InDetailsView->SetExtensionHandler(MakeShared<FAnimGraphNodeBindingExtension>());
}
```
## 测试代码:
```cpp
USTRUCT(BlueprintInternalUseOnly)
struct INSIDER_API FAnimNode_MyCustomProperty : public FAnimNode_Base
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CustomProperty)
FString MyString_Default;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CustomProperty, meta = (CustomizeProperty))
FString MyString_CustomizeProperty;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CustomProperty, meta = (CustomizeProperty))
FString MyString_CustomizeProperty_Other;
};
UCLASS()
class INSIDEREDITOR_API UAnimGraphNode_MyCustomProperty : public UAnimGraphNode_Base
{
GENERATED_UCLASS_BODY()
UPROPERTY(EditAnywhere, Category = Settings)
FAnimNode_MyCustomProperty Node;
};
//再创建一个定制化生成自定义UI
void FMyAnimNode_MyCustomPropertyCustomization::CustomizeDetails(class IDetailLayoutBuilder& DetailBuilder)
{
TSharedPtr<IPropertyHandle> PropertyHandle = DetailBuilder.GetProperty(TEXT("Node.MyString_CustomProperty"));
//Just for test
ComboListItems.Empty();
ComboListItems.Add(MakeShareable(new FString(TEXT("First"))));
ComboListItems.Add(MakeShareable(new FString(TEXT("Second"))));
ComboListItems.Add(MakeShareable(new FString(TEXT("Third"))));
const TSharedPtr<FString> ComboBoxSelectedItem = ComboListItems[0];
IDetailCategoryBuilder& Group = DetailBuilder.EditCategory(TEXT("CustomProperty"));
Group.AddCustomRow(INVTEXT("CustomProperty"))
.NameContent()
[
PropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
SNew(STextComboBox)
.OptionsSource(&ComboListItems)
.InitiallySelectedItem(ComboBoxSelectedItem)
.ContentPadding(2.f)
.ToolTipText(FText::FromString(*ComboBoxSelectedItem))
];
}
//注册定制化
PropertyModule.RegisterCustomClassLayout(TEXT("AnimGraphNode_MyCustomProperty"), FOnGetDetailCustomizationInstance::CreateStatic(&FMyAnimNode_MyCustomPropertyCustomization::MakeInstance));
```
## 测试效果:
SlotName的效果如右侧所示。
我们自己模仿的例子可见MyString_Default依然只是个默认String而MyString_CustomizeProperty则为它创建了自定义UI。
作为对比MyString_CustomizeProperty_Other我们标上了CustomizeProperty但是没有为它创建UI则没有显示出来说明引擎默认的机制因此就把它的UI默认创建流程给跳过了。
![Untitled](Untitled.png)
## 原理:
CustomizeProperty其实会会改变Pin的bPropertyIsCustomized 属性GetRecordDefaults中体现然后在创建流程的过程中不创建默认的widget这个可见CustomizeDetails中的bPropertyIsCustomized判断得知。
```cpp
void FAnimBlueprintNodeOptionalPinManager::GetRecordDefaults(FProperty* TestProperty, FOptionalPinFromProperty& Record) const
{
const UAnimationGraphSchema* Schema = GetDefault<UAnimationGraphSchema>();
// Determine if this is a pose or array of poses
FArrayProperty* ArrayProp = CastField<FArrayProperty>(TestProperty);
FStructProperty* StructProp = CastField<FStructProperty>(ArrayProp ? ArrayProp->Inner : TestProperty);
const bool bIsPoseInput = (StructProp && StructProp->Struct->IsChildOf(FPoseLinkBase::StaticStruct()));
//@TODO: Error if they specified two or more of these flags
const bool bAlwaysShow = TestProperty->HasMetaData(Schema->NAME_AlwaysAsPin) || bIsPoseInput;
const bool bOptional_ShowByDefault = TestProperty->HasMetaData(Schema->NAME_PinShownByDefault);
const bool bOptional_HideByDefault = TestProperty->HasMetaData(Schema->NAME_PinHiddenByDefault);
const bool bNeverShow = TestProperty->HasMetaData(Schema->NAME_NeverAsPin);
const bool bPropertyIsCustomized = TestProperty->HasMetaData(Schema->NAME_CustomizeProperty);
const bool bCanTreatPropertyAsOptional = CanTreatPropertyAsOptional(TestProperty);
Record.bCanToggleVisibility = bCanTreatPropertyAsOptional && (bOptional_ShowByDefault || bOptional_HideByDefault);
Record.bShowPin = bAlwaysShow || bOptional_ShowByDefault;
Record.bPropertyIsCustomized = bPropertyIsCustomized;
}
//这个是在AnimBP中选中一个节点然后在右侧细节面板中的属性
void FAnimGraphNodeDetails::CustomizeDetails(class IDetailLayoutBuilder& DetailBuilder)
{
// sometimes because of order of customization
// this gets called first for the node you'd like to customize
// then the above statement won't work
// so you can mark certain property to have meta data "CustomizeProperty"
// which will trigger below statement
if (OptionalPin.bPropertyIsCustomized)
{
continue;
}
TSharedRef<SWidget> InternalCustomWidget = CreatePropertyWidget(TargetProperty, TargetPropertyHandle.ToSharedRef(), AnimGraphNode->GetClass());
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

View File

@@ -0,0 +1,70 @@
# FoldProperty
- **功能描述:** 在动画蓝图中使得动画节点的某个属性成为FoldProperty。
- **使用位置:** UPROPERTY
- **引擎模块:** AnimationGraph
- **元数据类型:** bool
- **限制类型:** FAnimNode_Base下的属性
- **常用程度:** ★
在动画蓝图中使得动画节点的某个属性成为FoldProperty。
- 在UI表现上PinHiddenByDefault也有同样的效果但是FoldProperty在行为上有别的不同。
- 所谓FoldProperty指的是这些属性往往使被WITH_EDITORONLY_DATA包起来的。记录编辑器状况下的信息。比如FAnimNode_SequencePlayer下的PlayRate数据其就是在编辑器状态的下数据。又或者只是动画蓝图本身的信息不管动画蓝图的多少个实例这些属性的值其实都是同样的。这些属性就适合成为FoldProperty。
- 这些属性需要在节点上编辑但又不想暴露成引脚因此就在形式上和PinHiddenByDefault一样放到右侧的细节面板里。
在FAnimNodeData* FAnimNode_Base::NodeData里存储着该动画节点的所有实例所用到的“Constant/Fold”属性信息。该动画蓝图在游戏里可能有多个实例在这些实例之间都只存一份动画节点的常量信息也只存一份FoldProperty的信息。因此用FoldProperty标记的属性的真实数据是存在TArray<FAnimNodeData> UAnimBlueprintGeneratedClass::AnimNodeData中的。存在Class中其实就是类似CDO的意思了。这么做的显然好处之一是节省内存。
自然的不同的存储方式自然要采用不同的访问方式。因此这些FoldProperty都是采用GET_ANIM_NODE_DATA来访问该数据。
## 测试代码:
```cpp
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = FoldPropertyTest, meta = (FoldProperty))
int32 MyInt_FoldProperty = 123;
```
## 测试结果:
![Untitled](Untitled.png)
## 原理:
编译的时候会把该FoldProperty添加到FoldRecords里。如果这个属性不是动态也没有暴露成引脚连接则会被当作常量。
```cpp
void FAnimBlueprintCompilerContext::GatherFoldRecordsForAnimationNode(const UScriptStruct* InNodeType, FStructProperty* InNodeProperty, UAnimGraphNode_Base* InVisualAnimNode)
{
if(SubProperty->HasMetaData(NAME_FoldProperty))
{
// Add folding candidate
AddFoldedPropertyRecord(InVisualAnimNode, InNodeProperty, SubProperty, bAllPinsExposed, !bAllPinsDisconnected, bAlwaysDynamic);
}
}
void FAnimBlueprintCompilerContext::AddFoldedPropertyRecord(UAnimGraphNode_Base* InAnimGraphNode, FStructProperty* InAnimNodeProperty, FProperty* InProperty, bool bInExposedOnPin, bool bInPinConnected, bool bInAlwaysDynamic)
{
const bool bConstant = !bInAlwaysDynamic && (!bInExposedOnPin || (bInExposedOnPin && !bInPinConnected));
if(!InProperty->HasAnyPropertyFlags(CPF_EditorOnly))
{
MessageLog.Warning(*FString::Printf(TEXT("Property %s on @@ is foldable, but not editor only"), *InProperty->GetName()), InAnimGraphNode);
}
// Create record and add it our lookup map
TSharedRef<IAnimBlueprintCompilationContext::FFoldedPropertyRecord> Record = MakeShared<IAnimBlueprintCompilationContext::FFoldedPropertyRecord>(InAnimGraphNode, InAnimNodeProperty, InProperty, bConstant);
TArray<TSharedRef<IAnimBlueprintCompilationContext::FFoldedPropertyRecord>>& Array = NodeToFoldedPropertyRecordMap.FindOrAdd(InAnimGraphNode);
Array.Add(Record);
// Record it in the appropriate data area
if(bConstant)
{
ConstantPropertyRecords.Add(Record);
}
else
{
MutablePropertyRecords.Add(Record);
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@@ -0,0 +1,84 @@
# GetterContext
- **功能描述:** 继续限定AnimGetter函数在哪个地方才可以使用如果不填则默认都可以用。
- **使用位置:** UFUNCTION
- **引擎模块:** AnimationGraph
- **元数据类型:** string="abc"
- **限制类型:** UAnimInstance及子类的AnimGetter函数
- **关联项:** [AnimGetter](../AnimGetter/AnimGetter.md)
- **常用程度:** ★★
继续限定AnimGetter函数在哪个地方才可以使用如果不填则默认都可以用。
选项有TransitionCustomBlendAnimGraph。
## 源码注释:
```cpp
* A context string can be provided in the GetterContext metadata and can contain any (or none) of the
* following entries separated by a pipe (|)
* Transition - Only available in a transition rule
* AnimGraph - Only available in an animgraph (also covers state anim graphs)
* CustomBlend - Only available in a custom blend graph
```
## 测试代码:
```cpp
UFUNCTION(BlueprintPure, Category = "Animation|Insider", meta = (BlueprintThreadSafe))
float MyGetStateWeight(int32 MachineIndex, int32 StateIndex);
public:
UFUNCTION(BlueprintPure, Category = "Animation|Insider", meta = (BlueprintInternalUseOnly = "true", AnimGetter, GetterContext = "Transition", BlueprintThreadSafe))
float MyGetStateWeight_AnimGetter_OnlyTransition(int32 MachineIndex, int32 StateIndex);
UFUNCTION(BlueprintPure, Category = "Animation|Insider", meta = (BlueprintInternalUseOnly = "true", AnimGetter, GetterContext = "CustomBlend", BlueprintThreadSafe))
float MyGetTransitionTimeElapsed_AnimGetter_OnlyCustomBlend(int32 MachineIndex, int32 TransitionIndex);
```
## 测试效果:
这个图要对比AnimGetter里的图来查看。
关注点一是在AnimGraph里的MyGetStateWeight_AnimGetter_OnlyTransition如果不标GetterContext 则是可以调用的但标上就只能在Transition里调用。同时也发现该函数不能在CustomBlend里调用。
二是在CustomBlend里。操作步骤是在Rule上右侧细节面板改为Custom然后进入CustomBlend的蓝图。在该蓝图下MyGetStateWeight可以调用因为并没有填写GetterContext。而MyGetTransitionTimeElapsed_AnimGetter_OnlyCustomBlend可以开始调用了。
![Untitled](Untitled.png)
## 原理:
判断能否调用的函数如下。
```cpp
bool UK2Node_AnimGetter::IsContextValidForSchema(const UEdGraphSchema* Schema) const
{
if(Contexts.Num() == 0)
{
// Valid in all graphs
return true;
}
for(const FString& Context : Contexts)
{
UClass* ClassToCheck = nullptr;
if(Context == TEXT("CustomBlend"))
{
ClassToCheck = UAnimationCustomTransitionSchema::StaticClass();
}
if(Context == TEXT("Transition"))
{
ClassToCheck = UAnimationTransitionSchema::StaticClass();
}
if(Context == TEXT("AnimGraph"))
{
ClassToCheck = UAnimationGraphSchema::StaticClass();
}
return Schema->GetClass() == ClassToCheck;
}
return false;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

View File

@@ -0,0 +1,67 @@
# NeverAsPin
- **功能描述:** 在动画蓝图中使得动画节点的某个属性总是不暴露出来成为引脚
- **使用位置:** UPROPERTY
- **引擎模块:** Pin
- **元数据类型:** bool
- **限制类型:** FAnimNode_Base
- **关联项:** [PinShownByDefault](../PinShownByDefault/PinShownByDefault.md)
- **常用程度:** ★★★
NeverAsPin源码中并没有用到因为默认情况下就是不支持为引脚所以不加也都一样。
## 测试代码:
```cpp
USTRUCT(BlueprintInternalUseOnly)
struct INSIDEREDITOR_API FAnimNode_MyTestPinShown : public FAnimNode_Base
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PinShownByDefaultTest)
int32 MyInt_NotShown = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PinShownByDefaultTest, meta = (PinShownByDefault))
int32 MyInt_PinShownByDefault = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PinShownByDefaultTest, meta = (AlwaysAsPin))
int32 MyInt_AlwaysAsPin = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PinShownByDefaultTest, meta = (NeverAsPin))
int32 MyInt_NeverAsPin = 123;
};
```
## 测试效果:
MyInt_NeverAsPin只能和右边和默认的属性一样不能显示为引脚。
![PinShown](PinShown.gif)
## 原理:
发现bNeverShow并没有用到因为默认情况下就是不支持为引脚。
```cpp
void FAnimBlueprintNodeOptionalPinManager::GetRecordDefaults(FProperty* TestProperty, FOptionalPinFromProperty& Record) const
{
const UAnimationGraphSchema* Schema = GetDefault<UAnimationGraphSchema>();
// Determine if this is a pose or array of poses
FArrayProperty* ArrayProp = CastField<FArrayProperty>(TestProperty);
FStructProperty* StructProp = CastField<FStructProperty>(ArrayProp ? ArrayProp->Inner : TestProperty);
const bool bIsPoseInput = (StructProp && StructProp->Struct->IsChildOf(FPoseLinkBase::StaticStruct()));
//@TODO: Error if they specified two or more of these flags
const bool bAlwaysShow = TestProperty->HasMetaData(Schema->NAME_AlwaysAsPin) || bIsPoseInput;
const bool bOptional_ShowByDefault = TestProperty->HasMetaData(Schema->NAME_PinShownByDefault);
const bool bOptional_HideByDefault = TestProperty->HasMetaData(Schema->NAME_PinHiddenByDefault);
const bool bNeverShow = TestProperty->HasMetaData(Schema->NAME_NeverAsPin);
const bool bPropertyIsCustomized = TestProperty->HasMetaData(Schema->NAME_CustomizeProperty);
const bool bCanTreatPropertyAsOptional = CanTreatPropertyAsOptional(TestProperty);
Record.bCanToggleVisibility = bCanTreatPropertyAsOptional && (bOptional_ShowByDefault || bOptional_HideByDefault);
Record.bShowPin = bAlwaysShow || bOptional_ShowByDefault;
Record.bPropertyIsCustomized = bPropertyIsCustomized;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

View File

@@ -0,0 +1,19 @@
# OnEvaluate
- **使用位置:** UPROPERTY
- **引擎模块:** AnimationGraph
## 原理:
在源码中发现说明OnEvaluate已经放弃了。
```cpp
// Dynamic value that needs to be wired up and evaluated each frame
const FString& EvaluationHandlerStr = SourcePinProperty->GetMetaData(AnimGraphDefaultSchema->NAME_OnEvaluate);
FName EvaluationHandlerName(*EvaluationHandlerStr);
if (EvaluationHandlerName != NAME_None)
{
// warn that NAME_OnEvaluate is deprecated:
InCompilationContext.GetMessageLog().Warning(*LOCTEXT("OnEvaluateDeprecated", "OnEvaluate meta data is deprecated, found on @@").ToString(), SourcePinProperty);
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

View File

@@ -0,0 +1,82 @@
# PinShownByDefault
- **功能描述:** 在动画蓝图中使得动画节点的某个属性一开始就暴露出来成为引脚,但也可以改变。
- **使用位置:** UPROPERTY
- **引擎模块:** AnimationGraph
- **元数据类型:** bool
- **限制类型:** FAnimNode_Base
- **关联项:** [AlwaysAsPin](../AlwaysAsPin/AlwaysAsPin.md), [NeverAsPin](../NeverAsPin/NeverAsPin.md)
- **常用程度:** ★★★
在动画蓝图中使得动画节点的某个属性一开始就暴露出来成为引脚。
和常规的蓝图不同FAnimNode_Base里面的属性默认是不在节点上显示出来的。因此才需要这个meta显式的指定哪些需要显式。
PinShownByDefault目前只在动画蓝图节点上应用。
相反的可以用PinHiddenByDefault来隐藏属性成为引脚。
## 测试代码:
```cpp
USTRUCT(BlueprintInternalUseOnly)
struct INSIDEREDITOR_API FAnimNode_MyTestPinShown : public FAnimNode_Base
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PinShownByDefaultTest)
int32 MyInt_NotShown = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PinShownByDefaultTest, meta = (PinShownByDefault))
int32 MyInt_PinShownByDefault = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PinShownByDefaultTest, meta = (AlwaysAsPin))
int32 MyInt_AlwaysAsPin = 123;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PinShownByDefaultTest, meta = (NeverAsPin))
int32 MyInt_NeverAsPin = 123;
};
UCLASS()
class INSIDEREDITOR_API UAnimGraphNode_MyTestPinShown : public UAnimGraphNode_Base
{
GENERATED_UCLASS_BODY()
UPROPERTY(EditAnywhere, Category = Settings)
FAnimNode_MyTestPinShown Node;
};
```
## 测试效果:
可见同样的两个属性MyInt_NotShown 默认情况不显示成节点只能在细节面板里编辑。而MyInt_PinShownByDefault默认情况下成为引脚。当PinShownByDefault还可以改变去掉Pin的功能。
![PinShown](PinShown.gif)
## 原理:
源码里唯一用的地方就是在FAnimBlueprintNodeOptionalPinManager其实就是处理动画蓝图节点的Pin如何显示。
```cpp
void FAnimBlueprintNodeOptionalPinManager::GetRecordDefaults(FProperty* TestProperty, FOptionalPinFromProperty& Record) const
{
const UAnimationGraphSchema* Schema = GetDefault<UAnimationGraphSchema>();
// Determine if this is a pose or array of poses
FArrayProperty* ArrayProp = CastField<FArrayProperty>(TestProperty);
FStructProperty* StructProp = CastField<FStructProperty>(ArrayProp ? ArrayProp->Inner : TestProperty);
const bool bIsPoseInput = (StructProp && StructProp->Struct->IsChildOf(FPoseLinkBase::StaticStruct()));
//@TODO: Error if they specified two or more of these flags
const bool bAlwaysShow = TestProperty->HasMetaData(Schema->NAME_AlwaysAsPin) || bIsPoseInput;
const bool bOptional_ShowByDefault = TestProperty->HasMetaData(Schema->NAME_PinShownByDefault);
const bool bOptional_HideByDefault = TestProperty->HasMetaData(Schema->NAME_PinHiddenByDefault);
const bool bNeverShow = TestProperty->HasMetaData(Schema->NAME_NeverAsPin);
const bool bPropertyIsCustomized = TestProperty->HasMetaData(Schema->NAME_CustomizeProperty);
const bool bCanTreatPropertyAsOptional = CanTreatPropertyAsOptional(TestProperty);
Record.bCanToggleVisibility = bCanTreatPropertyAsOptional && (bOptional_ShowByDefault || bOptional_HideByDefault);
Record.bShowPin = bAlwaysShow || bOptional_ShowByDefault;
Record.bPropertyIsCustomized = bPropertyIsCustomized;
}
```