vault backup: 2024-10-12 17:19:45
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
# BindWidgetAnim
|
||||
|
||||
- **功能描述:** 指定在C++类中该UWidgetAnimation属性一定要绑定到UMG下的某个动画
|
||||
- **使用位置:** UPROPERTY
|
||||
- **引擎模块:** Widget Property
|
||||
- **元数据类型:** bool
|
||||
- **限制类型:** UWidget子类里UWidgetAnimation属性
|
||||
- **关联项:** [BindWidgetAnimOptional](../BindWidgetAnimOptional/BindWidgetAnimOptional.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
指定在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,跳过网络复制。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
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,一眼就懂。
|
||||
|
||||
```cpp
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
Reference in New Issue
Block a user