Fork me on GitHub

UE4 从无到有纯 C++ & Slate 开发沙盒游戏(十六) UI音乐播放与调节

到目前为止主菜单基本功能均已实现,目前还差进入游戏、进入存档、退出游戏、UI音乐音效四个部分,这部分将会实现UI音效

 

声音文件要通过 WidgetStyle 来获取,所以要在 MenuWidgetStyle.h 文件下添加注册

 D:\UE4 Project\UE26.2\CourseProject\SlAiCourse\Source\SlAiCourse\Public\UI\Style\SlAiMenuWidgetStyle.h

  1 #include "CoreMinimal.h"
  2 #include "Styling/SlateWidgetStyle.h"
  3 #include "Styling/SlateWidgetStyleContainerBase.h"
  4 #include "Styling/SlateBrush.h"
  5 #include "Fonts/SlateFontInfo.h"
  6 #include "Sound/SlateSound.h"
  7 
  8 #include "SlAiMenuWidgetStyle.generated.h"
  9 
 10 
 11 /**
 12  * 
 13  */
 14 USTRUCT()
 15 struct SLAICOURSE_API FSlAiMenuStyle : public FSlateWidgetStyle        //构造体
 16 {
 17     GENERATED_USTRUCT_BODY()
 18 
 19     FSlAiMenuStyle();
 20     virtual ~FSlAiMenuStyle();
 21 
 22     // FSlateWidgetStyle
 23     virtual void GetResources(TArray<const FSlateBrush*>& OutBrushes) const override;
 24     static const FName TypeName;
 25     virtual const FName GetTypeName() const override { return TypeName; };
 26     static const FSlAiMenuStyle& GetDefault();
 27 
 28     /*定义笔刷
 29     *     主背景图片*/
 30     UPROPERTY(EditAnywhere, Category = MenuHUD)
 31         FSlateBrush MenuHUDBackgroundBrush;
 32 
 33     /*定义笔刷
 34     *     主背景图片*/
 35     UPROPERTY(EditAnywhere, Category = Menu)
 36         FSlateBrush MenuBackgroundBrush;
 37 
 38     /*定义笔刷
 39     *     Menu左图标的Brush*/
 40     UPROPERTY(EditAnywhere, Category = Menu)
 41         FSlateBrush LeftIconBrush;
 42 
 43     /*定义笔刷
 44     *     Menu右图标的Brush*/
 45     UPROPERTY(EditAnywhere, Category = Menu)
 46         FSlateBrush RightIconBrush;
 47 
 48     /*定义笔刷
 49     *     Menu标题Border的Brush*/
 50     UPROPERTY(EditAnywhere, Category = Menu)
 51         FSlateBrush TitleBorderBrush;
 52 
 53     /*定义笔刷
 54     *     MenuItem的Brush*/
 55     UPROPERTY(EditAnywhere, Category = MenuItem)
 56         FSlateBrush MenuItemBrush;
 57 
 58     /**
 59     * 30号字体    */
 60     UPROPERTY(EditAnywhere, Category = Common)
 61         FSlateFontInfo Font_30;
 62 
 63     /**
 64     * 40号字体    */
 65     UPROPERTY(EditAnywhere, Category = Common)
 66         FSlateFontInfo Font_40;
 67 
 68     /**
 69     * 60号字体    */
 70     UPROPERTY(EditAnywhere, Category = Common)
 71         FSlateFontInfo Font_60;
 72 
 73     /**
 74     * 白色颜色    */
 75     UPROPERTY(EditAnywhere, Category = Common)
 76         FSlateColor FontColor_White;
 77     
 78     /**
 79     * 黑色颜色    */
 80     UPROPERTY(EditAnywhere, Category = Common)
 81         FSlateColor FontColor_Black;
 82 
 83     /**
 84     * GameSet的背景*/
 85     UPROPERTY(EditAnywhere, Category = GameOption)
 86         FSlateBrush GameOptionBGBrush;
 87 
 88     /**
 89     * CheckedBox的Brush被选中*/
 90     UPROPERTY(EditAnywhere, Category = GameOption)
 91         FSlateBrush CheckedBoxBrush;
 92 
 93     /**
 94     * CheckedBox的Brush不被选中*/
 95     UPROPERTY(EditAnywhere, Category = GameOption)
 96         FSlateBrush UnCheckedBoxBrush;
 97 
 98     /**
 99     * slider的背景Brush*/
100     UPROPERTY(EditAnywhere, Category = GameOption)
101         FSlateBrush SliderBarBrush;
102 
103     /**
104     * 指定slider的样式*/
105     UPROPERTY(EditAnywhere, Category = GameOption)
106         FSliderStyle SliderStyle;
107 
108     /**
109      * 开始游戏声音 */
110     UPROPERTY(EditAnywhere, Category = Sound)
111         FSlateSound StartGameSound;
112 
113     /**
114      * 结束游戏声音 */
115     UPROPERTY(EditAnywhere, Category = Sound)
116         FSlateSound ExitGameSound;
117 
118     /**
119      * 转换按钮声音 */
120     UPROPERTY(EditAnywhere, Category = Sound)
121         FSlateSound MenuItemChangeSound;
122 
123     /**
124      * 菜单背景声音 */
125     UPROPERTY(EditAnywhere, Category = Sound)
126         FSlateSound MenuBackgroudMusic;
127 };
128 
129 /**
130  */
131 UCLASS(hidecategories=Object, MinimalAPI)
132 class USlAiMenuWidgetStyle : public USlateWidgetStyleContainerBase
133 {
134     GENERATED_BODY()
135 
136 public:
137     /** The actual data describing the widget appearance. */
138     UPROPERTY(Category=Appearance, EditAnywhere, meta=(ShowOnlyInnerProperties))
139     FSlAiMenuStyle WidgetStyle;
140 
141     virtual const struct FSlateWidgetStyle* const GetStyle() const override
142     {
143         return static_cast< const struct FSlateWidgetStyle* >( &WidgetStyle );
144     }
145 };

 

 然后再UE4中,MenuSound文件夹下添加 wav 的音乐文件,(MP3的不能使用)

 

  每一个音乐文件对应一个Cue,新建Cue后对其进行编辑

 

