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 {}
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/681682.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】static静态关键字

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

C# CAD2016 多边形顶点按方向重新排序

多边形顶点按方向重新排序 初始化多边形顶点集合 outerPoints 创建一个名为 outerPoints 的 List<Point2d>&#xff0c;用于存储多边形的所有顶点坐标。 计算多边形顶点集合的边界框&#xff08;BoundingBox&#xff09; 使用LINQ的Aggregate方法遍历整个outerPoints列表…

【C++】中的 inline 用法

1、引入 inline 关键字的原因 在 c/c 中&#xff0c;为了解决一些频繁调用的小函数大量消耗栈空间&#xff08;栈内存&#xff09;的问题&#xff0c;特别的引入了 inline 修饰符&#xff0c;表示为内联函数。 栈空间就是指放置程序的局部数据&#xff08;也就是函数内数据&a…

SpringBoot+Vue3 完成小红书项目

简介 该项目采用微服务架构&#xff0c;实现了前后端分离的系统设计。在前端&#xff0c;我们选择了 Vue3 配合 TypeScript 和 ElementUi 框架&#xff0c;以提升开发效率和用户体验。而在后端&#xff0c;则是运用 SpringBoot 和 Mybatis-plus 进行开发&#xff0c;保证了系统…

使用word2vec+tensorflow自然语言处理NLP

目录 介绍&#xff1a; 搭建上下文或预测目标词来学习词向量 建模1&#xff1a; 建模2&#xff1a; 预测&#xff1a; 介绍&#xff1a; Word2Vec是一种用于将文本转换为向量表示的技术。它是由谷歌团队于2013年提出的一种神经网络模型。Word2Vec可以将单词表示为高维空间…

JS逆向进阶篇【去哪儿旅行登录】【上篇】

目标url: aHR0cHM6Ly91c2VyLnF1bmFyLmNvbS9wYXNzcG9ydC9sb2dpbi5qc3A 实现难点&#xff1a; 逆向滑块请求发送短信登录 目录 每篇前言&#xff1a;0、前置技术栈&#xff08;1&#xff09;JS实现页面滑动&#xff08;2&#xff09;JS实现记录滑动轨迹&#xff08;3&#xff…

SAP MM学习笔记42 - 特殊调达流程 - 受托品(寄售)

上一章讲了 外注加工的知识。 详细可以参考如下链接。 SAP MM学习笔记41 - 特殊调达流程 - 外注加工-CSDN博客 咱们继续学习特殊调达流程。 本章主要讲受托品。 1&#xff0c;什么是受托品 &#xff08;寄售&#xff09; 仕入先提供的商品&#xff0c;商品是放在你公司了&a…

一周学会Django5 Python Web开发-Django5 Hello World编写

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计14条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

java SSM新闻管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM新闻管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S…

《Java 简易速速上手小册》第8章:Java 性能优化(2024 最新版)

文章目录 8.1 性能评估工具 - 你的性能探测仪8.1.1 基础知识8.1.2 重点案例&#xff1a;使用 VisualVM 监控应用性能8.1.3 拓展案例 1&#xff1a;使用 JProfiler 分析内存泄漏8.1.4 拓展案例 2&#xff1a;使用 Gatling 进行 Web 应用压力测试 8.2 JVM 调优 - 魔法引擎的调校8…

智能汽车行业产业研究报告:4D成像毫米波雷达—自动驾驶最佳辅助

今天分享的是智能汽车系列深度研究报告&#xff1a;《智能汽车行业产业研究报告&#xff1a;4D成像毫米波雷达—自动驾驶最佳辅助》。 &#xff08;报告出品方&#xff1a;开源证券&#xff09; 报告共计&#xff1a;43页 视觉感知最佳辅助——4D 成像毫米波雷达 感知是自动…

【Python】window环境使用venv部署jupyter notebook

基础信息 执行winr&#xff0c;在输入框输入powershell: python版本&#xff1a;python -v 创建并激活虚拟环境 1、进入要创建虚拟环境的目录&#xff0c;操作示例如下&#xff1a; PS C:\Users\Administrator> cd D:\Python\weltest 2、创建虚拟环境&#xff0c;操作示…

BUGKU-WEB GET

题目描述 没有提示&#xff0c;就一个get&#xff0c;启动场景看看&#xff1a; 解题思路 显然是PHP语言解读分析代码吧写出你的payload 相关工具 略 解题步骤 进入场景分析代码 $what$_GET[what]; echo $what; if($whatflag) echo flag{****};前两句&#xff1a;使用get…

2024年最新指南:如何订阅Midjourney(详尽步骤解析)

前言&#xff1a; Midjourney是一个基于人工智能的图像生成工具&#xff0c;它使用高级算法来创建独特和复杂的图像。这个工具能够根据用户输入的文字描述生成对应的图片。Midjourney的特点在于它能够处理非常抽象或者具体的描述&#xff0c;生成高质量、富有创意的视觉内容。M…

Flaurm实现中文搜索

目录 摘要需求本文涉及环境情况如下解决方案最终效果文章其他链接&#xff1a; 摘要 Flarum本身对中文支持并不理想&#xff0c;但随着版本更新&#xff0c;逐渐加强了对中文的优化。然而在1.8.5版本&#xff0c;却还是不支持中文搜索网站文章内容。作者在检索了全网教程&#…

数模.SI模型SI的四种扩展

一&#xff1a;最简单的考虑方式 二考虑某种使得参数beta降低的因素 三&#xff1a;增加人口自然出生率和死亡率&#xff0c;但不考虑疾病的死亡率 四&#xff1a;不考虑人口自然出生率和死亡率&#xff0c;只考虑疾病的死亡率 五&#xff1a;同时考虑人口自然出生率和死亡率和…

kali系统概述、nmap扫描应用、john破解密码、抓包概述、以太网帧结构、抓包应用、wireshark应用、nginx安全加固、Linux系统加固

目录 kali nmap扫描 使用john破解密码 抓包 封装与解封装 网络层数据包结构 TCP头部结构​编辑 UDP头部结构 实施抓包 安全加固 nginx安全 防止缓冲区溢出 Linux加固 kali 实际上它就是一个预安装了很多安全工具的Debian Linux [rootmyhost ~]# kali resetkali …

C#一维数组排序方法:选择排序法

目录 一、数组元素常见的排序法 1.选择排序法 二、实例1&#xff1a;选择排序法 1.源码 2.生成效果 一、数组元素常见的排序法 常见的排序法&#xff1a;选择排序法、冒泡排序法、快速排序法、直接插入法、希尔排序法、Array.Sort方法。 1.选择排序法 通过遍历实现排序&…

基于Qt的人脸识别项目(功能:颜值检测,口罩检测,表情检测,性别检测,年龄预测等)

完整代码链接在文章末尾 效果展示 代码讲解(待更新) qt图片文件上传 #include <QtWidgets> #include <QFileDialog>

多模态学习综述(MultiModal Learning)

最早开始关注到多模态机器学习是看到Jeff Dean在2019年年底NeurIPS大会上的一个采访报道&#xff0c;讲到了2020年机器学习趋势&#xff1a;多任务和多模态学习将成为突破口。 Jeff Dean 谈2020年机器学习趋势&#xff1a;多任务和多模式学习将成为突破口 站在2022年&#xff…