5.4 KiB
5.4 KiB
BindWidgetAnim
- 功能描述: 指定在C++类中该UWidgetAnimation属性一定要绑定到UMG下的某个动画
- 使用位置: UPROPERTY
- 引擎模块: Widget Property
- 元数据类型: bool
- 限制类型: UWidget子类里UWidgetAnimation属性
- 关联项: BindWidgetAnimOptional
- 常用程度: ★★★★★
指定在C++类中该UWidgetAnimation属性一定要绑定到UMG下的某个动画。
作用同BindWidget类似,都是用来把C++的属性和BP里的控件或动画赋值绑定起来。但又有一些区别:
- UWidgetAnimation和Widget不同,Widget的属性和控件只要同名就可以自动绑定起来,而UWidgetAnimation就不允许不加BindWidgetAnim而同名,否则会名字冲突报错。这是由于UMG里创建的Widget默认是不创建BP变量的,子控件只是WidgetTree下的一个对象,但是动画是默认会创建BP变量的。因此即使是UMG里先定义动画,然后C++里再定义同名属性,也是会过不了编译的。
- UWidgetAnimation属性必须得是Transient,否则也会报错。我想这是因为UWidgetAnimation自然会在BP里作为子对象序列化,而不需要在C++序列的时候访问到该属性,因此强制Transient以免不小心序列化它。另外UWidgetAnimation只是用作表现,因此其实也会自动的加上CPF_RepSkip,跳过网络复制。
测试代码:
UCLASS(BlueprintType)
class INSIDER_API UMyProperty_BindWidget :public UUserWidget
{
GENERATED_BODY()
UMyProperty_BindWidget(const FObjectInitializer& ObjectInitializer);
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
class UWidgetAnimation* MyAnimation_NotFound;
//UPROPERTY(EditAnywhere, BlueprintReadWrite)
//class UWidgetAnimation* MyAnimation_SameName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, meta = (BindWidgetAnim))
class UWidgetAnimation* MyAnimation_Bind;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, meta = (BindWidgetAnimOptional))
class UWidgetAnimation* MyAnimation_BindOptional;
};
测试效果:
测试过程和BindWidget类似,在C++和UMG中定义不同类型属性和动画对象。可以根据VS里实际对象的值发现:
- MyAnimation_Bind和MyAnimation_BindOptional都自动的绑定了正确的动画对象。
- 没有加BindWidgetAnim的MyAnimation_SameName必须注释掉,否则会和UMG里的MyAnimation_SameName名字冲突。
- 再提一下,不能像Widget里一样先UMG里定义动画,然后再C++定义同名属性,因为WidgetAnimation是一定会创建BP变量的,这是关键不同。
原理:
大致逻辑和BindWidget类似,都是判断属性是否BindWidgetAnim。然后相应的在编译和改名的时候判断。
关于动画变量设置PropertyFlags的逻辑在CreateClassVariablesFromBlueprint里,可以看见加上了4个属性,明确了不要序列化该属性。
而为UWidgetAnimation*属性自动绑定赋值的逻辑在BindAnimationsStatic,一眼就懂。
bool FWidgetBlueprintEditorUtils::IsBindWidgetAnimProperty(const FProperty* InProperty, bool& bIsOptional)
{
if (InProperty)
{
bool bIsBindWidgetAnim = InProperty->HasMetaData("BindWidgetAnim") || InProperty->HasMetaData("BindWidgetAnimOptional");
bIsOptional = InProperty->HasMetaData("BindWidgetAnimOptional");
return bIsBindWidgetAnim;
}
return false;
}
void FWidgetBlueprintCompilerContext::CreateClassVariablesFromBlueprint()
{
for (UWidgetAnimation* Animation : WidgetBP->Animations)
{
FEdGraphPinType WidgetPinType(UEdGraphSchema_K2::PC_Object, NAME_None, Animation->GetClass(), EPinContainerType::None, true, FEdGraphTerminalType());
FProperty* AnimationProperty = CreateVariable(Animation->GetFName(), WidgetPinType);
if ( AnimationProperty != nullptr )
{
const FString DisplayName = Animation->GetDisplayName().ToString();
AnimationProperty->SetMetaData(TEXT("DisplayName"), *DisplayName);
AnimationProperty->SetMetaData(TEXT("Category"), TEXT("Animations"));
AnimationProperty->SetPropertyFlags(CPF_Transient);
AnimationProperty->SetPropertyFlags(CPF_BlueprintVisible);
AnimationProperty->SetPropertyFlags(CPF_BlueprintReadOnly);
AnimationProperty->SetPropertyFlags(CPF_RepSkip);
WidgetAnimToMemberVariableMap.Add(Animation, AnimationProperty);
}
}
}
void FWidgetBlueprintCompilerContext::FinishCompilingClass(UClass* Class)
{
if (!WidgetAnimProperty->HasAnyPropertyFlags(CPF_Transient))
{
const FText BindWidgetAnimTransientError = LOCTEXT("BindWidgetAnimTransient", "The property @@ uses BindWidgetAnim, but isn't Transient!");
MessageLog.Error(*BindWidgetAnimTransientError.ToString(), WidgetAnimProperty);
}
}
void UWidgetBlueprintGeneratedClass::BindAnimationsStatic(UUserWidget* Instance, const TArrayView<UWidgetAnimation*> InAnimations, const TMap<FName, FObjectPropertyBase*>& InPropertyMap)
{
// Note: It's not safe to assume here that the UserWidget class type is a UWidgetBlueprintGeneratedClass!
// - @see InitializeWidgetStatic()
for (UWidgetAnimation* Animation : InAnimations)
{
if (Animation->GetMovieScene())
{
// Find property with the same name as the animation and assign the animation to it.
if (FObjectPropertyBase*const* PropPtr = InPropertyMap.Find(Animation->GetMovieScene()->GetFName()))
{
check(*PropPtr);
(*PropPtr)->SetObjectPropertyValue_InContainer(Instance, Animation);
}
}
}
}