拖入音乐文件,将文件与Output连接,保存

 

然后再 BPSlAiMenuStyle 文件中对 Sound 进行配置。这个部分是让 C++ 调用对应的音乐蓝图

 

然后再代码中的相关事件,调用相关的音频资源

这里将要实现一个延时调用方法的功能,点击播放完音频后调用相关事件

D:\UE4 Project\UE26.2\CourseProject\SlAiCourse\Source\SlAiCourse\Public\Common\SlAiHelper.h

 1 #include "CoreMinimal.h"
 2 #include "Engine/World.h"
 3 #include "Engine/GameEngine.h"
 4 #include "Framework/Application/SlateApplication.h"
 5 #include "Engine/GameEngine.h"
 6 
 7 namespace SlAiHelper
 8 {
 9     FORCEINLINE void Debug(FString Message, float Duration = 3.f)
10     {
11         if (GEngine)
12         {
13             //输出一个持续X时间的黄色文字到控制台
14             GEngine->AddOnScreenDebugMessage(-1, Duration, FColor::Yellow, Message);
15         }
16     }
17 
18     /**
19      * 播放一段音乐后调用一个方法
20      * FTimerHandle 时间控制的一个句柄
21      * UWorld 的指针可以获取到 TimeWidget
22      * FSlateSound 使UBaseSound可用于 Slate 播放声音的中介
23      * FTimerDelegate 函数委托指针,TRawMethodDelegate 声明基于c++指针的委托实例类型,委托定义的那个类型的指针
24      */
25     template<class UserClass>
26     FORCEINLINE FTimerHandle PlayerSoundAndCall(UWorld* World, const FSlateSound Sound, UserClass* InUserObject, typename FTimerDelegate::TRawMethodDelegate<UserClass>::FMethodPtr InMethod)
27     {
28         //先播放传进来的音乐
29         FSlateApplication::Get().PlaySound(Sound);
30         //时间控制的句柄
31         FTimerHandle Result;
32         //获取音乐播放的长度(时间), 最少0.1秒
33         const float SoundDuration = FMath::Max(FSlateApplication::Get().GetSoundDuration(Sound), 0.1f);
34         //时间委托
35         FTimerDelegate Callback;
36         //给 Callback 这个委托绑定 传入的UserClass的指针,以及 FMethodPtr 的函数委托指针
37         Callback.BindRaw(InUserObject, InMethod);
38         //通过 World 获取时间管理器,设置Time,传入时间控制的句柄、时间委托、延迟时间、
39         World->GetTimerManager().SetTimer(Result, Callback, SoundDuration, false);
40 
41         return Result;
42     }
43 }
44 
45 /**
46  * 定时器相关知识
47  * 定义定时器句柄    FTimerHandle mTimer
48  * 定义调用的委托 FTimerDelegate timeDele;
49  * 绑定函数到委托 timeDele.BindRaw(this, &xxx:fff);
50  * 获取时间控制器并且启动定时器
51  *
52  * Gworld->GetTimerManager().SetTimer(mlime, timeDele, 1.f, true);
53  * Gworld->GetTimerManager().PauseTimer(mTimer);                    //暂停
54  * Gworld->GetTimerManager().UnPauseTimer(mTimer);                //唤醒
55  * Gworld->GetTimerManager().ClearTimer(mlimer);                    //清除计时器
56  */

 

 

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     //重写tick函数,动画播放过程中调用,判断EMenuAnim::Type AnimState 当前的状态
25     virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
26 
27 private:
28     //绑定到各个MenuItem的方法
29     void MenuItemOnClicked(EMenuItem::Type ItemType);
30     /**
31      * 由于MenuWidget是主菜单,我们要在这里实例化所有的功能控件,所有的事件都会放到这里
32      * 这样的好处是到后面加了其他场景后,用到这些组件的时候,实例化一个别的方法添加到事件里,就可以重复调用了
33      */
34     //修改语言
35     void ChangeCulture(ECultureTeam Culture);
36     //修改音量
37     void ChangeVolume(const float MusicVolume, const float SoundVolume);
38     //初始化所有的控件
39     void InitializedMenuList();
40     //选择显示的界面
41     void ChooseWidget(EMenuType::Type WidgetType);
42     //修改菜单大小
43     void ResetWidgetSize(float NewWidget, float NewHeight);
44     //初始化动画组件
45     void InitializedAnimation();
46     //播放关闭动画
47     void PlayClose(EMenuType::Type NewMenu);
48     //退出游戏
49     void QuitGame();
50     //进入游戏
51     void EnterGame();
52 
53 private:
54     //保存根节点,用来动态的修改SBox的大小
55     TSharedPtr<SBox> RootSizeBox;
56     //获取MenuStyle
57     const struct FSlAiMenuStyle* MenuStyle;
58     //保存标题
59     TSharedPtr<STextBlock> TitleText;
60     //用来保存垂直列表,所有的菜单按钮组件都会放置到这ContentBox中
61     TSharedPtr<SVerticalBox> ContentBox;
62     //保存菜单组
63     TMap<EMenuType::Type, TSharedPtr<MenuGroup>> MenuMap;
64     //游戏设置Widget的指引
65     TSharedPtr<SSlAiGameOptionWidget> GameOptionWidget;
66     //新游戏控件指针
67     TSharedPtr<SSlAiNewGameWidget> NewGameWidget;
68     //选择存档控件指针
69     TSharedPtr<SSlAiChooseRecordWidget> ChooseRecordWidget;
70 
71     //动画播放器
72     FCurveSequence MenuAnimation;
73     //曲线控制器
74     FCurveHandle MenuCurve;
75     //用来保存新的长度
76     float CurrentHeight;
77     //是否已经显示Menu组件,显示为Ture
78     bool IsMenuShow;
79     //是否锁住按钮,锁住为Ture
80     bool ControlLocked;
81     //保存当前的动画状态
82     EMenuAnim::Type AnimState;
83     //保存当前的菜单
84     EMenuType::Type CurrentMenu;
85 };

 

 

