vault backup: 2024-10-12 17:19:45
@@ -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中用到。
|
@@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
根据源码的里的逻辑可见,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;
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 194 KiB |
@@ -0,0 +1,9 @@
|
||||
# AnimBlueprintFunction
|
||||
|
||||
- **功能描述:** 标明是动画蓝图里的内部纯存根函数,只在动画蓝图编译时设置
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** AnimationGraph
|
||||
- **元数据类型:** bool
|
||||
- **限制类型:** Anim BP
|
||||
|
||||
只是在内部使用,在动画蓝图编译的时候设置。但是没有在代码里显式的编写。
|
@@ -0,0 +1,78 @@
|
||||
# AnimGetter
|
||||
|
||||
- **功能描述:** 指定UAnimInstance及子类的该函数成为一个AnimGetter函数。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** AnimationGraph
|
||||
- **元数据类型:** bool
|
||||
- **限制类型:** UAnimInstance及子类的函数
|
||||
- **关联项:** [GetterContext](../GetterContext/GetterContext.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
指定UAnimInstance及子类的该函数成为一个AnimGetter函数。
|
||||
|
||||
- 在一些情况下会继承UAnimInstance创建自己的动画蓝图子类,然后里面可以自己做一些优化,或者添加一些自己的功能函数。
|
||||
- 所谓的AnimGetter,其实就是会被UK2Node_AnimGetter识别并包装成该蓝图节点的函数。识别的范围是在UAnimInstance及子类(就是动画蓝图)的C++函数。
|
||||
- AnimGetter还有两个额外功能:一是会自动根据当前上下文填充函数里的AssetPlayerIndex,MachineIndex,StateIndex,TransitionIndex和参数。二是会根据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);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||
分别定义使用了AssetPlayerIndex,MachineIndex,StateIndex,TransitionIndex的AnimGetter函数以及普通蓝图函数作为对比。分别查看在动画蓝图里几个作用域里的用法。
|
||||
|
||||
- 可见在不管什么作用域,普通蓝图函数都可以调用(毕竟没有做Context的检查)。另外AssetPlayerIndex等参数都没有被自动填充,这几乎是没法用的,因为用户其实并不太懂如何去手填这些Index,最好是交给编译器来填充。
|
||||
- 图里高亮的是可以调用的AnimGetter函数。细看的话,可以分析发现规则是只有能正确填充AssetPlayerIndex等参数的才能调用。因此在Transition里能调用的最多,因为这个时候最叶子节点,有动画,又有状态机和Transition节点。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
分析函数上的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);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 620 KiB |
@@ -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不一样了。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
在定制化的时候,根据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;
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 218 KiB |
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
@@ -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
|
||||
}
|
||||
```
|
@@ -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里用到。
|
@@ -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默认创建流程给跳过了。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
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());
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 253 KiB |
@@ -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;
|
||||
```
|
||||
|
||||
## 测试结果:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
编译的时候会把该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);
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 77 KiB |
@@ -0,0 +1,84 @@
|
||||
# GetterContext
|
||||
|
||||
- **功能描述:** 继续限定AnimGetter函数在哪个地方才可以使用,如果不填,则默认都可以用。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** AnimationGraph
|
||||
- **元数据类型:** string="abc"
|
||||
- **限制类型:** UAnimInstance及子类的AnimGetter函数
|
||||
- **关联项:** [AnimGetter](../AnimGetter/AnimGetter.md)
|
||||
- **常用程度:** ★★
|
||||
|
||||
继续限定AnimGetter函数在哪个地方才可以使用,如果不填,则默认都可以用。
|
||||
|
||||
选项有:Transition,CustomBlend,AnimGraph。
|
||||
|
||||
## 源码注释:
|
||||
|
||||
```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可以开始调用了。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
判断能否调用的函数如下。
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 412 KiB |
@@ -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只能和右边和默认的属性一样,不能显示为引脚。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
发现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;
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 194 KiB |
@@ -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);
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 194 KiB |
@@ -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的功能。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
源码里唯一用的地方就是在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;
|
||||
}
|
||||
```
|