4.8 KiB
Raw Blame History

UseComponentPicker

  • 功能描述: 用在ComponentReference属性上使得选取器的列表里展示出Actor属下的Component以便选择。
  • 使用位置: UPROPERTY
  • 引擎模块: Component Property
  • 元数据类型: bool
  • 限制类型: FComponentReferenceFSoftComponentReference
  • 关联项: AllowAnyActor
  • 常用程度: ★★

用在ComponentReference属性上使得选取器的列表里展示出Actor属下的Component以便选择。

  • 默认情况下FComponentReference的Referenced Actor属性展开的选择器列表是让你选择场景里的Actor因此并不会把该Actor下的组件也都显示出来。而ComponentReference下的ComponentName属性需要玩家手动填写。这种方式比较原始也容易出错。
  • 因此加上UseComponentPicker后就可以显示出组件列表来选择。但是又默认限制是当前Actor属下的所有组件不包括场景里其他Actor里的组件。
  • 如果想要进一步把场景里所有Actor下的所有组件都列出来则需要进一步加上AllowAnyActor以扩大筛选范围。
  • ComponentReference的属性类型有两种FComponentReference和FSoftComponentReference二者都对应了FComponentReferenceCustomization。测试代码为简洁就没有列出FSoftComponentReference。

测试代码:

	UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = "UseComponentPickerTest")
	FComponentReference MyComponentReference_NoUseComponentPicker;

	UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = "UseComponentPickerTest", meta = (UseComponentPicker))
	FComponentReference MyComponentReference_UseComponentPicker;

	UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = "UseComponentPicker_AllowAnyActor_Test", meta = (UseComponentPicker,AllowAnyActor))
	FComponentReference MyComponentReference_UseComponentPicker_AllowAnyActor;

测试效果:

  • 可见默认的第一个列出了所有Actor但是ComponentName需要手写。
  • 第二个加上UseComponentPicker后列出了当前Actor下的所有组件但是不能选择到其他Actor的组件。
  • 第三个继续加上AllowAnyActor后列出了所有Actor的所有组件。

UseComponentPicker

原理:

FComponentReference和FSoftComponentReference二者都对应了FComponentReferenceCustomization。看源码可发现用上bUseComponentPicker后会专门创建ClassFilters和ComboBox就是采用不同的类型过滤器和不同的UI来选择组件。否则else分支就是很朴素的结构属性展开编辑。

void FComponentReferenceCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
	PropertyHandle = InPropertyHandle;

	CachedComponent.Reset();
	CachedFirstOuterActor.Reset();
	CachedPropertyAccess = FPropertyAccess::Fail;

	bAllowClear = false;
	bAllowAnyActor = false;
	bUseComponentPicker = PropertyHandle->HasMetaData(NAME_UseComponentPicker);
	bIsSoftReference = false;

	if (bUseComponentPicker)
	{
		FProperty* Property = InPropertyHandle->GetProperty();
		check(CastField<FStructProperty>(Property) &&
				(FComponentReference::StaticStruct() == CastFieldChecked<const FStructProperty>(Property)->Struct ||
				FSoftComponentReference::StaticStruct() == CastFieldChecked<const FStructProperty>(Property)->Struct));

		bAllowClear = !(InPropertyHandle->GetMetaDataProperty()->PropertyFlags & CPF_NoClear);
		bAllowAnyActor = InPropertyHandle->HasMetaData(NAME_AllowAnyActor);
		bIsSoftReference = FSoftComponentReference::StaticStruct() == CastFieldChecked<const FStructProperty>(Property)->Struct;

		BuildClassFilters();
		BuildComboBox();

		InPropertyHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FComponentReferenceCustomization::OnPropertyValueChanged));

		// set cached values
		{
			CachedComponent.Reset();
			CachedFirstOuterActor = GetFirstOuterActor();

			FComponentReference TmpComponentReference;
			CachedPropertyAccess = GetValue(TmpComponentReference);
			if (CachedPropertyAccess == FPropertyAccess::Success)
			{
				CachedComponent = TmpComponentReference.GetComponent(CachedFirstOuterActor.Get());
				if (!IsComponentReferenceValid(TmpComponentReference))
				{
					CachedComponent.Reset();
				}
			}
		}

		HeaderRow.NameContent()
		[
			InPropertyHandle->CreatePropertyNameWidget()
		]
		.ValueContent()
		[
			ComponentComboButton.ToSharedRef()
		]
		.IsEnabled(MakeAttributeSP(this, &FComponentReferenceCustomization::CanEdit));
	}
	else
	{
		HeaderRow.NameContent()
		[
			InPropertyHandle->CreatePropertyNameWidget()
		]
		.ValueContent()
		[
			InPropertyHandle->CreatePropertyValueWidget()
		]
		.IsEnabled(MakeAttributeSP(this, &FComponentReferenceCustomization::CanEdit));
	}
}