实现相关逻辑

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 #include "Kismet/GameplayStatics.h"
 16 #include "GamePlay/SlAiMenuController.h"
 17 
 18 
 19 /**
 20  * 每一个结构体对应一个菜单*/
 21 struct MenuGroup
 22 {
 23     //菜单标题
 24     FText MenuName;
 25     //菜单高度
 26     float MenuHeight;
 27     //下属组件
 28     TArray<TSharedPtr<SCompoundWidget>> ChildWidget;
 29 
 30     //构造函数
 31     MenuGroup(const FText Name, const float Height, TArray<TSharedPtr<SCompoundWidget>>* Children)
 32     {
 33         MenuName = Name;
 34         MenuHeight = Height;
 35         for (TArray<TSharedPtr<SCompoundWidget>>::TIterator It(*Children); It; It++)
 36         {
 37             ChildWidget.Add(*It);
 38         }
 39     }
 40 };
 41 
 42 
 43 BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
 44 void SSlAiMenuWidget::Construct(const FArguments& InArgs)
 45 {
 46     //获取MenuStyle
 47     MenuStyle = &SlAiStyle::Get().GetWidgetStyle<FSlAiMenuStyle>("BPSlAiMenuStyle");
 48 
 49     //转换为中文
 50     //SlAiDataHandle::Get()->ChangeLocalizationCulture(ECultureTeam::ZH);
 51 
 52     //播放背景音乐
 53     FSlateApplication::Get().PlaySound(MenuStyle->MenuBackgroudMusic);
 54 
 55     ChildSlot
 56     [
 57         /**
 58         *没有Slot,没有Slot要么不能插入子组件,要么只能插入一个子组件,SizeBox 只能插入一个子组件
 59         */
 60         SAssignNew(RootSizeBox, SBox)
 61         [
 62             SNew(SOverlay)
 63 
 64             +SOverlay::Slot()                                        //主菜单背景
 65                 .HAlign(HAlign_Fill)
 66                 .VAlign(VAlign_Fill)
 67                 .Padding(FMargin(0.f, 50.f, 0.f, 0.f))        //FMargin 间隔(左 上 右 下)
 68                 [
 69                     SNew(SImage)
 70                     .Image(&MenuStyle->MenuBackgroundBrush)
 71                 ]
 72 
 73             +SOverlay::Slot()                                        //菜单左侧图片
 74                 .HAlign(HAlign_Left)
 75                 .VAlign(VAlign_Center)
 76                 .Padding(FMargin(0.f, 25.f, 0.f, 0.f))
 77                 [
 78                     SNew(SImage).Image(&MenuStyle->LeftIconBrush)
 79                 ]
 80 
 81             +SOverlay::Slot()                                        //菜单右侧图片
 82                 .HAlign(HAlign_Right)
 83                 .VAlign(VAlign_Center)
 84                 .Padding(FMargin(0.f, 25.f, 0.f, 0.f))
 85                 [
 86                     SNew(SImage).Image(&MenuStyle->RightIconBrush)
 87                 ]
 88 
 89             +SOverlay::Slot()                                        //菜单标题图片
 90                 .HAlign(HAlign_Center)
 91                 .VAlign(VAlign_Top)
 92                 [
 93                     SNew(SBox)
 94                     .WidthOverride(400.f)
 95                     .HeightOverride(100.f)
 96                     [
 97                         SNew(SBorder)
 98                         .BorderImage(&MenuStyle->TitleBorderBrush)
 99                         .HAlign(HAlign_Center)
100                         .VAlign(VAlign_Center)
101                         [
102                             SAssignNew(TitleText, STextBlock)
103                             .Font(SlAiStyle::Get().GetFontStyle("MenuItemFort"))
104                             .Text(NSLOCTEXT("SlAiMenu", "Menu", "Menu"))
105                             .Font(MenuStyle->Font_60)
106                         ]
107                     ]
108                 ]
109 
110             +SOverlay::Slot()                            //菜单按钮组件
111                 .HAlign(HAlign_Center)
112                 .VAlign(VAlign_Top)
113                 .Padding(FMargin(0.f, 130.f, 0.f, 0.f))
114                 [
115                     //菜单组件创建到这里
116                     SAssignNew(ContentBox, SVerticalBox)
117                 ]
118         ]
119     ];
120     InitializedMenuList();
121     InitializedAnimation();
122 }
123 
124 void SSlAiMenuWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
125 {
126     switch (AnimState)
127     {
128     case EMenuAnim::Stop:
129         break;
130     case EMenuAnim::Close:                        //如果菜单为关闭状态
131         if (MenuAnimation.IsPlaying())
132         {
133             //MenuCurve.GetLerp()  动态修改Menu的大小,从1到0
134             ResetWidgetSize(MenuCurve.GetLerp() * 600.f, -1.f);
135             //Menu的大小在缩小了40%的时候不显示组件
136             if (MenuCurve.GetLerp() < 0.6f && IsMenuShow) ChooseWidget(EMenuType::None);
137         }
138         else
139         {
140             //动画播放完毕,GetLerp到0后,设置状态为打开
141             AnimState = EMenuAnim::Open;
142             //开始播放打开的动画, Play 0到1
143             MenuAnimation.Play(this->AsShared());
144         }
145         break;
146     case EMenuAnim::Open:
147         //如果正在播放
148         if (MenuAnimation.IsPlaying())
149         {
150             //实时修改Menu的大小
151             ResetWidgetSize(MenuCurve.GetLerp() * 600.f, CurrentHeight);
152             //打开60%后显示组件, 并且没有显示组件, 调用ChooseWidget(),WidgetType != EMenuType::None 时 IsMenuShow 将会变为True
153             if (MenuCurve.GetLerp() > 0.6f && !IsMenuShow) ChooseWidget(CurrentMenu);
154         }
155         if (MenuAnimation.IsAtEnd())
156         {
157             //修改动画状态为停止
158             AnimState = EMenuAnim::Stop;
159             //解锁按钮
160             ControlLocked = false;
161         }
162         break;
163     }
164 }
165 
166 END_SLATE_FUNCTION_BUILD_OPTIMIZATION
167 
168 //按钮点击事件,实现界面跳转
169 void SSlAiMenuWidget::MenuItemOnClicked(EMenuItem::Type ItemType)
170 {
171     //如果锁住了,直接return
172     if (ControlLocked) return;
173     //设置锁住按钮,避免播放动画过程中操作其他按钮造成bug
174     ControlLocked = true;
175 
176     switch (ItemType)
177     {
178     case EMenuItem::StartGame:
179         PlayClose(EMenuType::StartGame);
180         break;
181     case EMenuItem::GameOption:
182         PlayClose(EMenuType::GameOption);
183         break;
184     case EMenuItem::QuitGame:
185         //退出游戏,播放声音并且延时调用退出函数
186         SlAiHelper::PlayerSoundAndCall(UGameplayStatics::GetPlayerController(GWorld, 0)->GetWorld(), MenuStyle->ExitGameSound, this, &SSlAiMenuWidget::QuitGame);
187         break;
188     case EMenuItem::NewGame:
189         PlayClose(EMenuType::NewGame);
190         break;
191     case EMenuItem::LoadRecord:
192         PlayClose(EMenuType::ChooseRecord);
193         break;
194     case EMenuItem::StartGameGoBack:
195         PlayClose(EMenuType::MainMenu);
196         break;
197     case EMenuItem::GameOptionGoBack:
198         PlayClose(EMenuType::MainMenu);
199         break;
200     case EMenuItem::NewGameGoBack:
201         PlayClose(EMenuType::StartGame);
202         break;
203     case EMenuItem::ChooseRecordGoBack:
204         PlayClose(EMenuType::StartGame);
205         break;
206     case EMenuItem::EnterGame:
207         //进入游戏,播放声音并且延时调用进入游戏函数
208         SlAiHelper::PlayerSoundAndCall(UGameplayStatics::GetPlayerController(GWorld, 0)->GetWorld(), MenuStyle->StartGameSound, this, &SSlAiMenuWidget::EnterGame);
209         break;
210     case EMenuItem::EnterRecord:
211         //进入游戏,播放声音并且延时调用进入存档函数
212         SlAiHelper::PlayerSoundAndCall(UGameplayStatics::GetPlayerController(GWorld, 0)->GetWorld(), MenuStyle->StartGameSound, this, &SSlAiMenuWidget::EnterGame);
213         break;
214     }
215 }
216 
217 void SSlAiMenuWidget::ChangeCulture(ECultureTeam Culture)
218 {
219     SlAiDataHandle::Get()->ChangeLocalizationCulture(Culture);
220 }
221 
222 void SSlAiMenuWidget::ChangeVolume(const float MusicVolume, const float SoundVolume)
223 {
224     SlAiDataHandle::Get()->ResetMenuVolume(MusicVolume, SoundVolume);
225 }
226 
227 /*
228 * 游戏运行后会调用到 InitializedMenuList() 实例化所有的界面组件
229 */
230 void SSlAiMenuWidget::InitializedMenuList()
231 {
232     //实例化主界面
233     TArray<TSharedPtr<SCompoundWidget>> MainMenuList;
234     MainMenuList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "StartGame", "StartGame")).ItemType(EMenuItem::StartGame).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked));
235     MainMenuList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "GameOption", "GameOption")).ItemType(EMenuItem::GameOption).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked));
236     MainMenuList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "QuitGame", "QuitGame")).ItemType(EMenuItem::QuitGame).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked));
237 
238     MenuMap.Add(EMenuType::MainMenu, MakeShareable(new MenuGroup(NSLOCTEXT("SlAiMenu", "Menu", "Menu"), 510.f, &MainMenuList)));
239 
240     //开始游戏界面
241     TArray<TSharedPtr<SCompoundWidget>> StartGameList;
242     StartGameList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "NewGame", "NewGame")).ItemType(EMenuItem::NewGame).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked));
243     StartGameList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "LoadRecord", "LoadRecord")).ItemType(EMenuItem::LoadRecord).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked));
244     StartGameList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "GoBack", "GoBack")).ItemType(EMenuItem::StartGameGoBack).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked));
245 
246     MenuMap.Add(EMenuType::StartGame, MakeShareable(new MenuGroup(NSLOCTEXT("SlAiMenu", "StartGame", "StartGame"), 510.f, &StartGameList)));
247 
248     //游戏设置界面
249     TArray<TSharedPtr<SCompoundWidget>> GameOptionList;
250     //实例化游戏设置Widget(语言和声音)
251     SAssignNew(GameOptionWidget, SSlAiGameOptionWidget).ChangeCulture(this, &SSlAiMenuWidget::ChangeCulture).ChangeVolume(this, &SSlAiMenuWidget::ChangeVolume);
252     GameOptionList.Add(GameOptionWidget);
253     GameOptionList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "GoBack", "GoBack")).ItemType(EMenuItem::GameOptionGoBack).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked));
254 
255     MenuMap.Add(EMenuType::GameOption, MakeShareable(new MenuGroup(NSLOCTEXT("SlAiMenu", "GameOption", "GameOption"), 610.f, &GameOptionList)));
256 
257     //新游戏界面
258     TArray<TSharedPtr<SCompoundWidget>> NewGameList;
259     SAssignNew(NewGameWidget, SSlAiNewGameWidget);
260     NewGameList.Add(NewGameWidget);
261     NewGameList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "EnterGame", "EnterGame")).ItemType(EMenuItem::EnterGame).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked));
262     NewGameList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "GoBack", "GoBack")).ItemType(EMenuItem::NewGameGoBack).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked));
263 
264     MenuMap.Add(EMenuType::NewGame, MakeShareable(new MenuGroup(NSLOCTEXT("SlAiMenu", "NewGame", "NewGame"), 510.f, &NewGameList)));
265 
266     //选择存档界面
267     TArray<TSharedPtr<SCompoundWidget>> ChooseRecordList;
268     SAssignNew(ChooseRecordWidget, SSlAiChooseRecordWidget);
269     ChooseRecordList.Add(ChooseRecordWidget);
270     ChooseRecordList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "EnterRecord", "EnterRecord")).ItemType(EMenuItem::EnterRecord).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked));
271     ChooseRecordList.Add(SNew(SSlAiMenuItemWidget).ItemText(NSLOCTEXT("SlAiMenu", "GoBack", "GoBack")).ItemType(EMenuItem::ChooseRecordGoBack).OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked));
272 
273     MenuMap.Add(EMenuType::ChooseRecord, MakeShareable(new MenuGroup(NSLOCTEXT("SlAiMenu", "LoadRecord", "LoadRecord"), 510.f, &ChooseRecordList)));
274 }
275 
276 void SSlAiMenuWidget::ChooseWidget(EMenuType::Type WidgetType)
277 {
278     //是否已经显示菜单,显示菜单为True
279     IsMenuShow = WidgetType != EMenuType::None;
280 
281     //移除所有组件
282     ContentBox->ClearChildren();
283 
284     if (WidgetType == EMenuType::None) return;
285 
286     //添加菜单的下属组件
287     for (TArray<TSharedPtr<SCompoundWidget>>::TIterator It((*MenuMap.Find(WidgetType))->ChildWidget); It; It++)
288     {
289         ContentBox->AddSlot().AutoHeight()[(*It)->AsShared()];
290     }
291 
292     //更改标题
293     TitleText->SetText((*MenuMap.Find(WidgetType))->MenuName);
294 }
295 
296 //如果不修改高度,NewHeight传入-1
297 void SSlAiMenuWidget::ResetWidgetSize(float NewWidget, float NewHeight)
298 {
299     RootSizeBox->SetWidthOverride(NewWidget);
300     if (NewHeight < 0)
301     {
302         return;
303     }
304     RootSizeBox->SetHeightOverride(NewHeight);
305 }
306 
307 /**
308  * FCurveSequence 动画播放器
309  * FCurveHandle 是曲线控制器
310  * 我们会通过动画控制器 FCurveSequence 的一些方法 (如 Play Stop 等……) 让曲线 FCurveHandle 0到1、1到0实时的变化
311  * 我们可以实时的获取曲线 FCurveHandle 的值,获取曲线的值动态的修改界面的大小
312  */
313 void SSlAiMenuWidget::InitializedAnimation()
314 {
315     //开始延时
316     const float StartDelay = 0.3f;
317 
318     //持续时间
319     const float AnimDuration = 0.6f;
320 
321     //将动画播放器实例化
322     MenuAnimation = FCurveSequence();
323 
324     //曲线控制器注册进动画播放器,参数:开始延时、持续时间、播放类型
325     MenuCurve = MenuAnimation.AddCurve(StartDelay, AnimDuration, ECurveEaseFunction::QuadInOut);
326 
327     //初始设置Menu大小
328     ResetWidgetSize(600.f, 510.f);
329     
330     //初始显示主界面
331     ChooseWidget(EMenuType::MainMenu);
332 
333     //是否允许点击按钮
334     ControlLocked = false;
335 
336     //设置动画状态为停止
337     AnimState = EMenuAnim::Stop;
338 
339     //设置动画播放器跳到结尾 1
340     MenuAnimation.JumpToEnd();
341 }
342 
343 void SSlAiMenuWidget::PlayClose(EMenuType::Type NewMenu)
344 {
345     //设置新的界面
346     CurrentMenu = NewMenu;
347     //设置新高度
348     CurrentHeight = (*MenuMap.Find(CurrentMenu))->MenuHeight;
349     //设置播放状态是Close, 代表关闭Menu
350     AnimState = EMenuAnim::Close;
351     //播放反向动画,从1播放到0,动画播放器初始化时 MenuAnimation.JumpToEnd(); 设置到了1
352     MenuAnimation.PlayReverse(this->AsShared());
353     //播放切换菜单音乐
354     FSlateApplication::Get().PlaySound(MenuStyle->MenuItemChangeSound);
355 }
356 
357 void SSlAiMenuWidget::QuitGame()
358 {
359     //Cast 是一个类型转换的方法,控制台输入 quit
360     Cast<ASlAiMenuController>(UGameplayStatics::GetPlayerController(GWorld, 0))->ConsoleCommand("quit");
361 }
362 
363 void SSlAiMenuWidget::EnterGame()
364 {
365     SlAiHelper::Debug(FString("EnterGame"), 10.f);
366     ControlLocked = false;        //测试用,免得解不了锁
367 }

 

