当前位置: 首页 > news >正文

UE4运用C++和框架开发坦克大战教程笔记(十八)(第55~57集)

UE4运用C++和框架开发坦克大战教程笔记(十八)(第55~57集)

  • 55. UI 进入退出动画
    • HideOther 面板出现时隐藏其他面板
    • 添加面板出现和收起的动画效果
    • 编写遮罩管理器前的准备
  • 56. 弹窗进入界面
  • 57. UI 显示隐藏与遮罩转移
    • 完善遮罩管理器

55. UI 进入退出动画

HideOther 面板出现时隐藏其他面板

我们先前写的 “根据面板类型 PanelShowType 采用不同的首次进入界面方法”,只写了对于 DoNothing 面板类型的首次进入界面逻辑,接下来我们来补全 HideOther 面板类型的相关逻辑,Reverse 的留到后面再写。

HideOther 面板类型的进入界面后会隐藏同 Level 下的其他面板(不包括弹窗),如果进入的是 Level_All 层级,则隐藏所有层级的其他面板(不包括弹窗)。

DDFrameWidget.cpp

void UDDFrameWidget::EnterPanelHideOther(UCanvasPanel* WorkLayout, UDDPanelWidget* PanelWidget)
{// 将显示组的同一层级的其他对象都隐藏,如果是 Level_All 就全部隐藏,Level_All 优先级高for (TMap<FName, UDDPanelWidget*>::TIterator It(ShowPanelGroup); It; ++It) {if (PanelWidget->UINature.LayoutLevel == ELayoutLevel::Level_All || PanelWidget->UINature.LayoutLevel == It.Value()->UINature.LayoutLevel) {It.Value()->PanelHidden();}}// 添加 UI 面板到 LayoutUCanvasPanelSlot* PanelSlot = WorkLayout->AddChildToCanvas(PanelWidget);PanelSlot->SetAnchors(PanelWidget->UINature.Anchors);PanelSlot->SetOffsets(PanelWidget->UINature.Offsets);// 将 UI 面板添加到显示组ShowPanelGroup.Add(PanelWidget->GetObjectName(), PanelWidget);// 调用进入界面生命周期函数PanelWidget->PanelEnter();
}void UDDFrameWidget::EnterPanelHideOther(UOverlay* WorkLayout, UDDPanelWidget* PanelWidget)
{// 将显示组的同一层级的其他对象都隐藏,如果是 Level_All 就全部隐藏,Level_All 优先级高for (TMap<FName, UDDPanelWidget*>::TIterator It(ShowPanelGroup); It; ++It) {if (PanelWidget->UINature.LayoutLevel == ELayoutLevel::Level_All || PanelWidget->UINature.LayoutLevel == It.Value()->UINature.LayoutLevel) {It.Value()->PanelHidden();}}// 添加到 UOverlayUOverlaySlot* PanelSlot = WorkLayout->AddChildToOverlay(PanelWidget);PanelSlot->SetHorizontalAlignment(PanelWidget->UINature.HAlign);PanelSlot->SetVerticalAlignment(PanelWidget->UINature.VAlign);// 将 UI 面板添加到显示组ShowPanelGroup.Add(PanelWidget->GetObjectName(), PanelWidget);// 调用进入界面生命周期函数PanelWidget->PanelEnter();
}

为了测试 HideOther 面板类型的面板首次进入界面,我们使用协程方法来安排各面板的出场顺序。等下会新建一个蓝图界面 BigMapPanel,并且将其设置为 HideOther 面板类型。

RCGameUIFrame.h

public:// 协程方法,用于安排面板出现顺序DDCoroTask* UIProcess();

RCGameUIFrame.cpp

void URCGameUIFrame::DDInit()
{AddToViewport();// 将显示面板代码放到协程方法里,此处替换为启动协程StartCoroutine("UIProcess", UIProcess());
}DDCoroTask* URCGameUIFrame::UIProcess()
{DDCORO_PARAM(URCGameUIFrame);#include DDCORO_BEGIN()D->ShowUIPanel("StatePanel");D->ShowUIPanel("MiniMapPanel");#include DDYIELD_READY()DDYIELD_RETURN_SECOND(10.f);	// 挂起 10 秒D->ShowUIPanel("BigMapPanel");#include DDCORO_END()
}

