赞
踩
这节课我们先来补全 TransferMask()
里对于 Overlay 布局类型面板的遮罩转移逻辑,大体上与 Canvas 布局类型的差不多。
接下来就是编写弹窗的隐藏和重新显示的逻辑。
在写重新显示弹窗的逻辑时我们发现 DoEnterUIPanel()
有一段代码可以复用,但是发现了一处逻辑上的错误,所以要调整一下代码。
DDFrameWidget.cpp
void UDDFrameWidget::DoEnterUIPanel(FName PanelName) { // ... 省略 // 此处作更改 if (!WorkLayout) { if (UnActiveCanvas.Num() == 0) { WorkLayout = WidgetTree->ConstructWidget<UCanvasPanel>(UCanvasPanel::StaticClass()); WorkLayout->SetVisibility(ESlateVisibility::SelfHitTestInvisible); } else WorkLayout = UnActiveCanvas.Pop(); // 添加布局控件到界面最顶层 UCanvasPanelSlot* FrameCanvasSlot = RootCanvas->AddChildToCanvas(WorkLayout); FrameCanvasSlot->SetAnchors(FAnchors(0.f, 0.f, 1.f, 1.f)); FrameCanvasSlot->SetOffsets(FMargin(0.f, 0.f, 0.f, 0.f)); // 添加到激活画布组 ActiveCanvas.Push(WorkLayout); } // ... 省略 // 此处作更改 if (!WorkLayout) { if (UnActiveOverlay.Num() == 0) { WorkLayout = WidgetTree->ConstructWidget<UOverlay>(UOverlay::StaticClass()); WorkLayout->SetVisibility(ESlateVisibility::SelfHitTestInvisible); } else WorkLayout = UnActiveOverlay.Pop(); // 添加布局控件到界面最顶层 UCanvasPanelSlot* FrameCanvasSlot = RootCanvas->AddChildToCanvas(WorkLayout); FrameCanvasSlot->SetAnchors(FAnchors(0.f, 0.f, 1.f, 1.f)); FrameCanvasSlot->SetOffsets(FMargin(0.f, 0.f, 0.f, 0.f)); // 添加到激活画布组 ActiveOverlay.Push(WorkLayout); } // ... 省略 } void UDDFrameWidget::HidePanelReverse(UDDPanelWidget* PanelWidget) { // 获取弹窗栈到数组 TArray<UDDPanelWidget*> PopStack; PopPanelStack.GenerateValueArray(PopStack); // 如果不是最上层的弹窗,直接返回 if (PopStack[PopStack.Num() - 1] != PanelWidget) { DDH::Debug() << PanelWidget->GetObjectName() << " Is Not Last Panel In PopPanelStack" << DDH::Endl(); return; } // 从栈中移除 PopPanelStack.Remove(PanelWidget->GetObjectName()); // 执行隐藏函数 PanelWidget->PanelHidden(); // 调整弹窗栈 PopStack.Pop(); if (PopStack.Num() > 0) { UDDPanelWidget* PrePanelWidget = PopStack[PopStack.Num() - 1]; // 转移遮罩到新的最顶层的弹窗的下一层 TransferMask(PrePanelWidget); // 恢复被冻结的最顶层的弹窗 PrePanelWidget->PanelResume(); } // 如果没有弹窗就移除遮罩 else RemoveMaskPanel(); } void UDDFrameWidget::ShowPanelReverse(UDDPanelWidget* PanelWidget) { // 如果弹窗栈里有元素,冻结最顶层的弹窗 if (PopPanelStack.Num() > 0) { TArray<UDDPanelWidget*> PanelStack; PopPanelStack.GenerateValueArray(PanelStack); PanelStack[PanelStack.Num() - 1]->PanelFreeze(); } // 弹窗对象必须从当前父控件移除,再重新添加到最顶层的界面(因为弹窗只是隐藏而不是销毁了) if (PanelWidget->UINature.LayoutType == ELayoutType::Canvas) { UCanvasPanel* PreWorkLayout = Cast<UCanvasPanel>(PanelWidget->GetParent()); UCanvasPanelSlot* PrePanelSlot = Cast<UCanvasPanelSlot>(PanelWidget->Slot); FAnchors PreAnchors = PrePanelSlot->GetAnchors(); FMargin PreOffsets = PrePanelSlot->GetOffsets(); // 将 PanelWidget 从当前父控件移除 PanelWidget->RemoveFromParent(); // 处理父控件 if (PreWorkLayout->GetChildrenCount() == 0) { PreWorkLayout->RemoveFromParent(); ActiveCanvas.Remove(PreWorkLayout); UnActiveCanvas.Push(PreWorkLayout); } // 寻找最顶层的 WorkLayout UCanvasPanel* WorkLayout = NULL; // 判断最底层的布局控件是否是 Canvas if (RootCanvas->GetChildrenCount() > 0) WorkLayout = Cast<UCanvasPanel>(RootCanvas->GetChildAt(RootCanvas->GetChildrenCount() - 1)); if (!WorkLayout) { // 如果没有任何对象 if (UnActiveCanvas.Num() == 0) { WorkLayout = WidgetTree->ConstructWidget<UCanvasPanel>(UCanvasPanel::StaticClass()); WorkLayout->SetVisibility(ESlateVisibility::SelfHitTestInvisible); } else WorkLayout = UnActiveCanvas.Pop(); // 添加布局控件到界面最顶层 UCanvasPanelSlot* FrameCanvasSlot = RootCanvas->AddChildToCanvas(WorkLayout); FrameCanvasSlot->SetAnchors(FAnchors(0.f, 0.f, 1.f, 1.f)); FrameCanvasSlot->SetOffsets(FMargin(0.f, 0.f, 0.f, 0.f)); // 添加到激活画布组 ActiveCanvas.Push(WorkLayout); } // 激活遮罩到最顶层弹窗 ActiveMask(WorkLayout, PanelWidget->UINature.PanelLucencyType); // 把弹窗添加到获取的最顶层的父控件 UCanvasPanelSlot* PanelSlot = WorkLayout->AddChildToCanvas(PanelWidget); PanelSlot->SetAnchors(PreAnchors); PanelSlot->SetOffsets(PreOffsets); } else { UOverlay* PreWorkLayout = Cast<UOverlay>(PanelWidget->GetParent()); UOverlaySlot* PrePanelSlot = Cast<UOverlaySlot>(PanelWidget->Slot); FMargin PrePadding = PrePanelSlot->Padding; TEnumAsByte<EHorizontalAlignment> PreHAlign = PrePanelSlot->HorizontalAlignment; TEnumAsByte<EVerticalAlignment> PreVAlign = PrePanelSlot->VerticalAlignment; // 将 PanelWidget 从当前父控件移除 PanelWidget->RemoveFromParent(); // 处理父控件 if (PreWorkLayout->GetChildrenCount() == 0) { PreWorkLayout->RemoveFromParent(); ActiveOverlay.Remove(PreWorkLayout); UnActiveOverlay.Push(PreWorkLayout); } UOverlay* WorkLayout = NULL; // 如果存在布局控件,试图把最后一个布局控件转换成 Overlay if (RootCanvas->GetChildrenCount() > 0) WorkLayout = Cast<UOverlay>(RootCanvas->GetChildAt(RootCanvas->GetChildrenCount() - 1)); if (!WorkLayout) { if (UnActiveOverlay.Num() == 0) { WorkLayout = WidgetTree->ConstructWidget<UOverlay>(UOverlay::StaticClass()); WorkLayout->SetVisibility(ESlateVisibility::SelfHitTestInvisible); } else WorkLayout = UnActiveOverlay.Pop(); // 添加布局控件到界面最顶层 UCanvasPanelSlot* FrameCanvasSlot = RootCanvas->AddChildToCanvas(WorkLayout); FrameCanvasSlot->SetAnchors(FAnchors(0.f, 0.f, 1.f, 1.f)); FrameCanvasSlot->SetOffsets(FMargin(0.f, 0.f, 0.f, 0.f)); // 添加到激活画布组 ActiveOverlay.Push(WorkLayout); } // 激活遮罩到最顶层弹窗 ActiveMask(WorkLayout, PanelWidget->UINature.PanelLucencyType); // 添加弹窗到获取到的最顶层的布局控件 UOverlaySlot* PanelSlot = WorkLayout->AddChildToOverlay(PanelWidget); PanelSlot->SetPadding(PrePadding); PanelSlot->SetHorizontalAlignment(PreHAlign); PanelSlot->SetVerticalAlignment(PreVAlign); } // 添加弹窗到栈 PopPanelStack.Add(PanelWidget->GetObjectName(), PanelWidget); // 显示弹窗 PanelWidget->PanelDisplay(); } void UDDFrameWidget::TransferMask(UDDPanelWidget* PanelWidget) { // ... 省略 if (PanelWidget->UINature.LayoutType == ELayoutType::Canvas) { // ... 省略 } else { UOverlay* WorkLayout = Cast<UOverlay>(PanelWidget->GetParent()); int32 StartOrder = WorkLayout->GetChildIndex(PanelWidget); for (int i = StartOrder; i < WorkLayout->GetChildrenCount(); ++i) { UDDPanelWidget* TempPanelWidget = Cast<UDDPanelWidget>(WorkLayout->GetChildAt(i)); if (!TempPanelWidget) continue; // 保存 UI 面板以及布局数据 AbovePanelStack.Push(TempPanelWidget); FUINature TempUINature; UOverlaySlot* TempPanelSlot = Cast<UOverlaySlot>(TempPanelWidget->Slot); TempUINature.Offsets = TempPanelSlot->Padding; TempUINature.HAlign = TempPanelSlot->HorizontalAlignment; TempUINature.VAlign = TempPanelSlot->VerticalAlignment; AboveNatureStack.Push(TempUINature); } // 循环移除上层 UI 面板 for (int i = 0; i < AbovePanelStack.Num(); ++i) AbovePanelStack[i]->RemoveFromParent(); // 添加遮罩到新的父控件 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 (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) { UOverlaySlot* PanelSlot = WorkLayout->AddChildToOverlay(AbovePanelStack[i]); PanelSlot->SetPadding(AboveNatureStack[i].Offsets); PanelSlot->SetHorizontalAlignment(AboveNatureStack[i].HAlign); PanelSlot->SetVerticalAlignment(AboveNatureStack[i].VAlign); } } }
接下来我们测试一下弹窗隐藏和重新显示的逻辑是否正常运行。依旧是修改协程方法里的调用顺序。
RCGameUIFrame.cpp
// 修改协程方法如下 DDCoroTask* URCGameUIFrame::UIProcess() { DDCORO_PARAM(URCGameUIFrame); #include DDCORO_BEGIN() // 显示状态栏和小地图 D->ShowUIPanel("StatePanel"); D->ShowUIPanel("MiniMapPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); // 缩短时间 // 显示菜单栏 D->ShowUIPanel("MenuPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); // 显示设置栏 D->ShowUIPanel("OptionPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); // 隐藏菜单栏,这个操作会失败并输出 Debug 错误 D->HideUIPanel("MenuPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); // 隐藏设置栏 D->HideUIPanel("OptionPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); // 隐藏菜单栏,本次操作成功 D->HideUIPanel("MenuPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); // 显示菜单栏 D->ShowUIPanel("MenuPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); // 显示设置栏 D->ShowUIPanel("OptionPanel"); #include DDCORO_END() }
运行游戏,可见界面上有状态栏和小地图,
3 秒后菜单弹窗伴随着遮罩出现,菜单弹窗可交互;
再 3 秒后设置弹窗出现,遮罩移到它的底下,菜单弹窗不可交互,设置弹窗可交互;
再 3 秒后试图隐藏菜单弹窗,但是它不是最靠前的弹窗,所以左上角输出 Debug 语句提示失败。
再 3 秒后设置弹窗隐藏,遮罩移到菜单弹窗的底下,并且菜单弹窗又恢复可交互;
再 3 秒菜单弹窗和遮罩隐藏;
再 3 秒,菜单弹窗和遮罩重新出现,菜单弹窗可交互;
最后再 3 秒,设置弹窗出现,遮罩移到它的底下,菜单弹窗不可交互,设置弹窗可交互;
说明我们写的遮罩管理器以及弹窗的隐藏和重新显示都没有问题。
UI 框架已经实现了大半,我们接下来继续实现 UI 的销毁功能。对于销毁逻辑也需要根据面板类型来使用相应的方法。
DDFrameWidget.h
public: // 销毁 UI UFUNCTION() void ExitUIPanel(FName PanelName); // 处理 UI 面板销毁后的父控件(供反射系统调用) UFUNCTION() void ExitCallBack(ELayoutType LayoutType, UPanelWidget* InLayout); protected: // 销毁 UI void ExitPanelDoNothing(UDDPanelWidget* PanelWidget); void ExitPanelHideOther(UDDPanelWidget* PanelWidget); void ExitPanelReverse(UDDPanelWidget* PanelWidget);
DDFrameWidget.cpp
void UDDFrameWidget::ExitUIPanel(FName PanelName) { // 如果正在预加载但是没有加载完成(这种情况出现在执行第一次显示或提前加载后就马上执行销毁界面) if (!AllPanelGroup.Contains(PanelName) && LoadedPanelName.Contains(PanelName)) { DDH::Debug() << "Do Not Exit UI Panel when Loading Panel" << DDH::Endl(); return; } // 如果这个 UI 面板没有加载到全部组 if (!AllPanelGroup.Contains(PanelName)) return; // 获取 UI 面板 UDDPanelWidget* PanelWidget = *AllPanelGroup.Find(PanelName); // 是否在显示组或者弹窗栈内 if (!ShowPanelGroup.Contains(PanelName) && !PopPanelStack.Contains(PanelName)) { AllPanelGroup.Remove(PanelName); LoadedPanelName.Remove(PanelName); // 运行 PanelExit 生命周期,具体的内存释放代码在该周期函数里面 PanelWidget->PanelExit(); // 直接返回 return; } // 处理隐藏 UI 面板相关的流程 switch (PanelWidget->UINature.PanelShowType) { case EPanelShowType::DoNothing: ExitPanelDoNothing(PanelWidget); break; case EPanelShowType::HideOther: ExitPanelHideOther(PanelWidget); break; case EPanelShowType::Reverse: ExitPanelReverse(PanelWidget); break; } } void UDDFrameWidget::ExitPanelDoNothing(UDDPanelWidget* PanelWidget) { // 从显示组,全部组,加载名字组移除 ShowPanelGroup.Remove(PanelWidget->GetObjectName()); AllPanelGroup.Remove(PanelWidget->GetObjectName()); LoadedPanelName.Remove(PanelWidget->GetObjectName()); // 运行销毁生命周期 PanelWidget->PanelExit(); } void UDDFrameWidget::ExitPanelHideOther(UDDPanelWidget* PanelWidget) { // 从显示组,全部组,加载名字组移除 ShowPanelGroup.Remove(PanelWidget->GetObjectName()); AllPanelGroup.Remove(PanelWidget->GetObjectName()); LoadedPanelName.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->PanelExit(); } void UDDFrameWidget::ExitPanelReverse(UDDPanelWidget* PanelWidget) { // 获取弹窗栈到数组 TArray<UDDPanelWidget*> PopStack; PopPanelStack.GenerateValueArray(PopStack); // 如果不是最上层的弹窗,直接返回 if (PopStack[PopStack.Num() - 1] != PanelWidget) { DDH::Debug() << PanelWidget->GetObjectName() << " Is Not Last Panel In PopPanelStack" << DDH::Endl(); return; } // 从栈,全部组,加载名字组中移除 PopPanelStack.Remove(PanelWidget->GetObjectName()); AllPanelGroup.Remove(PanelWidget->GetObjectName()); LoadedPanelName.Remove(PanelWidget->GetObjectName()); // 运行销毁生命周期函数 PanelWidget->PanelExit(); // 调整弹窗栈 PopStack.Pop(); if (PopStack.Num() > 0) { UDDPanelWidget* PrePanelWidget = PopStack[PopStack.Num() - 1]; // 转移遮罩到新的最顶层的弹窗的下一层 TransferMask(PrePanelWidget); // 恢复被冻结的最顶层的弹窗 PrePanelWidget->PanelResume(); } else RemoveMaskPanel(); } void UDDFrameWidget::ExitCallBack(ELayoutType LayoutType, UPanelWidget* InLayout) { if (LayoutType == ELayoutType::Canvas) { UCanvasPanel* WorkLayout = Cast<UCanvasPanel>(InLayout); if (WorkLayout->GetChildrenCount() == 0) { WorkLayout->RemoveFromParent(); ActiveCanvas.Remove(WorkLayout); UnActiveCanvas.Push(WorkLayout); } } else { UOverlay* WorkLayout = Cast<UOverlay>(InLayout); if (WorkLayout->GetChildrenCount() == 0) { WorkLayout->RemoveFromParent(); ActiveOverlay.Remove(WorkLayout); UnActiveOverlay.Push(WorkLayout); } } }
来到 DDPanelWidget,编写销毁 UI 的具体逻辑。
因为需要考虑面板销毁后父控件是否还有子面板(如果没有就没必要存在了),所以我们利用反射系统声明一个方法来调用 DDFrameWidget 里的方法来移除没有内容的父控件。
DDPanelWidget.h
protected: // 销毁动画回调函数 void RemoveCallBack(); protected: // UIFrame 管理器所在的模组 ID,约定在 HUD 下,数值是 1 static int32 UIFrameModuleIndex; // UIFrame 管理器的对象名,约定是 UIFrame static FName UIFrameName; // 销毁回调函数名字 static FName ExitCallBackName; protected: DDOBJFUNC_TWO(ExitCallBack, ELayoutType, LayoutType, UPanelWidget*, WorkLayout);
DDPanelWidget.cpp
int32 UDDPanelWidget::UIFrameModuleIndex(1); FName UDDPanelWidget::UIFrameName(TEXT("UIFrame")); FName UDDPanelWidget::ExitCallBackName(TEXT("ExitCallBack")); void UDDPanelWidget::PanelExit() { // 如果 UI 面板正在显示 if (GetVisibility() != ESlateVisibility::Hidden) InvokeDelay(PanelHiddenName, DisplayLeaveMovie(), this, &UDDPanelWidget::RemoveCallBack); else RemoveCallBack(); } void UDDPanelWidget::RemoveCallBack() { // 获取父控件 UPanelWidget* WorkLayout = GetParent(); // 这个判断条件会在下一集补充 // 已经加载了 UI 面板,但是一直没有运行显示命令的情况下,WorkLayout 为空 if (WorkLayout) { RemoveFromParent(); // 告诉 UI 管理器处理父控件 ExitCallBack(UIFrameModuleIndex, UIFrameName, ExitCallBackName, UINature.LayoutType, WorkLayout); } // 执行销毁 DDDestroy(); }
接下来测试下销毁 UI 的逻辑是否正常运行,依旧是调整协程方法里的调用逻辑。
RCGameUIFrame.cpp
// 修改协程方法如下 DDCoroTask* URCGameUIFrame::UIProcess() { DDCORO_PARAM(URCGameUIFrame); #include DDCORO_BEGIN() D->ShowUIPanel("StatePanel"); D->ShowUIPanel("MiniMapPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); D->ShowUIPanel("BigMapPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); D->ShowUIPanel("MenuPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); D->ShowUIPanel("OptionPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); DDH::Debug() << "ExitUIPanel MiniMapPanel" << DDH::Endl(); D->ExitUIPanel("MiniMapPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); DDH::Debug() << "ExitUIPanel OptionPanel" << DDH::Endl(); D->ExitUIPanel("OptionPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); DDH::Debug() << "ExitUIPanel BigMapPanel" << DDH::Endl(); D->ExitUIPanel("BigMapPanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); DDH::Debug() << "ExitUIPanel StatePanel" << DDH::Endl(); D->ExitUIPanel("StatePanel"); #include DDYIELD_READY() DDYIELD_RETURN_SECOND(3.f); DDH::Debug() << "ExitUIPanel MenuPanel" << DDH::Endl(); D->ExitUIPanel("MenuPanel"); #include DDCORO_END() }
运行游戏,首先可看到状态栏和小地图;接着是大地图出现(状态栏和小地图收起);菜单弹窗出现;设置弹窗出现;
随后销毁小地图、销毁设置弹窗、销毁大地图(状态栏出现)、销毁状态栏,最后销毁菜单弹窗。
说明销毁 UI 的功能也写好了。
课程已经接近尾声,本集开头会修改一些疏漏的地方,笔者已经在先前的课程里标注了。
我们前面写了很多 UI 框架的方法,但是看起来都是管理类在操控面板,我们打算让面板自己也能执行这些操控方法,只要通过反射系统让管理类执行就可以实现了。
DDPanelWidget.h
protected: void ShowSelfPanel(); void HideSelfPanel(); void ExitSelfPanel(); void AdvanceLoadPanel(FName PanelName); void ShowUIPanel(FName PanelName); void HideUIPanel(FName PanelName); void ExitUIPanel(FName PanelName); protected: // 显示 UI 方法名 static FName ShowUIPanelName; // 隐藏 UI 方法名 static FName HideUIPanelName; // 销毁 UI 方法名 static FName ExitUIPanelName; // 预加载方法名 static FName AdvanceLoadPanelName; protected: DDOBJFUNC_ONE(OperatorUIPanel, FName, PanelName);
DDPanelWidge.cpp
FName UDDPanelWidget::ShowUIPanelName(TEXT("ShowUIPanel")); FName UDDPanelWidget::HideUIPanelName(TEXT("HideUIPanel")); FName UDDPanelWidget::ExitUIPanelName(TEXT("ExitUIPanel")); FName UDDPanelWidget::AdvanceLoadPanelName(TEXT("AdvanceLoadPanel")); void UDDPanelWidget::ShowSelfPanel() { ShowUIPanel(GetObjectName()); } void UDDPanelWidget::HideSelfPanel() { HideUIPanel(GetObjectName()); } void UDDPanelWidget::ExitSelfPanel() { ExitUIPanel(GetObjectName()); } void UDDPanelWidget::AdvanceLoadPanel(FName PanelName) { OperatorUIPanel(UIFrameModuleIndex, UIFrameName, AdvanceLoadPanelName, PanelName); } void UDDPanelWidget::ShowUIPanel(FName PanelName) { OperatorUIPanel(UIFrameModuleIndex, UIFrameName, ShowUIPanelName, PanelName); } void UDDPanelWidget::HideUIPanel(FName PanelName) { OperatorUIPanel(UIFrameModuleIndex, UIFrameName, HideUIPanelName, PanelName); } void UDDPanelWidget::ExitUIPanel(FName PanelName) { OperatorUIPanel(UIFrameModuleIndex, UIFrameName, ExitUIPanelName, PanelName); }
最后再改一些地方的错误。
DDTypes.h
// 执行 Execute 方法之前必须手动调用 IsBound() 方法判定是否有绑定函数
template<typename RetType, typename... VarTypes>
RetType DDCallHandle<RetType, VarTypes...>::Execute(VarTypes... Params)
{
// 删除原来的 是否绑定 判断
return MsgQuene->Execute<RetType, VarTypes...>(CallName, Params...);
}
DDDriver.cpp
#if WITH_EDITOR void ADDDriver::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); if (PropertyChangedEvent.Property && PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(ADDDriver, ModuleType)) { Center->IterChangeModuleType(Center, ModuleType); } } #endif // 下面这两个方法要放在预编译的 #if WITH_EDITOR 之外,笔者检查的时候已经是放在外面的了,应该是在前面的课程也有提到这个修改 void ADDDriver::ExecuteFunction(DDModuleAgreement Agreement, DDParam* Param) { Center->AllotExecuteFunction(Agreement, Param); } void ADDDriver::ExecuteFunction(DDObjectAgreement Agreement, DDParam* Param) { Center->AllotExecuteFunction(Agreement, Param); }
最后我们来看一下梁迪老师的 DataDriven 框架实现了如下功能:
至于坦克大战,应该是没有的了。不过能系统地学习梁迪老师的这个框架,相信读者也能收获到很多。笔者在此衷心感谢梁迪老师的优质课程 : )
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。