到这里便可以正常播放音效

接下来写设置音量和音效大小的部分

先在 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 class USoundCue;
 5 
 6 class SLAICOURSE_API SlAiDataHandle
 7 {
 8 public:
 9     SlAiDataHandle();
10 
11     static void Initialize();
12 
13     static TSharedPtr<SlAiDataHandle> Get();
14 
15     //修改语言
16     void ChangeLocalizationCulture(ECultureTeam Culture);
17     //修改菜单音量大小
18     void ResetMenuVolume(float MusicVol, float SoundVol);
19 
20 public:
21     /**
22     * 这里没有使用 GamePlay 框架的 GameInstance,变量直接写到C++类中除非你主动销毁,否则他会一直存在
23     */
24     //语言状态
25     ECultureTeam CurrentCulture;
26     //音乐状态
27     float MusicVolume;
28     //音效状态
29     float SoundVolume;
30     //存档数据
31     TArray<FString> RecordDataList;
32     //存档名
33     FString RecordName;
34 
35 private:
36     //创建单例
37     static TSharedRef<SlAiDataHandle> Create();
38 
39     //根据enum类型获取字符串(该方法只能用于UENUM()反射后的枚举,普通枚举不能这样搞)
40     template<typename TEnum>
41     FString GetEnumValueAsString(const FString& Name, TEnum Value);
42 
43     //根据字符串获取enum值(该方法只能用于UENUM()反射后的枚举,普通枚举不能这样搞)
44     template<typename TEnum>
45     TEnum GetEnumValueFromString(const FString& Name, FString Value);
46 
47     //初始化存档数据
48     void InitRecordData();
49 
50     //初始化 Menu 声音数据
51     void InitializedMenuAudio();
52 
53 private:
54     static TSharedPtr<SlAiDataHandle> DataInstance;
55 
56     //保存 Menu 的声音
57     TMap<FString, TArray<USoundCue*>> MenuAudioResource;
58 
59     //获取 MenuStyle  里边存放有声音文件
60     const struct FSlAiMenuStyle* MenuStyle;
61 
62 };

 

 