编译后,在 Blueprint/UIFrame 下创建一个 Widget Blueprint,命名为 BigMapPanel。打开界面并将其父类改成 RCBigMapPanel。随后将界面修改如下:

在这里插入图片描述
来到 HUDData,给 Class Wealth Data 添加一个元素如下:

在这里插入图片描述
运行游戏,10 秒后大地图面板会显示,原本界面上的状态栏和小地图面板会隐藏。(这里笔者为了方便读者查看效果,换了一张比较明显的图,后面课程也会要求替换)

(注:UI 框架的动图,笔者都会缩短挂起时间或稍作剪辑,所以从动图里看到会觉得变化比较快)

在这里插入图片描述

添加面板出现和收起的动画效果

接下来我们实现一下面板的出现和收起的动画效果。由于动画是在 UMG 里面做的,所以我们在面板类声明两个蓝图实现的方法,供 C++ 代码调用蓝图动画节点。

DDPanelWidget.h

public:// 动画回调函数,返回的 float 是动画时长UFUNCTION(BlueprintImplementableEvent)float DisplayEnterMovie();UFUNCTION(BlueprintImplementableEvent)float DisplayLeaveMovie();protected:// 隐藏 UI 面板void SetSelfHidden();protected:// 隐藏动画任务名static FName PanelHiddenName;

在面板显示和收起的方法里加入调用动画的语句;并且让面板收起动画播放完毕后再隐藏面板(通过延时方法实现)。

DDPanelWidget.cpp

FName UDDPanelWidget::PanelHiddenName(TEXT("PanelHiddenTask"));void UDDPanelWidget::PanelEnter()
{SetVisibility(ESlateVisibility::Visible);// 调用进入界面动画DisplayEnterMovie();
}void UDDPanelWidget::PanelDisplay()
{SetVisibility(ESlateVisibility::Visible);// 调用进入界面动画DisplayEnterMovie();
}void UDDPanelWidget::PanelHidden()
{// 修改原本代码如下// 运行完移出界面动画后调用隐藏函数InvokeDelay(PanelHiddenName, DisplayLeaveMovie(), this, &UDDPanelWidget::SetSelfHidden);
}void UDDPanelWidget::SetSelfHidden()
{SetVisibility(ESlateVisibility::Hidden);
}

来到 StatePanel 蓝图界面,给它添加 UI 动画如下:(两个箭头指的是关键帧)

在这里插入图片描述
然后在其蓝图节点编辑界面重写 DisplayEnterMovie()DisplayLeaveMovie() 节点:

在这里插入图片描述
运行游戏,此时左上角状态栏会有移入动画,10 秒后大地图面板出现,状态栏播放移出动画。

在这里插入图片描述

编写遮罩管理器前的准备

Reverse 面板类型一般是弹窗使用的,在写弹窗首次进入界面的逻辑之前我们先考虑写一下遮罩管理器,因为弹窗跟遮罩是同时出现的,遮罩用于覆盖其他界面的可视性以及可交互性。

此处就先添加两种布局类型的遮罩激活方法和遮罩移除方法,一共 3 个方法。后续的留到后面课程继续补充。

DDFrameWidget.h

protected:// 激活遮罩,第一个参数是保存遮罩的父控件,第二个参数是遮罩透明度void ActiveMask(UCanvasPanel* WorkLayout, EPanelLucencyType LucencyType);void ActiveMask(UOverlay* WorkLayout, EPanelLucencyType LucencyType);// 将 MaskPanel 移出,传入的 Layout 如果不为空,说明 MaskPanel 准备添加到这个 Layoutvoid RemoveMaskPanel(UPanelWidget* WorkLayout = NULL);

DDFrameWidget.cpp

