BlueRoseNote/03-UnrealEngine/UI/扩展UProgressBar以实现多重进度条控件.md
2023-06-29 11:55:02 +08:00

209 lines
7.7 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 扩展UProgressBar以实现多重进度条控件
date: 2020-02-20 16:42:36       
tags: Slate
rating: ⭐️
---
## 前言
因为本人想制作一个类似血源诅咒Demo的关系所以需要实现一个类似的进度条。它的不同之处在于血量的进度条会显示实际血量与下次攻击后的最大回复血量。也就是一个进度条显示两个量。
UProgressBar为UMG进度条空间它本质上对Slate组件SProgressBar的封装。负责绑定数据、UI更新。所以我们应该先了解SProgressBar的绘制过程。
## OnPaint
Slate控件的绘制过程在OnPaint函数。查看代码后发现它就是在画一个个盒子并进行剪裁
```
int32 SCustomProgressBar::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
// Used to track the layer ID we will return.
int32 RetLayerId = LayerId;
//获取各种数据与资源
bool bEnabled = ShouldBeEnabled( bParentEnabled );
const ESlateDrawEffect DrawEffects = bEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;
const FSlateBrush* CurrentFillImage = GetFillImage();
const FLinearColor FillColorAndOpacitySRGB(InWidgetStyle.GetColorAndOpacityTint() * FillColorAndOpacity.Get().GetColor(InWidgetStyle) * CurrentFillImage->GetTint(InWidgetStyle));
const FLinearColor ColorAndOpacitySRGB = InWidgetStyle.GetColorAndOpacityTint();
TOptional<float> ProgressFraction = Percent.Get();
FVector2D BorderPaddingRef = BorderPadding.Get();
const FSlateBrush* CurrentBackgroundImage = GetBackgroundImage();
//绘制底层背景
FSlateDrawElement::MakeBox(
OutDrawElements,
RetLayerId++,
AllottedGeometry.ToPaintGeometry(),
CurrentBackgroundImage,
DrawEffects,
InWidgetStyle.GetColorAndOpacityTint() * CurrentBackgroundImage->GetTint( InWidgetStyle )
);
if( ProgressFraction.IsSet() )
{
ECutomProgressBarFillType::Type ComputedBarFillType = BarFillType;
if (GSlateFlowDirection == EFlowDirection::RightToLeft)
{
switch (ComputedBarFillType)
{
case ECutomProgressBarFillType::LeftToRight:
ComputedBarFillType = ECutomProgressBarFillType::RightToLeft;
break;
case ECutomProgressBarFillType::RightToLeft:
ComputedBarFillType = ECutomProgressBarFillType::LeftToRight;
break;
}
}
//以下是进度条内部色块的绘制过程
const float ClampedFraction = FMath::Clamp(ProgressFraction.GetValue(), 0.0f, 1.0f);
switch (ComputedBarFillType)
{
...
//略去部分代码
case ECutomProgressBarFillType::LeftToRight:
default:
{
if (PushTransformedClip(OutDrawElements, AllottedGeometry, BorderPaddingRef, FVector2D(0, 0), FSlateRect(0, 0, ClampedFraction, 1)))
{
// Draw Fill
FSlateDrawElement::MakeBox(
OutDrawElements,
RetLayerId++,
AllottedGeometry.ToPaintGeometry(
FVector2D::ZeroVector,
FVector2D( AllottedGeometry.GetLocalSize().X, AllottedGeometry.GetLocalSize().Y )),
CurrentFillImage,
DrawEffects,
FillColorAndOpacitySRGB
);
OutDrawElements.PopClip();
}
break;
}
//略去部分代码
...
}
return RetLayerId - 1;
}
```
## 在UMG中绑定Tarray类型数据并传递给Slate
在开发过程中Slate部分很顺利。但在UMG的SynchronizeProperties函数中在对Tarray类型的数据进行绑定并传递给Slate控件这一步遇到了问题。
也就是使用OPTIONAL_BINDING_CONVERT与PROPERTY_BINDING宏绑定数据时遇到了问题。因为一开始没有搞明白TAttribute与TOptional是啥玩意。
TAttribute为Slate包装属性用的模板类TOptional是类似c++17 std::optional的模板类。
使用这两个宏还需要声明一些函数与委托。下面将一一介绍:
### OPTIONAL_BINDING_CONVERT
```
TAttribute<TOptional<TArray<float> >> PercentBinding=OPTIONAL_BINDING_CONVERT(TArray<float>, PercentArray, TOptional<TArray<float>>, ConvertFloatToOptionalFloatArray);
```
因为本人使用的变量名为PercentArray类型为TArray<float>所以需要声明返回TArray<float>类型的委托。
```
DECLARE_DYNAMIC_DELEGATE_RetVal(TArray<float>, FGetFloatArray);
```
并且委托名为PercentArrayDelegate。也就是变量名+“Delegate”
```
UPROPERTY()
FGetFloatArray PercentArrayDelegate;
```
另外需要实现函数ConvertFloatToOptionalFloatArray
```
TOptional<TArray<float>> ConvertFloatToOptionalFloatArray(TAttribute<TArray<float>> InFloatArray) const
{
return InFloatArray.Get();
}
```
### PROPERTY_BINDING
```
TAttribute<TArray<FSlateColor>> FillColorAndOpacityBinding = PROPERTY_BINDING(TArray<FSlateColor>, FillColorAndOpacityArray);
```
因为本人使用的变量名为FillColorAndOpacityArray类型为TArray<FSlateColor>但需要声明的委托为返回TArray<FLinearColor>类型。
```
DECLARE_DYNAMIC_DELEGATE_RetVal(TArray<FLinearColor>, FGetLinearColorArray);
```
并且委托名为FillColorAndOpacityArrayDelegate。也就是变量名+“Delegate”
```
UPROPERTY()
FGetLinearColorArray FillColorAndOpacityArrayDelegate;
```
另外还需要在类内使用PROPERTY_BINDING_IMPLEMENTATION宏
```
PROPERTY_BINDING_IMPLEMENTATION(TArray<FSlateColor>, FillColorAndOpacityArray);
```
## 其他主要操作
向Slate传递数据
```
void UMultipleProgressBar::SynchronizeProperties()
{
Super::SynchronizeProperties();
TAttribute< TOptional<TArray<float> >> PercentBinding=OPTIONAL_BINDING_CONVERT(TArray<float>, PercentArray, TOptional<TArray<float>>, ConvertFloatToOptionalFloatArray);
TAttribute<TArray<FSlateColor>> FillColorAndOpacityBinding = PROPERTY_BINDING(TArray<FSlateColor>, FillColorAndOpacityArray);
MyProgressBar->SetStyle(&WidgetStyle);
MyProgressBar->SetBarFillType(BarFillType);
MyProgressBar->SetBorderPadding(BorderPadding);
MyProgressBar->SetPercentArray(PercentBinding);
MyProgressBar->SetFillColorAndOpacityArray(FillColorAndOpacityBinding);
}
```
实现数据设置函数
```
UMultipleProgressBar::SetPercentArray
UMultipleProgressBar::SetFillColorAndOpacityArray
SMultipleProgressBar::SetPercentArray
SMultipleProgressBar::SetFillColorAndOpacityArray
SMultipleProgressBar::Construct
```
OnPaint中的绘制逻辑
```
int32 SMultipleProgressBar::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
...
//略去部分代码
//从传递进来的数据中取得Percent与Color数组
TArray<float> PercentArrayList = PercentArray.Get().GetValue();
TArray<FSlateColor> FillColorAndOpacityArrayList = FillColorAndOpacityArray.Get();
while (FillColorAndOpacityArrayList.Num() < PercentArrayList.Num())
{
FillColorAndOpacityArrayList.Add(FSlateColor());
}
for (int i = 0; i < PercentArrayList.Num(); i++)
{
//在循环中获取每个Percent与color最后进行绘制
const float ClampedFraction = FMath::Clamp(PercentArrayList[i], 0.0f, 1.0f);
const FLinearColor FillColorAndOpacitySRGB = (InWidgetStyle.GetColorAndOpacityTint() * FillColorAndOpacityArrayList[i].GetColor(InWidgetStyle) * CurrentFillImage->GetTint(InWidgetStyle));
switch (ComputedBarFillType)
{
case EMultipleProgressBarFillType::RightToLeft:
...
case EMultipleProgressBarFillType::FillFromCenter:
...
case EMultipleProgressBarFillType::TopToBottom:
...
case EMultipleProgressBarFillType::BottomToTop:
...
case EMultipleProgressBarFillType::LeftToRight:
default:
...
}
}
//略去部分代码
...
```
## 结语
具体操作的可以参考我的写的插件https://github.com/blueroseslol/BRPlugins
感觉有帮助的请给我的项目Star。