实现相关功能

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 #include "UI/Style/SlAiStyle.h"
  7 #include "UI/Style/SlAiMenuWidgetStyle.h"
  8 #include "Sound/SoundCue.h"
  9 
 10 TSharedPtr<SlAiDataHandle> SlAiDataHandle::DataInstance = NULL;
 11 
 12 SlAiDataHandle::SlAiDataHandle()
 13 {
 14     //初始化存档数据
 15     InitRecordData();
 16     //初始化音乐数据
 17     InitializedMenuAudio();
 18 
 19 }
 20 
 21 void SlAiDataHandle::Initialize()
 22 {
 23     if (!DataInstance.IsValid())
 24     {
 25         DataInstance = Create();
 26     }
 27 }
 28 
 29 TSharedPtr<SlAiDataHandle> SlAiDataHandle::Get()
 30 {
 31     Initialize();
 32     return DataInstance;
 33 }
 34 
 35 TSharedRef<SlAiDataHandle> SlAiDataHandle::Create()
 36 {
 37     /**
 38     *MakeShareable 可以用来创建共享指针和共享引用
 39     */
 40     TSharedRef<SlAiDataHandle> DataRef = MakeShareable(new SlAiDataHandle());
 41     return DataRef;
 42 }
 43 
 44 void SlAiDataHandle::ChangeLocalizationCulture(ECultureTeam Culture)
 45 {
 46     switch (Culture)
 47     {
 48     case ECultureTeam::EN:
 49         FInternationalization::Get().SetCurrentCulture(TEXT("en"));
 50         break;
 51     case ECultureTeam::ZH:
 52         FInternationalization::Get().SetCurrentCulture(TEXT("zh"));
 53         break;
 54     }
 55     //赋值
 56     CurrentCulture = Culture;
 57     /*
 58     更新存档数据*/
 59     SlAiSingleton<SlAiJsonHandle>::Get()->UpdateRecordData(GetEnumValueAsString<ECultureTeam>(FString("ECultureTeam"), CurrentCulture), MusicVolume, SoundVolume, &RecordDataList);
 60 }
 61 
 62 void SlAiDataHandle::ResetMenuVolume(float MusicVol, float SoundVol)
 63 {
 64     if (MusicVol > 0)
 65     {
 66         MusicVolume = MusicVol;
 67         //循环设置所有背景音乐的音量
 68         for (TArray<USoundCue*>::TIterator It(MenuAudioResource.Find(FString("Music"))->CreateIterator()); It; It++)
 69         {
 70             //设置音量
 71             (*It)->VolumeMultiplier = MusicVolume;
 72         }
 73     }
 74     if (SoundVol > 0)
 75     {
 76         SoundVolume = SoundVol;
 77         //循环设置所有音效的音量
 78         for (TArray<USoundCue*>::TIterator It(MenuAudioResource.Find(FString("Sound"))->CreateIterator()); It; It++)
 79         {
 80             //设置音量
 81             (*It)->VolumeMultiplier = SoundVolume;
 82         }
 83     }
 84     /*
 85     更新存档数据*/
 86     SlAiSingleton<SlAiJsonHandle>::Get()->UpdateRecordData(GetEnumValueAsString<ECultureTeam>(FString("ECultureTeam"), CurrentCulture), MusicVolume, SoundVolume, &RecordDataList);
 87 }
 88 
 89 //根据枚举值返回字符串
 90 template<typename TEnum>
 91 FString SlAiDataHandle::GetEnumValueAsString(const FString& Name, TEnum Value)
 92 {
 93     //通过UE4的FindObject寻找所有ANY_PACKAGE反射到的Name,寻找到后实例化后返回
 94     const UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, *Name, true);
 95 
 96     if (!EnumPtr)
 97     {
 98         return FString("InValid");
 99     }