void UDDFrameWidget::ActiveMask(UCanvasPanel* WorkLayout, EPanelLucencyType LucencyType)
{
}void UDDFrameWidget::ActiveMask(UOverlay* WorkLayout, EPanelLucencyType LucencyType)
{
}void UDDFrameWidget::RemoveMaskPanel(UPanelWidget* WorkLayout)
{// 获取遮罩当前父控件UPanelWidget* MaskParent = MaskPanel->GetParent();if (MaskParent) {// 比较当前父控件与将要插入的父控件是否相同,当前父控件的子控件为 1if (MaskParent != WorkLayout && MaskParent->GetChildrenCount() == 1) {MaskParent->RemoveFromParent();UCanvasPanel* ParentCanvas = Cast<UCanvasPanel>(MaskParent);UOverlay* ParentOverlay = Cast<UOverlay>(MaskParent);if (ParentCanvas) {ActiveCanvas.Remove(ParentCanvas);UnActiveCanvas.Push(ParentCanvas);}else if (ParentOverlay) {ActiveOverlay.Remove(ParentOverlay);UnActiveOverlay.Push(ParentOverlay);}}// 将遮罩从父级移除MaskPanel->RemoveFromParent();}
}

56. 弹窗进入界面

继续补充遮罩管理器的逻辑。

补全 ActiveMask() 的代码,因为遮罩只有一个,所以每次激活遮罩前都要移除遮罩。

接下来就是补充 Reverse 面板类型的弹窗首次进入界面的逻辑。我们先前声明了一个弹窗栈 PopPanelStack,如果两个弹窗一前一后地出现在界面上,那么先进入的弹窗会进入冻结状态,不允许被操作。

DDFrameWidget.cpp

void UDDFrameWidget::EnterPanelReverse(UCanvasPanel* WorkLayout, UDDPanelWidget* PanelWidget)
{// 把栈内最后一个节点冻结if (PopPanelStack.Num() > 0) {TArray<UDDPanelWidget*> PanelStack;PopPanelStack.GenerateValueArray(PanelStack);PanelStack[PanelStack.Num() - 1]->PanelFreeze();}// 激活遮罩ActiveMask(WorkLayout, PanelWidget->UINature.PanelLucencyType);// 添加弹窗到界面UCanvasPanelSlot* PanelSlot = WorkLayout->AddChildToCanvas(PanelWidget);PanelSlot->SetAnchors(PanelWidget->UINature.Anchors);PanelSlot->SetOffsets(PanelWidget->UINature.Offsets);// 添加弹窗到栈,并且运行进入生命周期函数PopPanelStack.Add(PanelWidget->GetObjectName(), PanelWidget);PanelWidget->PanelEnter();
}void UDDFrameWidget::EnterPanelReverse(UOverlay* WorkLayout, UDDPanelWidget* PanelWidget)
{// 把栈内最后一个节点冻结if (PopPanelStack.Num() > 0) {TArray<UDDPanelWidget*> PanelStack;PopPanelStack.GenerateValueArray(PanelStack);PanelStack[PanelStack.Num() - 1]->PanelFreeze();}// 激活遮罩ActiveMask(WorkLayout, PanelWidget->UINature.PanelLucencyType);// 添加弹窗到界面UOverlaySlot* PanelSlot = WorkLayout->AddChildToOverlay(PanelWidget);PanelSlot->SetPadding(PanelWidget->UINature.Offsets);PanelSlot->SetHorizontalAlignment(PanelWidget->UINature.HAlign);PanelSlot->SetVerticalAlignment(PanelWidget->UINature.VAlign);// 添加弹窗到栈,并且运行进入生命周期函数PopPanelStack.Add(PanelWidget->GetObjectName(), PanelWidget);PanelWidget->PanelEnter();
}void UDDFrameWidget::ActiveMask(UCanvasPanel* WorkLayout, EPanelLucencyType LucencyType)
{// 移出遮罩RemoveMaskPanel(WorkLayout);// 添加遮罩到新的父控件UCanvasPanelSlot* MaskSlot = WorkLayout->AddChildToCanvas(MaskPanel);MaskSlot->SetAnchors(FAnchors(0.f, 0.f, 1.f, 1.f));MaskSlot->SetOffsets(FMargin(0.f, 0.f, 0.f, 0.f));// 根据透明类型设置透明度switch (LucencyType) {case EPanelLucencyType::Lucency:MaskPanel->SetVisibility(ESlateVisibility::Visible);MaskPanel->SetColorAndOpacity(NormalLucency);break;case EPanelLucencyType::Translucence:MaskPanel->SetVisibility(ESlateVisibility::Visible);MaskPanel->SetColorAndOpacity(TranslucenceLucency);break;case EPanelLucencyType::ImPenetrable:MaskPanel->SetVisibility(ESlateVisibility::Visible);MaskPanel->SetColorAndOpacity(ImPenetrableLucency);break;case EPanelLucencyType::Penetrate:MaskPanel->SetVisibility(ESlateVisibility::Hidden);MaskPanel->SetColorAndOpacity(NormalLucency);break;}
}void UDDFrameWidget::ActiveMask(UOverlay* WorkLayout, EPanelLucencyType LucencyType)
{// 移出遮罩RemoveMaskPanel(WorkLayout);// 添加遮罩到新的父控件UOverlaySlot* MaskSlot = WorkLayout->AddChildToOverlay(MaskPanel);MaskSlot->SetPadding(FMargin(0.f, 0.f, 0.f, 0.f));MaskSlot->SetHorizontalAlignment(HAlign_Fill);MaskSlot->SetVerticalAlignment(VAlign_Fill);// 根据透明类型设置透明度switch (LucencyType) {case EPanelLucencyType::Lucency:MaskPanel->SetVisibility(ESlateVisibility::Visible);MaskPanel->SetColorAndOpacity(NormalLucency);break;case EPanelLucencyType::Translucence:MaskPanel->SetVisibility(ESlateVisibility::Visible);MaskPanel->SetColorAndOpacity(TranslucenceLucency);break;case EPanelLucencyType::ImPenetrable:MaskPanel->SetVisibility(ESlateVisibility::Visible);MaskPanel->SetColorAndOpacity(ImPenetrableLucency);break;case EPanelLucencyType::Penetrate:MaskPanel->SetVisibility(ESlateVisibility::Hidden);MaskPanel->SetColorAndOpacity(NormalLucency);break;}
}

