UE4 从无到有纯 C++ & Slate 开发沙盒游戏(十四) 菜单控件初始化
到现在游戏的主菜单各个组件功能均已实现,这部分要进行主菜单界面的初始化,就是显示菜单选项的初始显示和来回跳转,下图标黄的部分
首先添加菜单界面类型
d:\ue4 project\ue26.2\courseproject\slaicourse\Source\SlAiCourse\Public\Data\SlAiTypes.h
1 #include "CoreMinimal.h" 2 3 /** 4 * UENUM()是UE4反射的枚举,UE4蓝图中将可以调用 5 */ 6 UENUM() 7 enum class ECultureTeam : uint8 8 { 9 EN = 0, 10 ZH 11 }; 12 13 14 // 菜单按钮的类型 15 namespace EMenuItem 16 { 17 enum Type 18 { 19 None, 20 StartGame, //开始游戏 21 GameOption, //游戏设置 22 QuitGame, //退出游戏 23 NewGame, //新游戏 24 LoadRecord, //加载存档 25 StartGameGoBack, //从 开始游戏菜单 返回 26 GameOptionGoBack, //从 游戏设置菜单 返回 27 NewGameGoBack, //从 新游戏菜单 返回 28 ChooseRecordGoBack, //从 加载存档菜单 返回 29 EnterGame, //进入游戏 30 EnterRecord //进入存档 31 }; 32 } 33 34 //菜单界面类型 35 namespace EMenuType 36 { 37 enum Type 38 { 39 None, 40 MainMenu, //主菜单 41 StartGame, //开始游戏菜单:显示新游戏菜单 || 加载存档菜单 42 GameOption, //游戏设置菜单 43 NewGame, //新游戏菜单 44 ChooseRecord //加载存档菜单 45 }; 46 }
声明需要的指针等变量和方法
d:\ue4 project\ue26.2\courseproject\slaicourse\Source\SlAiCourse\Public\UI\Widget\SSlAiMenuWidget.h
1 #include "CoreMinimal.h" 2 #include "Data/SlAiTypes.h" 3 #include "Widgets/SCompoundWidget.h" 4 5 class SBox; 6 class STextBlock; 7 class SVerticalBox; 8 struct MenuGroup; 9 class SSlAiGameOptionWidget; 10 class SSlAiNewGameWidget; 11 class SSlAiChooseRecordWidget; 12 13 class SLAICOURSE_API SSlAiMenuWidget : public SCompoundWidget 14 { 15 public: 16 SLATE_BEGIN_ARGS(SSlAiMenuWidget) 17 {} 18 19 SLATE_END_ARGS() 20 21 /** Constructs this widget with InArgs */ 22 void Construct(const FArguments& InArgs); 23 24 private: 25 //绑定到各个MenuItem的方法 26 void MenuItemOnClicked(EMenuItem::Type ItemType); 27 28 /** 29 * 由于MenuWidget是主菜单,我们要在这里实例化所有的功能控件,所有的事件都会放到这里 30 * 这样的好处是到后面加了其他场景后,用到这些组件的时候,实例化一个别的方法添加到事件里,就可以重复调用了 31 */ 32 //修改语言 33 void ChangeCulture(ECultureTeam Culture); 34 //修改音量 35 void ChangeVolume(const float MusicVolume, const float SoundVolume); 36 //初始化所有的控件 37 void InitializedMenuList(); 38 //选择显示的界面 39 void ChooseWidget(EMenuType::Type WidgetType); 40 //修改菜单大小 41 void ResetWidgetSize(float NewWidget, float NewHeight); 42 43 private: 44 //保存根节点,用来动态的修改SBox的大小 45 TSharedPtr<SBox> RootSizeBox; 46 //获取MenuStyle 47 const struct FSlAiMenuStyle* MenuStyle; 48 //保存标题 49 TSharedPtr<STextBlock> TitleText; 50 //用来保存垂直列表,所有的菜单按钮组件都会放置到这ContentBox中 51 TSharedPtr<SVerticalBox> ContentBox; 52 //保存菜单组 53 TMap<EMenuType::Type, TSharedPtr<MenuGroup>> MenuMap; 54 //游戏设置Widget的指引 55 TSharedPtr<SSlAiGameOptionWidget> GameOptionWidget; 56 //新游戏控件指针 57 TSharedPtr<SSlAiNewGameWidget> NewGameWidget; 58 //选择存档控件指针 59 TSharedPtr<SSlAiChooseRecordWidget> ChooseRecordWidget; 60 };
以下主要就实现所有界面的实例化,以及界面之间的跳转逻辑,新增的结构体 MenuGroup 用来存放每个菜单的属性,最后会将这个结构体放置到 MenuMap 中,通过 ChooseWidget 方法的调用来控制当前显示哪个菜单。
D:\UE4 Project\UE26.2\CourseProject\SlAiCourse\Source\SlAiCourse\Private\UI\Widget\SSlAiMenuWidget.cpp
1 #include "SlateOptMacros.h" 2 #include "Common/SlAiHelper.h" 3 #include "Data/SlAiDataHandle.h" 4 #include "Widgets/Layout/SBox.h" 5 #include "Widgets/Images/SImage.h" 6 #include "Widgets/Text/STextBlock.h" 7 #include "Widgets/SBoxPanel.h" 8 #include "UI/Style/SlAiStyle.h" 9 #include "UI/Style/SlAiMenuWidgetStyle.h" 10 #include "UI/Widget/SSlAiMenuWidget.h" 11 #include "UI/Widget/SSlAiGameOptionWidget.h" 12 #include "UI/Widget/SSlAiMenuItemWidget.h" 13 #include "UI/Widget/SSlAiNewGameWidget.h" 14 #include "UI/Widget/SSlAiChooseRecordWidget.h" 15 16 17 /** 18 * 每一个结构体对应一个菜单*/ 19 struct MenuGroup 20 { 21 //菜单标题 22 FText MenuName; 23 //菜单高度 24 float MenuHeight; 25 //下属组件 26 TArray<TSharedPtr<SCompoundWidget>> ChildWidget; 27 28 //构造函数 29 MenuGroup(const FText Name, const float Height, TArray<TSharedPtr<SCompoundWidget>>* Children) 30 { 31 MenuName = Name; 32 MenuHeight = Height; 33 for (TArray<TSharedPtr<SCompoundWidget>>::TIterator It(*Children); It; It++) 34 { 35 ChildWidget.Add(*It); 36 } 37 } 38 }; 39 40 41 BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION 42 void SSlAiMenuWidget::Construct(const FArguments& InArgs) 43 { 44 //获取MenuStyle 45 MenuStyle = &SlAiStyle::Get().GetWidgetStyle<FSlAiMenuStyle>("BPSlAiMenuStyle"); 46 /** 47 *切换语言 48 //FInternationalization::Get().SetCurrentCulture(TEXT("en")); 49 //FInternationalization::Get().SetCurrentCulture(TEXT("ch")); 50 */ 51 //转换为中文 52 SlAiDataHandle::Get()->ChangeLocalizationCulture(ECultureTeam::ZH); 53 54 ChildSlot 55 [ 56 /** 57 *没有Slot,没有Slot要么不能插入子组件,要么只能插入一个子组件,SizeBox 只能插入一个子组件 58 */ 59 SAssignNew(RootSizeBox, SBox) 60 [ 61 SNew(SOverlay) 62 63 +SOverlay::Slot() //主菜单背景 64 .HAlign(HAlign_Fill) 65 .VAlign(VAlign_Fill) 66 .Padding(FMargin(0.f, 50.f, 0.f, 0.f)) //FMargin 间隔(左 上 右 下) 67 [ 68 SNew(SImage) 69 .Image(&MenuStyle->MenuBackgroundBrush) 70 ] 71 72 +SOverlay::Slot() //菜单左侧图片 73 .HAlign(HAlign_Left) 74 .VAlign(VAlign_Center) 75 .Padding(FMargin(0.f, 25.f, 0.f, 0.f)) 76 [ 77 SNew(SImage).Image(&MenuStyle->LeftIconBrush) 78 ] 79 80 +SOverlay::Slot() //菜单右侧图片 81 .HAlign(HAlign_Right) 82 .VAlign(VAlign_Center) 83 .Padding(FMargin(0.f, 25.f, 0.f, 0.f)) 84 [ 85 SNew(SImage).Image(&MenuStyle->RightIconBrush) 86 ] 87 88 +SOverlay::Slot() //菜单标题图片 89 .HAlign(HAlign_Center) 90 .VAlign(VAlign_Top) 91 [ 92 SNew(SBox) 93 .WidthOverride(400.f) 94 .HeightOverride(100.f) 95 [ 96 SNew(SBorder) 97 .BorderImage(&MenuStyle->TitleBorderBrush) 98 .HAlign(HAlign_Center) 99 .VAlign(VAlign_Center) 100 [ 101 SAssignNew(TitleText, STextBlock) 102 .Font(SlAiStyle::Get().GetFontStyle("MenuItemFort")) 103 .Text(NSLOCTEXT("SlAiMenu", "Menu", "Menu")) 104 .Font(MenuStyle->Font_60) 105 ] 106 ] 107 ] 108 109 +SOverlay::Slot() //菜单按钮组件 110 .HAlign(HAlign_Center) 111 .VAlign(VAlign_Top) 112 .Padding(FMargin(0.f, 130.f, 0.f, 0.f)) 113 [ 114 //菜单组件创建到这里 115 SAssignNew(ContentBox, SVerticalBox) 116 ] 117 ] 118 ]; 119 120 InitializedMenuList(); 121 } 122 123 END_SLATE_FUNCTION_BUILD_OPTIMIZATION 124 125 //按钮点击事件,实现界面跳转 126 void SSlAiMenuWidget::MenuItemOnClicked(EMenuItem::Type ItemType) 127 { 128 switch (ItemType) 129 { 130 case EMenuItem::StartGame: 131 ChooseWidget(EMenuType::StartGame); 132 break; 133 case EMenuItem::GameOption: 134 ChooseWidget(EMenuType::GameOption); 135 break; 136 case EMenuItem::QuitGame: 137 SlAiHelper::Debug(FString("退出游戏功能"), 5.f); 138 break; 139 case EMenuItem::NewGame: 140 ChooseWidget(EMenuType::NewGame); 141 break; 142 case EMenuItem::LoadRecord: 143 ChooseWidget(EMenuType::ChooseRecord); 144 break; 145 case EMenuItem::StartGameGoBack: 146 ChooseWidget(EMenuType::MainMenu); 147 break; 148 case EMenuItem::GameOptionGoBack: 149 ChooseWidget(EMenuType::MainMenu); 150 break; 151 case EMenuItem::NewGameGoBack: 152 ChooseWidget(EMenuType::StartGame); 153 break; 154 case EMenuItem::ChooseRecordGoBack: 155 ChooseWidget(EMenuType::StartGame); 156 break; 157 case EMenuItem::EnterGame: 158 SlAiHelper::Debug(FString("进入新游戏功能"), 5.f); 159 break; 160 case EMenuItem::EnterRecord: 161 SlAiHelper::Debug(FString("进入存档功能"), 5.f); 162 break; 163 } 164 165 } 166 167 void SSlAiMenuWidget::ChangeCulture(ECultureTeam Culture) 168 { 169 SlAiDataHandle::Get()->ChangeLocalizationCulture(Culture); 170 } 171 172 void SSlAiMenuWidget::ChangeVolume(const float MusicVolume, const float SoundVolume) 173 { 174 SlAiDataHandle::Get()->ResetMenuVolume(MusicVolume, SoundVolume); 175 } 176 177 /* 178 * 游戏运行后会调用到 InitializedMenuList() 实例化所有的界面组件 179 * 并在实例化所有组件后 通过 ChooseWidget() 调用主界面的首菜单 180 */ 181 void SSlAiMenuWidget::InitializedMenuList() 182 { 183 //实例化主界面 184 TArray<TSharedPtr<SCompoundWidget>> MainMenuList; 185 MainMenuList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "StartGame", "StartGame")).ItemType(EMenuItem::StartGame).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)); 186 MainMenuList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "GameOption", "GameOption")).ItemType(EMenuItem::GameOption).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)); 187 MainMenuList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "QuitGame", "QuitGame")).ItemType(EMenuItem::QuitGame).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)); 188 189 MenuMap.Add(EMenuType::MainMenu, MakeShareable(new MenuGroup(NSLOCTEXT("SlAiMenu", "Menu", "Menu"), 510.f, &MainMenuList))); 190 191 //开始游戏界面 192 TArray<TSharedPtr<SCompoundWidget>> StartGameList; 193 StartGameList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "NewGame", "NewGame")).ItemType(EMenuItem::NewGame).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)); 194 StartGameList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "LoadRecord", "LoadRecord")).ItemType(EMenuItem::LoadRecord).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)); 195 StartGameList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "GoBack", "GoBack")).ItemType(EMenuItem::StartGameGoBack).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)); 196 197 MenuMap.Add(EMenuType::StartGame, MakeShareable(new MenuGroup(NSLOCTEXT("SlAiMenu", "StartGame", "StartGame"), 510.f, &StartGameList))); 198 199 //游戏设置界面 200 TArray<TSharedPtr<SCompoundWidget>> GameOptionList; 201 //实例化游戏设置Widget(语言和声音) 202 SAssignNew(GameOptionWidget, SSlAiGameOptionWidget).ChangeCulture(this, &SSlAiMenuWidget::ChangeCulture).ChangeVolume(this, &SSlAiMenuWidget::ChangeVolume); 203 GameOptionList.Add(GameOptionWidget); 204 GameOptionList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "GoBack", "GoBack")).ItemType(EMenuItem::GameOptionGoBack).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)); 205 206 MenuMap.Add(EMenuType::GameOption, MakeShareable(new MenuGroup(NSLOCTEXT("SlAiMenu", "GameOption", "GameOption"), 610.f, &GameOptionList))); 207 208 //新游戏界面 209 TArray<TSharedPtr<SCompoundWidget>> NewGameList; 210 SAssignNew(NewGameWidget, SSlAiNewGameWidget); 211 NewGameList.Add(NewGameWidget); 212 NewGameList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "EnterGame", "EnterGame")).ItemType(EMenuItem::EnterGame).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)); 213 NewGameList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "GoBack", "GoBack")).ItemType(EMenuItem::NewGameGoBack).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)); 214 215 MenuMap.Add(EMenuType::NewGame, MakeShareable(new MenuGroup(NSLOCTEXT("SlAiMenu", "NewGame", "NewGame"), 510.f, &NewGameList))); 216 217 //选择存档界面 218 TArray<TSharedPtr<SCompoundWidget>> ChooseRecordList; 219 SAssignNew(ChooseRecordWidget, SSlAiChooseRecordWidget); 220 ChooseRecordList.Add(ChooseRecordWidget); 221 ChooseRecordList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "EnterRecord", "EnterRecord")).ItemType(EMenuItem::EnterRecord).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)); 222 ChooseRecordList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "GoBack", "GoBack")).ItemType(EMenuItem::ChooseRecordGoBack).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)); 223 224 MenuMap.Add(EMenuType::ChooseRecord, MakeShareable(new MenuGroup(NSLOCTEXT("SlAiMenu", "LoadRecord", "LoadRecord"), 510.f, &ChooseRecordList))); 225 226 ChooseWidget(EMenuType::MainMenu); 227 } 228 229 void SSlAiMenuWidget::ChooseWidget(EMenuType::Type WidgetType) 230 { 231 //移除所有组件 232 ContentBox->ClearChildren(); 233 234 //之后会调用UI动画 235 if (WidgetType == EMenuType::None) return; 236 237 //添加菜单的下属组件 238 for (TArray<TSharedPtr<SCompoundWidget>>::TIterator It((*MenuMap.Find(WidgetType))->ChildWidget); It; It++) 239 { 240 ContentBox->AddSlot().AutoHeight()[(*It)->AsShared()]; 241 } 242 243 //更改标题 244 TitleText->SetText((*MenuMap.Find(WidgetType))->MenuName); 245 246 //修改Size 247 ResetWidgetSize(600.f, (*MenuMap.Find(WidgetType))->MenuHeight); 248 } 249 250 //如果不修改高度,NewHeight传入-1 251 void SSlAiMenuWidget::ResetWidgetSize(float NewWidget, float NewHeight) 252 { 253 RootSizeBox->SetWidthOverride(NewWidget); 254 if (NewHeight < 0) 255 { 256 return; 257 } 258 RootSizeBox->SetHeightOverride(NewHeight); 259 }
=================================================================================================================================================================================================
MenuMap