5.4 KiB
Raw Blame History

IsBindableEvent

  • 功能描述: 把一个动态单播委托暴露到UMG蓝图里以绑定相应事件。
  • 使用位置: UPROPERTY
  • 引擎模块: Widget Property
  • 元数据类型: bool
  • 限制类型: UWidget子类里动态单播属性
  • 常用程度: ★★★

把一个动态单播委托暴露到UMG蓝图里以绑定相应事件。

需要注意的点是:

  • 必须是动态委托就是DYNAMIC的那些这样才可以在蓝图里序列化。
  • 动态多播委托DECLARE_DYNAMIC_MULTICAST_DELEGATE默认就可以在UMG里绑定事件因此没有必要加IsBindableEvent。往往也配合加上BlueprintAssignable以便也可以在蓝图里手动绑定。
  • 动态单播委托DECLARE_DYNAMIC_DELEGATE默认是不在UMG里暴露的。但可以加上IsBindableEvent以便可以在其实例的细节面板上绑定。
  • UMG里的控件事件为什么要有多播和单播其实多播和单播除了数量不同以外最大的不同是多播没有返回值。这个例子可以对比UButton下的OnClicked多播事件和UImage下的OnMouseButtonDownEvent单播委托前者是点击的事件已经是个“结果”事件了点击事件可能被多个地方响应因此要设计成多播。而后者的OnMouseButtonDownEvent是鼠标按下的事件有一个重要的逻辑是会根据返回值FEventReply的不同而决定该事件是否继续路由上去因此只能用单播只能绑定一个。

源码例子:

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnButtonClickedEvent);
class UButton : public UContentWidget
{
	UPROPERTY(BlueprintAssignable, Category="Button|Event")
	FOnButtonClickedEvent OnClicked;
}

DECLARE_DYNAMIC_DELEGATE_RetVal_TwoParams(FEventReply, FOnPointerEvent, FGeometry, MyGeometry, const FPointerEvent&, MouseEvent);
class UImage : public UWidget
{
		UPROPERTY(EditAnywhere, Category=Events, meta=( IsBindableEvent="True" ))
		FOnPointerEvent OnMouseButtonDownEvent;
}

测试代码:

UCLASS(BlueprintType)
class INSIDER_API UMyProperty_BindWidget :public UUserWidget
{
public:
	DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnMyClickedMulticastDelegate);

	UPROPERTY(EditAnywhere, BlueprintAssignable, Category = MyEvent)
	FOnMyClickedMulticastDelegate MyClickedMulticastDelegate;

public:
	DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(FString,FOnMyClickedDelegate,int32,MyValue);

	UPROPERTY(EditAnywhere, Category = MyEvent)
	FOnMyClickedDelegate MyClickedDelegate_Default;

	UPROPERTY(EditAnywhere, Category = MyEvent)
	FOnMyClickedDelegate MyClickedEvent;

	UPROPERTY(EditAnywhere, Category = MyEvent, meta = (IsBindableEvent = "True"))
	FOnMyClickedDelegate MyClickedDelegate_Bind;
}

测试结果:

操作步骤是在UMG_BindTest外再创建一个UMG然后让UMG_BindTest成为子控件然后观察其实例上的事件绑定如下图右侧所示。

  • 可以发现动态多播委托默认就会出现可以绑定的+定制化按钮如MyClickedMulticastDelegate。
  • 动态多播委托加上BlueprintAssignable不能加在单播委托上了之后就可以在蓝图里绑定事件如左下侧图。
  • 加了IsBindableEvent 的MyClickedDelegate_Bind可以看见出现了可以Bind的下拉按钮绑定之后可以显示函数名字也可以清除。
  • 没有加IsBindableEvent 的MyClickedDelegate_Default就没有出现在可绑定的按钮你只能在C++里自己绑定了。
  • 没有加IsBindableEvent 的MyClickedEvent因为名字以Event结尾也出现了可绑定的按钮这只能说是当前的一个潜规则。源码注释也说以后会去除。
  • 另外这些委托我虽然都加上了EditAnywhere但其实你也知道这并没法办法编辑。

Untitled

原理:

对于Widget的细节面板引擎也定义了各种Customization。其中对应的就是FBlueprintWidgetCustomization。其针对绑定的部分的代码如下。

代码也很容易理解动态多播委托默认都出现绑定动态单播委托有加IsBindableEvent或者名字以Event结尾就也创建绑定按钮。

PropertyView->RegisterInstancedCustomPropertyLayout(UWidget::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FBlueprintWidgetCustomization::MakeInstance, BlueprintEditorRef, BlueprintEditorRef->GetBlueprintObj()));

void FBlueprintWidgetCustomization::PerformBindingCustomization(IDetailLayoutBuilder& DetailLayout, const TArrayView<UWidget*> Widgets)
{
	static const FName IsBindableEventName(TEXT("IsBindableEvent"));

	bCreateMulticastEventCustomizationErrorAdded = false;
	if ( Widgets.Num() == 1 )
	{
		UWidget* Widget = Widgets[0];
		UClass* PropertyClass = Widget->GetClass();

		for ( TFieldIterator<FProperty> PropertyIt(PropertyClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt )
		{
			FProperty* Property = *PropertyIt;

			if ( FDelegateProperty* DelegateProperty = CastField<FDelegateProperty>(*PropertyIt) )
			{
				//TODO Remove the code to use ones that end with "Event".  Prefer metadata flag.
				if ( DelegateProperty->HasMetaData(IsBindableEventName) || DelegateProperty->GetName().EndsWith(TEXT("Event")) )
				{
					CreateEventCustomization(DetailLayout, DelegateProperty, Widget);
				}
			}
			else if ( FMulticastDelegateProperty* MulticastDelegateProperty = CastField<FMulticastDelegateProperty>(Property) )
			{
				CreateMulticastEventCustomization(DetailLayout, Widget->GetFName(), PropertyClass, MulticastDelegateProperty);
			}
		}
	}
}