接下来验证一下弹窗面板首次显示在界面上的功能。来到 RCGameUIFrame 调整一下协程方法里的调用顺序。

RCGameUIFrame.cpp

// 将协程方法修改如下
DDCoroTask* URCGameUIFrame::UIProcess()
{DDCORO_PARAM(URCGameUIFrame);#include DDCORO_BEGIN()D->ShowUIPanel("StatePanel");D->ShowUIPanel("MiniMapPanel");#include DDYIELD_READY()DDYIELD_RETURN_SECOND(10.f);D->ShowUIPanel("MenuPanel");#include DDYIELD_READY()DDYIELD_RETURN_SECOND(10.f);D->ShowUIPanel("OptionPanel");#include DDCORO_END()
}

编译后,在 Blueprint/UIFrame 下创建两个 Widget Blueprint,分别取名为 MenuPanelOptionPanel

将 MenuPanel 的父类更改为 RCMenuPanel;OptionPanel 的父类更改为 RCOptionMenu。

将 MenuPanel 更改界面如下:(对于弹窗类来说,面板层级是没有效果的,所以设为 Level 0)

在这里插入图片描述
将 OptionPanel 更改界面如下:

在这里插入图片描述
来到 HUDData,给 Class Wealth Data 添加两个元素如下:

在这里插入图片描述
运行游戏,十秒后可以看到 MenuPanel 出现,并且鼠标可以点击到按钮;再十秒后可以看到 OptionPanel 出现,鼠标可以拖动 Slider 的游标(需准确拖动,否则鼠标会消失,因为目前暂时没有设置好鼠标显示),并且此时 MenuPanel 的按钮无法互动。

在这里插入图片描述

最后本集课程结束之前会先给隐藏 UI 和显示 UI 的功能声明方法,不过为了减少重复部分我将笔记内容放到下一节课里。

57. UI 显示隐藏与遮罩转移

之前我们写的 DoEnterUIPanel() 是用于面板首次显示在界面上的,如果后续面板隐藏后重新显示,那就要用到 DoShowUIPanel(),那么接下来我们需要编写隐藏 UI 面板和重新显示 UI 面板的逻辑。

DDFrameWidget.h

public:// 隐藏 UIUFUNCTION()void HideUIPanel(FName PanelName);protected:// 显示 UIvoid ShowPanelDoNothing(UDDPanelWidget* PanelWidget);void ShowPanelHideOther(UDDPanelWidget* PanelWidget);void ShowPanelReverse(UDDPanelWidget* PanelWidget);// 隐藏 UIvoid HidePanelDoNothing(UDDPanelWidget* PanelWidget);void HidePanelHideOther(UDDPanelWidget* PanelWidget);void HidePanelReverse(UDDPanelWidget* PanelWidget);