100     return EnumPtr->GetEnumName((int32)Value);
101 }
102 
103 //根据字符串返回枚举值
104 template<typename TEnum>
105 TEnum SlAiDataHandle::GetEnumValueFromString(const FString& Name, FString Value)
106 {
107     const UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, *Name, true);
108 
109     if (!EnumPtr)
110     {
111         return TEnum(0);
112     }
113     return (TEnum)EnumPtr->GetIndexByName(FName(*FString(Value)));
114 }
115 
116 void SlAiDataHandle::InitRecordData()
117 {
118     //获取语言
119     FString Culture;
120 
121     //使用单例读取存档数据
122     SlAiSingleton<SlAiJsonHandle>::Get()->RecordDataJsonRead(Culture, MusicVolume, SoundVolume, RecordDataList);
123 
124     //初始化语言
125     ChangeLocalizationCulture(GetEnumValueFromString<ECultureTeam>(FString("ECultureTeam"), Culture));
126 
127     RecordName = FString("");
128 }
129 
130 void SlAiDataHandle::InitializedMenuAudio()
131 {
132     //获取 MenuStyle
133     MenuStyle = &SlAiStyle::Get().GetWidgetStyle<FSlAiMenuStyle>("BPSlAiMenuStyle");
134 
135     //添加资源文件到资源列表,添加前先转成USoundCue
136     TArray<USoundCue*> MusicList;
137     MusicList.Add(Cast<USoundCue>(MenuStyle->MenuBackgroudMusic.GetResourceObject()));
138     TArray<USoundCue*> SoundList;
139     SoundList.Add(Cast<USoundCue>(MenuStyle->StartGameSound.GetResourceObject()));
140     SoundList.Add(Cast<USoundCue>(MenuStyle->ExitGameSound.GetResourceObject()));
141     SoundList.Add(Cast<USoundCue>(MenuStyle->MenuItemChangeSound.GetResourceObject()));
142 
143     //添加资源到Map
144     MenuAudioResource.Add(FString("Music"), MusicList);
145     MenuAudioResource.Add(FString("Sound"), SoundList);
146 
147     //重制一下声音
148     ResetMenuVolume(MusicVolume, SoundVolume);
149 }

 

 

 

 

 

 

 

==============================================================================================================================

 

posted @ 2021-09-13 16:38  索智源  阅读(597)  评论(0编辑  收藏  举报