UE4 从无到有纯 C++ & Slate 开发沙盒游戏(十三) 进入游戏控件
下图为菜单结构,到目前为止已经实现了游戏设置控件(绿色的部分),还缺少开始游戏控件,这个部将会开始实现 "输入存档名" 与 "选择存档" 的部分
首先我们在 UI/Widget 文件夹下新建两个 SlateWidget 组件
SlAiNewGameWidget 与 SlAiChooseRecordWidget
d:\ue4 project\ue26.2\courseproject\slaicourse\Source\SlAiCourse\Public\UI\Widget\SSlAiNewGameWidget.h
1 #include "CoreMinimal.h" 2 #include "Widgets/SCompoundWidget.h" 3 4 class SEditableTextBox; 5 6 /** 7 * 8 */ 9 class SLAICOURSE_API SSlAiNewGameWidget : public SCompoundWidget 10 { 11 public: 12 SLATE_BEGIN_ARGS(SSlAiNewGameWidget) 13 {} 14 SLATE_END_ARGS() 15 16 /** Constructs this widget with InArgs */ 17 void Construct(const FArguments& InArgs); 18 19 //是否可以进入游戏 20 bool AllowEnterGame(); 21 22 private: 23 //获取MenuStyle 24 const struct FSlAiMenuStyle* MenuStyle; 25 26 //输入框指针 27 TSharedPtr<SEditableTextBox> EditTextBox; 28 };
D:\UE4 Project\UE26.2\CourseProject\SlAiCourse\Source\SlAiCourse\Private\UI\Widget\SSlAiNewGameWidget.cpp
1 #include "UI/Widget/SSlAiNewGameWidget.h" 2 #include "SlateOptMacros.h" 3 #include "UI/Style/SlAiStyle.h" 4 #include "UI/Style/SlAiMenuWidgetStyle.h" 5 #include "Widgets/Layout/SBox.h" //一版会把SlateWidget的根组件设成SBox 6 #include "Widgets/Text/STextBlock.h" //文本块部件 7 #include "Widgets/SOverlay.h" //布局控件 8 #include "Widgets/Input/SEditableTextBox.h" //输入框部件 9 #include "Widgets/Images/SImage.h" //图片控件 10 11 #include "Data/SlAiDataHandle.h" 12 13 14 15 BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION 16 void SSlAiNewGameWidget::Construct(const FArguments& InArgs) 17 { 18 //获取MenuStyle 19 MenuStyle = &SlAiStyle::Get().GetWidgetStyle<FSlAiMenuStyle>("BPSlAiMenuStyle"); 20 21 ChildSlot 22 [ 23 SNew(SBox) 24 .WidthOverride(500.f) 25 .HeightOverride(100.f) 26 [ 27 SNew(SOverlay) 28 29 +SOverlay ::Slot() //添加按钮图片 30 .HAlign(HAlign_Fill) 31 .VAlign(VAlign_Fill) 32 [ 33 SNew(SImage) 34 .Image(&MenuStyle->MenuItemBrush) 35 ] 36 37 +SOverlay::Slot() //添加按钮文字 38 .HAlign(HAlign_Left) 39 .VAlign(VAlign_Center) 40 .Padding(FMargin(20.f, 0.f, 0.f, 0.f)) //FMargin偏移方式为:左上右下 41 [ 42 SNew(STextBlock) 43 .Font(MenuStyle->Font_40) 44 .Text(NSLOCTEXT("SlAiMenu", "NewGame", "NewGame")) 45 ] 46 47 +SOverlay::Slot() //添加输入框 48 .HAlign(HAlign_Right) 49 .VAlign(VAlign_Center) 50 .Padding(FMargin(0.f, 0.f, 20.f, 0.f)) 51 [ 52 SNew(SBox) 53 .WidthOverride(300.f) 54 .HeightOverride(60.f) 55 [ 56 SAssignNew(EditTextBox, SEditableTextBox) 57 .HintText(NSLOCTEXT("SlAiMenu", "RecordNameHint", "Input Record Name!")) 58 .Font(MenuStyle->Font_30) 59 ] 60 ] 61 ] 62 ]; 63 } 64 END_SLATE_FUNCTION_BUILD_OPTIMIZATION 65 66 bool SSlAiNewGameWidget::AllowEnterGame() 67 { 68 //获取输入的新存档名 69 FText InputText = EditTextBox->GetText(); 70 71 //文字是否为空 72 if (InputText.ToString().IsEmpty()) //ToString 可以将 FText 转换为 FString 73 { 74 return false; 75 } 76 else 77 { 78 for (TArray<FString>::TIterator It(SlAiDataHandle::Get()->RecordDataList); It; It++) 79 { 80 if ((*It).Equals(InputText.ToString())) //Equals 测试字符串是否与参数字符串相等 81 { 82 //设置 TextBox 为空 83 EditTextBox->SetText(FText::FromString("")); 84 //修改Hint为存档名重复 85 EditTextBox->SetHintText(NSLOCTEXT("SlAiMenu", "NameRepeatedHint", "Record Name Repeated!")); 86 return false; 87 } 88 } 89 } 90 //保存新的存档名 91 SlAiDataHandle::Get()->RecordName = InputText.ToString(); 92 93 return true; 94 }
D:\UE4 Project\UE26.2\CourseProject\SlAiCourse\Source\SlAiCourse\Public\UI\Widget\SSlAiChooseRecordWidget.h
1 #include "CoreMinimal.h" 2 #include "Widgets/SCompoundWidget.h" 3 4 class STextComboBox; 5 /** 6 * 7 */ 8 class SLAICOURSE_API SSlAiChooseRecordWidget : public SCompoundWidget 9 { 10 public: 11 SLATE_BEGIN_ARGS(SSlAiChooseRecordWidget) 12 {} 13 SLATE_END_ARGS() 14 15 /** Constructs this widget with InArgs */ 16 void Construct(const FArguments& InArgs); 17 18 //更新存档名 19 void UpDateRecordName(); 20 21 private: 22 //获取MenuStyle 23 const struct FSlAiMenuStyle* MenuStyle; 24 25 //文字下拉菜单指针 26 TSharedPtr<STextComboBox> RecordComboBox; 27 28 //存放下拉菜单内容的字符串数组 29 TArray<TSharedPtr<FString>> OptionsSoure; 30 31 };
D:\UE4 Project\UE26.2\CourseProject\SlAiCourse\Source\SlAiCourse\Private\UI\Widget\SSlAiChooseRecordWidget.cpp
1 #include "UI/Widget/SSlAiChooseRecordWidget.h" 2 #include "SlateOptMacros.h" 3 #include "UI/Style/SlAiStyle.h" 4 #include "UI/Style/SlAiMenuWidgetStyle.h" 5 #include "Widgets/Layout/SBox.h" //一版会把SlateWidget的根组件设成SBox 6 #include "Widgets/Text/STextBlock.h" //文本块部件 7 #include "Widgets/SOverlay.h" //布局控件 8 #include "Widgets/Images/SImage.h" //图片控件 9 #include "Widgets/Input/STextComboBox.h" //文字下拉菜单部件 10 #include "Data/SlAiDataHandle.h" 11 12 BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION 13 void SSlAiChooseRecordWidget::Construct(const FArguments& InArgs) 14 { 15 //获取MenuStyle 16 MenuStyle = &SlAiStyle::Get().GetWidgetStyle<FSlAiMenuStyle>("BPSlAiMenuStyle"); 17 18 //初始化下拉菜单 19 for (TArray<FString>::TIterator It(SlAiDataHandle::Get()->RecordDataList); It; It++) 20 { 21 OptionsSoure.Add(MakeShareable(new FString(*It))); 22 } 23 24 ChildSlot 25 [ 26 SNew(SBox) 27 .WidthOverride(500.f) 28 .HeightOverride(100.f) 29 [ 30 SNew(SOverlay) 31 32 +SOverlay ::Slot() //添加按钮图片 33 .HAlign(HAlign_Fill) 34 .VAlign(VAlign_Fill) 35 [ 36 SNew(SImage) 37 .Image(&MenuStyle->MenuItemBrush) 38 ] 39 40 +SOverlay::Slot() //添加按钮文字 41 .HAlign(HAlign_Left) 42 .VAlign(VAlign_Center) 43 .Padding(FMargin(20.f, 0.f, 0.f, 0.f)) //FMargin偏移方式为:左上右下 44 [ 45 SNew(STextBlock) 46 .Font(MenuStyle->Font_40) 47 .Text(NSLOCTEXT("SlAiMenu", "NewGame", "NewGame")) 48 ] 49 50 +SOverlay::Slot() //添加选择栏 51 .HAlign(HAlign_Right) 52 .VAlign(VAlign_Center) 53 .Padding(FMargin(0.f, 0.f, 20.f, 0.f)) 54 [ 55 SNew(SBox) 56 .WidthOverride(300.f) 57 .HeightOverride(60.f) 58 [ 59 SAssignNew(RecordComboBox, STextComboBox) 60 .Font(MenuStyle->Font_30) 61 .OptionsSource(&OptionsSoure) 62 63 //SAssignNew(EditTextBox, SEditableTextBox) 64 //.HintText(NSLOCTEXT("SlAiMenu", "RecordNameHint", "Input Record Name!")) 65 //.Font(MenuStyle->Font_30) 66 ] 67 ] 68 ] 69 ]; 70 71 //设置下拉菜单默认的选项 72 RecordComboBox->SetSelectedItem(OptionsSoure[0]); 73 } 74 END_SLATE_FUNCTION_BUILD_OPTIMIZATION 75 76 void SSlAiChooseRecordWidget::UpDateRecordName() 77 { 78 //GetSelectedItem 返回当前选中的字符串 79 SlAiDataHandle::Get()->RecordName = *RecordComboBox->GetSelectedItem().Get(); 80 }
SlAiDataHandle.h下添加存档名的声明
d:\ue4 project\ue26.2\courseproject\slaicourse\Source\SlAiCourse\Public\Data\SlAiDataHandle.h
1 #include "SlAiTypes.h" 2 #include "CoreMinimal.h" 3 4 5 class SLAICOURSE_API SlAiDataHandle 6 { 7 public: 8 SlAiDataHandle(); 9 10 static void Initialize(); 11 12 static TSharedPtr<SlAiDataHandle> Get(); 13 14 //修改语言 15 void ChangeLocalizationCulture(ECultureTeam Culture); 16 //修改菜单音量大小 17 void ResetMenuVolume(float MusicVol, float SoundVol); 18 19 public: 20 /** 21 * 这里没有使用 GamePlay 框架的 GameInstance,变量直接写到C++类中除非你主动销毁,否则他会一直存在 22 */ 23 //语言状态 24 ECultureTeam CurrentCulture; 25 //音乐状态 26 float MusicVolume; 27 //音效状态 28 float SoundVolume; 29 //存档数据 30 TArray<FString> RecordDataList; 31 //存档名 32 FString RecordName; 33 34 private: 35 //创建单例 36 static TSharedRef<SlAiDataHandle> Create(); 37 38 //根据enum类型获取字符串(该方法只能用于UENUM()反射后的枚举,普通枚举不能这样搞) 39 template<typename TEnum> 40 FString GetEnumValueAsString(const FString& Name, TEnum Value); 41 42 //根据字符串获取enum值(该方法只能用于UENUM()反射后的枚举,普通枚举不能这样搞) 43 template<typename TEnum> 44 TEnum GetEnumValueFromString(const FString& Name, FString Value); 45 46 //初始化存档数据 47 void InitRecordData(); 48 49 private: 50 static TSharedPtr<SlAiDataHandle> DataInstance; 51 };
对RecordName初始化
d:\ue4 project\ue26.2\courseproject\slaicourse\Source\SlAiCourse\Private\Data\SlAiDataHandle.cpp
1 #include "Data/SlAiDataHandle.h" 2 #include "Internationalization/Internationalization.h" 3 #include "Data/SlAiSingleton.h" 4 #include "Data/SlAiJsonHandle.h" 5 #include "Common/SlAiHelper.h" 6 7 TSharedPtr<SlAiDataHandle> SlAiDataHandle::DataInstance = NULL; 8 9 SlAiDataHandle::SlAiDataHandle() 10 { 11 ////初始化中文 12 //CurrentCulture = ECultureTeam::ZH; 13 ////初始化音乐 14 //MusicVolume = 0.5f; 15 ////初始化音效 16 //SoundVolume = 0.5f; 17 18 //初始化存档数据 19 InitRecordData(); 20 } 21 22 void SlAiDataHandle::Initialize() 23 { 24 if (!DataInstance.IsValid()) 25 { 26 DataInstance = Create(); 27 } 28 } 29 30 TSharedPtr<SlAiDataHandle> SlAiDataHandle::Get() 31 { 32 Initialize(); 33 return DataInstance; 34 } 35 36 TSharedRef<SlAiDataHandle> SlAiDataHandle::Create() 37 { 38 /** 39 *MakeShareable 可以用来创建共享指针和共享引用 40 */ 41 TSharedRef<SlAiDataHandle> DataRef = MakeShareable(new SlAiDataHandle()); 42 return DataRef; 43 } 44 45 void SlAiDataHandle::ChangeLocalizationCulture(ECultureTeam Culture) 46 { 47 switch (Culture) 48 { 49 case ECultureTeam::EN: 50 FInternationalization::Get().SetCurrentCulture(TEXT("en")); 51 break; 52 case ECultureTeam::ZH: 53 FInternationalization::Get().SetCurrentCulture(TEXT("zh")); 54 break; 55 } 56 //赋值 57 CurrentCulture = Culture; 58 /* 59 更新存档数据*/ 60 SlAiSingleton<SlAiJsonHandle>::Get()->UpdateRecordData(GetEnumValueAsString<ECultureTeam>(FString("ECultureTeam"), CurrentCulture), MusicVolume, SoundVolume, &RecordDataList); 61 } 62 63 void SlAiDataHandle::ResetMenuVolume(float MusicVol, float SoundVol) 64 { 65 if (MusicVol > 0) 66 { 67 MusicVolume = MusicVol; 68 } 69 if (SoundVol > 0) 70 { 71 SoundVolume = SoundVol; 72 } 73 /* 74 更新存档数据*/ 75 SlAiSingleton<SlAiJsonHandle>::Get()->UpdateRecordData(GetEnumValueAsString<ECultureTeam>(FString("ECultureTeam"), CurrentCulture), MusicVolume, SoundVolume, &RecordDataList); 76 } 77 78 //根据枚举值返回字符串 79 template<typename TEnum> 80 FString SlAiDataHandle::GetEnumValueAsString(const FString& Name, TEnum Value) 81 { 82 //通过UE4的FindObject寻找所有ANY_PACKAGE反射到的Name,寻找到后实例化后返回 83 const UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, *Name, true); 84 85 if (!EnumPtr) 86 { 87 return FString("InValid"); 88 } 89 return EnumPtr->GetEnumName((int32)Value); 90 } 91 92 //根据字符串返回枚举值 93 template<typename TEnum> 94 TEnum SlAiDataHandle::GetEnumValueFromString(const FString& Name, FString Value) 95 { 96 const UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, *Name, true); 97 98 if (!EnumPtr) 99 { 100 return TEnum(0); 101 } 102 return (TEnum)EnumPtr->GetIndexByName(FName(*FString(Value))); 103 } 104 105 void SlAiDataHandle::InitRecordData() 106 { 107 //获取语言 108 FString Culture; 109 110 //使用单例读取存档数据 111 SlAiSingleton<SlAiJsonHandle>::Get()->RecordDataJsonRead(Culture, MusicVolume, SoundVolume, RecordDataList); 112 113 //初始化语言 114 ChangeLocalizationCulture(GetEnumValueFromString<ECultureTeam>(FString("ECultureTeam"), Culture)); 115 116 RecordName = FString(""); 117 }
在主菜单组件中调用,这里只调用了 SSlAiChooseRecordWidget 用作测试,也可以将 SSlAiNewGameWidget 添加到这里测试
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" //一版会把SlateWidget的根组件设成SBox 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 BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION 19 void SSlAiMenuWidget::Construct(const FArguments& InArgs) 20 { 21 //获取MenuStyle 22 MenuStyle = &SlAiStyle::Get().GetWidgetStyle<FSlAiMenuStyle>("BPSlAiMenuStyle"); 23 /** 24 *切换语言 25 //FInternationalization::Get().SetCurrentCulture(TEXT("en")); 26 //FInternationalization::Get().SetCurrentCulture(TEXT("ch")); 27 */ 28 //转换为中文 29 SlAiDataHandle::Get()->ChangeLocalizationCulture(ECultureTeam::ZH); 30 31 ChildSlot 32 [ 33 /** 34 *没有Slot,没有Slot要么不能插入子组件,要么只能插入一个子组件,SizeBox 只能插入一个子组件 35 */ 36 SAssignNew(RootSizeBox, SBox) 37 [ 38 SNew(SOverlay) 39 40 +SOverlay::Slot() //主菜单背景 41 .HAlign(HAlign_Fill) 42 .VAlign(VAlign_Fill) 43 .Padding(FMargin(0.f, 50.f, 0.f, 0.f)) //FMargin 间隔(左 上 右 下) 44 [ 45 SNew(SImage) 46 .Image(&MenuStyle->MenuBackgroundBrush) 47 ] 48 49 +SOverlay::Slot() //菜单左侧图片 50 .HAlign(HAlign_Left) 51 .VAlign(VAlign_Center) 52 .Padding(FMargin(0.f, 25.f, 0.f, 0.f)) 53 [ 54 SNew(SImage).Image(&MenuStyle->LeftIconBrush) 55 ] 56 57 +SOverlay::Slot() //菜单右侧图片 58 .HAlign(HAlign_Right) 59 .VAlign(VAlign_Center) 60 .Padding(FMargin(0.f, 25.f, 0.f, 0.f)) 61 [ 62 SNew(SImage).Image(&MenuStyle->RightIconBrush) 63 ] 64 65 +SOverlay::Slot() //菜单标题图片 66 .HAlign(HAlign_Center) 67 .VAlign(VAlign_Top) 68 [ 69 SNew(SBox) 70 .WidthOverride(400.f) 71 .HeightOverride(100.f) 72 [ 73 SNew(SBorder) 74 .BorderImage(&MenuStyle->TitleBorderBrush) 75 .HAlign(HAlign_Center) 76 .VAlign(VAlign_Center) 77 [ 78 SAssignNew(TitleText, STextBlock) 79 .Font(SlAiStyle::Get().GetFontStyle("MenuItemFort")) 80 .Text(NSLOCTEXT("SlAiMenu", "Menu", "Menu")) 81 .Font(MenuStyle->Font_60) 82 ] 83 ] 84 ] 85 86 +SOverlay::Slot() //菜单按钮组件 87 .HAlign(HAlign_Center) 88 .VAlign(VAlign_Top) 89 .Padding(FMargin(0.f, 130.f, 0.f, 0.f)) 90 [ 91 //菜单组件创建到这里 92 SAssignNew(ContentBox, SVerticalBox) 93 ] 94 ] 95 ]; 96 97 /** 98 * RootSizeBox在生成的时候没有设置他的宽和高,这里设置下 99 */ 100 RootSizeBox->SetWidthOverride(600.f); 101 RootSizeBox->SetHeightOverride(510.f); 102 103 ContentBox->AddSlot() 104 [ 105 SNew(SSlAiChooseRecordWidget) 106 ]; 107 } 108 109 END_SLATE_FUNCTION_BUILD_OPTIMIZATION 110 111 void SSlAiMenuWidget::MenuItemOnClicked(EMenuItem::Type ItemType) 112 { 113 //SlAiHelper::Debug(FString("TEXT"), 5.f); 114 } 115 116 void SSlAiMenuWidget::ChangeCulture(ECultureTeam Culture) 117 { 118 SlAiDataHandle::Get()->ChangeLocalizationCulture(Culture); 119 } 120 121 void SSlAiMenuWidget::ChangeVolume(const float MusicVolume, const float SoundVolume) 122 { 123 SlAiDataHandle::Get()->ResetMenuVolume(MusicVolume, SoundVolume); 124 }
===========================================================================================================================================
………………