DDFrameWidget.cpp

void UDDFrameWidget::DoShowUIPanel(FName PanelName)
{// 从全部组获取对象UDDPanelWidget* PanelWidget = *AllPanelGroup.Find(PanelName);// 根据 UI 面板类型调用不同的显示方法switch (PanelWidget->UINature.PanelShowType) {case EPanelShowType::DoNothing:ShowPanelDoNothing(PanelWidget);break;case EPanelShowType::HideOther:ShowPanelHideOther(PanelWidget);break;case EPanelShowType::Reverse:ShowPanelReverse(PanelWidget);break;}
}void UDDFrameWidget::ShowPanelDoNothing(UDDPanelWidget* PanelWidget)
{// 添加 UI 面板到显示组ShowPanelGroup.Add(PanelWidget->GetObjectName(), PanelWidget);PanelWidget->PanelDisplay();
}void UDDFrameWidget::ShowPanelHideOther(UDDPanelWidget* PanelWidget)
{// 将显示组的同一层级的其他对象都隐藏,如果是 Level_All 就全部隐藏,Level_All 优先级高for (TMap<FName, UDDPanelWidget*>::TIterator It(ShowPanelGroup); It; ++It) {if (PanelWidget->UINature.LayoutLevel == ELayoutLevel::Level_All || PanelWidget->UINature.LayoutLevel == It.Value()->UINature.LayoutLevel) {It.Value()->PanelHidden();}}// 添加到显示组ShowPanelGroup.Add(PanelWidget->GetObjectName(), PanelWidget);PanelWidget->PanelDisplay();
}// 弹窗的显示和隐藏留到后面再写
void UDDFrameWidget::ShowPanelReverse(UDDPanelWidget* PanelWidget)
{
}void UDDFrameWidget::HideUIPanel(FName PanelName)
{// 判断 UI 面板是否在显示组或者弹窗栈if (!ShowPanelGroup.Contains(PanelName) && !PopPanelStack.Contains(PanelName))return;// 获取 UI 面板UDDPanelWidget* PanelWidget = *AllPanelGroup.Find(PanelName);// 根据 UI 面板类型调用不同的隐藏方法switch (PanelWidget->UINature.PanelShowType) {case EPanelShowType::DoNothing:HidePanelDoNothing(PanelWidget);break;case EPanelShowType::HideOther:HidePanelHideOther(PanelWidget);break;case EPanelShowType::Reverse:HidePanelReverse(PanelWidget);break;}
}void UDDFrameWidget::HidePanelDoNothing(UDDPanelWidget* PanelWidget)
{// 从显示组移除ShowPanelGroup.Remove(PanelWidget->GetObjectName());// 运行隐藏生命周期PanelWidget->PanelHidden();
}void UDDFrameWidget::HidePanelHideOther(UDDPanelWidget* PanelWidget)
{// 从显示组移除ShowPanelGroup.Remove(PanelWidget->GetObjectName());// 显示同一层级下的其他 UI 面板,如果该面板是 Level_All 层级,显示所有显示组的面板for (TMap<FName, UDDPanelWidget*>::TIterator It(ShowPanelGroup); It; ++It) {if (PanelWidget->UINature.LayoutLevel == ELayoutLevel::Level_All || PanelWidget->UINature.LayoutLevel == It.Value()->UINature.LayoutLevel)It.Value()->PanelDisplay();}// 运行隐藏生命周期PanelWidget->PanelHidden();
}void UDDFrameWidget::HidePanelReverse(UDDPanelWidget* PanelWidget)
{
}

接下来为了测试,我们调整一下 RCGameUIFrame 里面协程方法里的调用逻辑。

同时为了方便测试 UI,我们把输入模式改成仅对 UI 生效,并且显示鼠标。

RCGameUIFrame.cpp

void URCGameUIFrame::DDInit()
{FInputModeUIOnly InputMode;InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);UDDCommon::Get()->GetController()->bShowMouseCursor = true;UDDCommon::Get()->GetController()->SetInputMode(InputMode);}// 修改协程方法如下
DDCoroTask* URCGameUIFrame::UIProcess()
{DDCORO_PARAM(URCGameUIFrame);#include DDCORO_BEGIN()D->ShowUIPanel("StatePanel");D->ShowUIPanel("MiniMapPanel");#include DDYIELD_READY()DDYIELD_RETURN_SECOND(6.f);		// 缩短时间//D->ShowUIPanel("MenuPanel");D->HideUIPanel("StatePanel");#include DDYIELD_READY()DDYIELD_RETURN_SECOND(6.f);//D->ShowUIPanel("OptionPanel");D->ShowUIPanel("StatePanel");#include DDYIELD_READY()DDYIELD_RETURN_SECOND(6.f);D->ShowUIPanel("BigMapPanel");#include DDYIELD_READY()DDYIELD_RETURN_SECOND(6.f);D->HideUIPanel("BigMapPanel");#include DDYIELD_READY()DDYIELD_RETURN_SECOND(6.f);D->ShowUIPanel("BigMapPanel");#include DDCORO_END()
}

编译后,将 BigMapPanel 的图片换成比较深色的图片,方便观察。

在这里插入图片描述
运行游戏,可见界面上有状态栏和小地图,
6 秒后状态栏收起,小地图无变化;
再 6 秒后状态栏出现;
再 6 秒后状态栏收起,小地图消失,出现大地图;
再 6 秒后大地图消失,状态栏和小地图重新出现。
最后再过 6 秒,状态栏收起、小地图消失,大地图重新出现,

在这里插入图片描述

完善遮罩管理器

在写弹窗的显示和隐藏之前,我们再完善一下之前写的遮罩管理器。

根据逻辑来说,我们设定只能隐藏掉当前界面上层级最靠前的弹窗。之前写的弹窗栈 PopPanelStack 可以让我们知道当前最靠前的弹窗是哪个。

前面我们说到过一前一后出现的两个弹窗,先出现的弹窗 1 会被冻结。那么按道理来说,如果要隐藏后出现的弹窗 2 的话,那么我们先隐藏掉这个弹窗 2,然后再移动遮罩到先出现的弹窗 1 的层级底下。

不过可惜的是,UE4 没有提供这样便捷地将某个控件移动到指定层级下面的功能。所以我们只能在隐藏掉弹窗 2 后,将当前父面板下、比弹窗 1 更高层级的所有面板和弹窗 1 临时保存在一个数组里,随后将它们从父面板移除,然后将遮罩添加进父面板里最靠前的层级,最后再将所有临时保存的面板一个个按顺序地放进父面板(置于遮罩的层级之上)。

DDFrameWidget.h

protected:// 转移遮罩,将遮罩放置在传入的 UI 面板的下一层void TransferMask(UDDPanelWidget* PanelWidget);

DDFrameWidget.cpp

void UDDFrameWidget::TransferMask(UDDPanelWidget* PanelWidget)
{// 临时保存 PanelWidget 以及它上层的所有 UI 面板TArray<UDDPanelWidget*> AbovePanelStack;// 临时保存 PanelWidget 以及它上层的所有 UI 面板的布局数据TArray<FUINature> AboveNatureStack;// 区分布局if (PanelWidget->UINature.LayoutType == ELayoutType::Canvas) {UCanvasPanel* WorkLayout = Cast<UCanvasPanel>(PanelWidget->GetParent());int32 StartOrder = WorkLayout->GetChildIndex(PanelWidget);for (int i = StartOrder; i < WorkLayout->GetChildrenCount(); ++i) {UDDPanelWidget* TempPanelWidget = Cast<UDDPanelWidget>(WorkLayout->GetChildAt(i));// 如果不是 DDPanelWidgetif (!TempPanelWidget)continue;// 保存 UI 面板以及布局数据AbovePanelStack.Push(TempPanelWidget);FUINature TempUINature;UCanvasPanelSlot* TempPanelSlot = Cast<UCanvasPanelSlot>(TempPanelWidget);TempUINature.Anchors = TempPanelSlot->GetAnchors();TempUINature.Offsets = TempPanelSlot->GetOffsets();AboveNatureStack.Push(TempUINature);}// 循环移除上层 UI 面板for (int i = 0; i < AbovePanelStack.Num(); ++i)AbovePanelStack[i]->RemoveFromParent();// 添加遮罩到新的父控件UCanvasPanelSlot* MaskSlot = WorkLayout->AddChildToCanvas(MaskPanel);MaskSlot->SetAnchors(FAnchors(0.f, 0.f, 1.f, 1.f));MaskSlot->SetOffsets(FMargin(0.f, 0.f, 0.f, 0.f));// 根据透明类型设置透明度switch (PanelWidget->UINature.PanelLucencyType) {case EPanelLucencyType::Lucency:MaskPanel->SetVisibility(ESlateVisibility::Visible);MaskPanel->SetColorAndOpacity(NormalLucency);break;case EPanelLucencyType::Translucence:MaskPanel->SetVisibility(ESlateVisibility::Visible);MaskPanel->SetColorAndOpacity(TranslucenceLucency);break;case EPanelLucencyType::ImPenetrable:MaskPanel->SetVisibility(ESlateVisibility::Visible);MaskPanel->SetColorAndOpacity(ImPenetrableLucency);break;case EPanelLucencyType::Penetrate:MaskPanel->SetVisibility(ESlateVisibility::Hidden);MaskPanel->SetColorAndOpacity(NormalLucency);break;}// 把刚才移除的 UI 面板按顺序重新添加到布局控件for (int i = 0; i < AbovePanelStack.Num(); ++i) {UCanvasPanelSlot* PanelSlot = WorkLayout->AddChildToCanvas(AbovePanelStack[i]);PanelSlot->SetAnchors(AboveNatureStack[i].Anchors);PanelSlot->SetOffsets(AboveNatureStack[i].Offsets);}}// 对于 Overlay 布局类型的面板遮罩转移留到下一节课写else {}
}

剩余的代码留到下一节课。

相关文章:

  • 【自然语言处理-工具篇】spaCy<2>--模型的使用
  • 【从零开始学设计模式】第八章_桥接模式
  • 网络的基本概念和socket编程
  • 07-Java桥接模式 ( Bridge Pattern )
  • PYthon进阶--网页采集器(基于百度搜索的Python3爬虫程序)
  • 引入BertTokenizer出现OSError: Can‘t load tokenizer for ‘bert-base-uncased‘.
  • G口大流量服务器选择的关键点有哪些?
  • arm 汇编积累
  • Java实现教学资源共享平台 JAVA+Vue+SpringBoot+MySQL
  • C语言第二十三弹---指针(七)
  • Android---PermissionX实现动态权限申请
  • vscode开发FPGA(0)--windows平台搭建
  • JS逆向进阶篇【去哪儿旅行登录】【上篇】
  • 意外删除照片数据?恢复照片数据的 10 大照片恢复工具方法
  • 2024/2/12 图的基础知识 2
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • [case10]使用RSQL实现端到端的动态查询
  • 【RocksDB】TransactionDB源码分析
  • Apache Spark Streaming 使用实例
  • express.js的介绍及使用
  • Git学习与使用心得(1)—— 初始化
  • Javascript Math对象和Date对象常用方法详解
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • leetcode-27. Remove Element
  • python 学习笔记 - Queue Pipes,进程间通讯
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • 大型网站性能监测、分析与优化常见问题QA
  • 将回调地狱按在地上摩擦的Promise
  • 可能是历史上最全的CC0版权可以免费商用的图片网站
  • 前端自动化解决方案
  • 如何使用 JavaScript 解析 URL
  • 微信小程序开发问题汇总
  • 学习使用ExpressJS 4.0中的新Router
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 用Python写一份独特的元宵节祝福
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 追踪解析 FutureTask 源码
  • 整理一些计算机基础知识!
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • ​虚拟化系列介绍(十)
  • # MySQL server 层和存储引擎层是怎么交互数据的?
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #vue3 实现前端下载excel文件模板功能
  • #单片机(TB6600驱动42步进电机)
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (Ruby)Ubuntu12.04安装Rails环境
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (动态规划)5. 最长回文子串 java解决
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (附源码)ssm捐赠救助系统 毕业设计 060945
  • (三)Honghu Cloud云架构一定时调度平台