HOW TO ADD PLUGIN AND CONTENTS TO AN EXISTING PROJECT
[spoiler]
For an existing Blueprint only project
- Add a new C++ class on your own, for example a Game Mode C++ class
- Close the editor and VS2015
- Copy and Paste the “Plugins” folder into your project folder (is the same folder of yourproject.uproject file)
- Right click on yourproject.uproject, select “Generate Visual Studio project files”
- Open your project solution with VS2015, in yourproject.Build.cs add “MainMenuLib” to PublicDependencyModuleNames like this
PublicDependencyModuleNames.AddRange(new string] { "Core", "CoreUObject", "Engine", "InputCore", "MainMenuLib" });
and recompile your solution
- Copy the “GameMenu” folder into your “Content” folder, now you are ready to go! (use my example project as reference on how to create Menu and InGameMenu widget).
For an existing C++ project
Skip step 1.
[/spoiler]
GAME INSTANCE
[spoiler]
I’m using game instance to pass Game Settings between levels, as you can notice the BP_GameInstance blueprint include four variables and four event dispatchers. The variables are never used in my example project (but i set anyway), the event dispatchers are used to send data to our player character in the Level_1 level blueprint. I call these dispatchers in the GameSettingsDetails widget when i change settings value like the FOV via slider and when i load values from saved data or if i set defaults values.
And here is where i bind these event dispatchers in Level_1 level blueprint
[/spoiler]
GAME SETTINGS
[spoiler]
The GameSettingsDetails allow you to edit FOV, Mouse Sensitivity and Mouse Axis orientation, as i said i pass this values to our player character via game instance event dispatchers, here i want to show you how i set the mouse sensitivity and axis orientation.
In our ThirdPersonCharacter blueprint i edited the InputAxis Turn and InputAxis LookUp events, i multiplied the Axis Value with two variables, in this way i can invert the orientation of the axis and scale his value.
The XAxisOrientation (and YAxisOrientation) variable are always -1 or 1, i change the value via the SetIsXAxisInverted (and SetIsYAxisInverted) like this
[/spoiler]
GRAPHICS SETTINGS
Graphics settings system is based on the Persistent graphics settings in UE4 tutorial by Impetus Games, you can find the source code into the Blueprint Function Library BPGraphicsSettingsLib.cpp (.h)
CONTROLS SETTINGS
[spoiler]
The controls settings system use the Rama’s UMG rebindable key system source code, you can find it into the Blueprint Function Library BPControlsSettingsLib.cpp (.h)
The only noticeable fix a done in the Rama’s system is about capture the Left Mouse Button on rebind a key, the original blueprint part of Rama’s project have a little bug, you can’t bind the LMB because if you click on button to rebind it detect the LMB as a click on button and not as a command to rebind, to fix this i disable the button on click and i re-enable on mouse button (and key button) event.
Here where i disable the button into the VictoryElement widget
and here where i re-enable it into the ControlsSettingsDetails widget OnMouseButtonDown and OnKeyDown functions override
[/spoiler]
AUDIO SETTINGS
[spoiler]
For the audio settings system i created four SoundClasses, the MasterSound SoundClass is the main sound class and the others (Effects, Music, Ambient) are his children, in this way with the MasterSound SoundClass you can pilot the other SoundClasses all together.
Now you can use your SoundClasses into SoundCues, for example i used the AmbientSound SoundClass into the AmbientBirds SoundCue like this
Now to set the volume of your SoundCue based on the SoundClasses you use the SetSoundClassVolume method you find in the BPAudioSettingsLib C++ code, as you can see this method is exposed to blueprint because it have the UFUNCTION(BlueprintCallable, …) macro.
What it do is very simple, it get a SoundClass as parameter and set his volume:
BPAudioSettingsLib.h
UCLASS()
class MAINMENU_API UBPAudioSettingsLib : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
// Set the SoundClass volume
UFUNCTION(BlueprintCallable, Category = "Menu Settings | Audio Settings")
static void SetSoundClassVolume(USoundClass* SoundClass, float Volume = 1.f);
// Get the SoundClass volume
UFUNCTION(BlueprintCallable, Category = "Menu Settings | Audio Settings")
static float GetSoundClassVolume(USoundClass* SoundClass);
};
BPAudioSettingsLib.cpp
#include "MainMenu.h"
#include "BPAudioSettingsLib.h"
// Set the SoundClass volume
void UBPAudioSettingsLib::SetSoundClassVolume(USoundClass* SoundClass, float Volume)
{
if (SoundClass)
{
SoundClass->Properties.Volume = Volume;
}
}
// Get the SoundClass volume
float UBPAudioSettingsLib::GetSoundClassVolume(USoundClass* SoundClass)
{
if (SoundClass)
{
return SoundClass->Properties.Volume;
}
return 0.f;
}
Now you can use these nodes like i done in the AudioSettingsDetails widget
[/spoiler]
ARCHEO RADIO BUTTONS
[spoiler]
The only fix i made to the Archeo Radio button is add an event dispatcher and call it when the radio button is selected
for the rest look at his tutorial
[/spoiler]
BLUEPRINT INTERFACE
[spoiler]
I used a blueprint interface to manage the behaviour of ApplySettings and Defaults buttons you can find in the SettingsSubMenu widget, these buttons “make stuff” based on which widget is active in the DetailsPanel widget switcher (will be more clear later and if you looking into the project).
The SubMenuInterface contains two functions: CallApplySettings and CallDefaultSettings, these function are implemented where needed like, for example, in the AudioSettingsDetails widget:
The ControlsSettingsDetails implement only the CallDefaultSettings because the Rama’s system save the new key bind a runtime and we don’t need to do it manually.
In the SettingsSubMenu widget we bind the Selected event dispatchers for all SelectableButton on construct, both for Apply and Default buttons and for the other submenu buttons, in the below picture i show you how i bind the dispatchers:
and here how i send the SubMenuInterface messages to the active DetailsPanel
[/spoiler]
SAVE/LOAD DATA
[spoiler]
For graphics settings and input key bindings we don’t need to use a SaveGame class because the source code save to data respectively to
C:\Users<user name>\AppData\Local\MainMenu\Saved\Config\WindowsNoEditor\GameUserSettings.ini for graphics settings
C:\Users<user name>\AppData\Local\MainMenu\Saved\Config\WindowsNoEditor\Input.ini for controls settings (key bindings).
Instead we use SaveGame class to save Game Settings (FOV, Mouse Sensitivity, Axis orientation) and Audio settings (Master volume, Music volume, Effects volume, Ambient volume), and we save these data to two separate SaveGame class, BP_AudioSettingsSave and BP_GameSettingsSave, these class create two files into C:\Users<user name>\AppData\Local\MainMenu\Saved\SaveGames folder, AudioSettings.sav and GameSettings.sav
You can find how i managed save and load into the AudioSettingsDetails and GameSettingsDetails widgets, is very easy stuff and is based to official documentation example.
[/spoiler]
HOW TO ADD A NEW MENU/SUBMENU VOICE?
[spoiler]
Add a new menu/submenu is very easy to do, thanks to how RadioButtonsLine work, for example if you look into MainMenu widget designer you will notice i used a RadioButtonsLine for menu buttons
if you go in the Details panel of RadioButtonsLine you can find in the Default section an array named “Buttons Names”, this array allow you to add/remove buttons to the line
if you want to add a new menu voice simply insert an element to the array before the QUIT button (very important!) like this
an important thing is the array index of where reside the button will become the ID of the button (ButtonID), this is important because with the ButtonID you will pilot what to show into the SubMenuPanel widget switcher, go look how it work.
In the MainMenu EventGraph we bind the “Selected” event dispatcher for all buttons we find into the MenuButtonsLine RadioButtonLine and we use this bind to call the OpenSubMenu function when a button is selected and we pass to this function the ButtonID (the index of the array on which position the button is in the line)
Now open the OpenSubMenu function graph, as you can notice on first we check if we are clicked a button associated to an active (opened) SubMenuPanel (in this case the sub lines of buttons or the Credits panel), this passage could be difficulty to understand but instead is very easy, the widget switcher work with the index of his childs, in our case at index = 0 of widget switcher we have the EmptyWidget, no buttons are associated to EmptyWidget, this means the button with ButtonID = 0 need to be associated to WidgetSwitcher index = 1 to open the correct panel.
We check if the SubMenuID (that it takes the ButtonID from previous bind) is the same of the actual active WidgetSwitcher index, if it is SubMenuID + 1 means we clicked the button of an active panel and we switch to the EmptyWidget, in this way we have the illusion of close the active panel!
If instead the SubMenuID is not equal to SubMenuID + 1 mean we have clicked a different button and we can open the associated panel (switch).
Now we know this mechanism (sorry if is not clear but is hard to explain with my poor English, is more simple understand it on look the graph when you running the menu and debug it) we have to “bind” our new button to do something on click, and this is very easy, you only have to create the widget you want to show when you click the new button and add it to the widget switcher in the right hierarchy position, you need to put it at the SwitcherWidget index = ButtonID +1 like this
after this come back to the OpenSubMenu function and add a pin to the Switch on Int node
move the link of the Quit Game node from 3 to 4
now add the SubMenuPanel variable to the graph, add the SetActiveWidget node and put the new (in this example case) “NewCreditsSubMenu” to the Widget pinr of SetActiveWidget node and link the node to the pin 3 of Switch to Int node
now link the SetActiveWidget node to the PlayAnimation node
run the project and the new menu button is ready to use and it work!
Do the same process for sub menu!
[/spoiler]
GAMEMODE
[spoiler]
I’m using two Game Modes in the project, one for the MainMenu level, this level is the entry level of the project, you will always start from here in the packaged project, and one for the Level_1 level.
The MainMenu level use the BP_MenuGameMode blueprint, as you can see is it have all the stuff set to default, the Level_1 level instead use the BP_GameMode blueprint and the only difference is the Default Pawn Class set to ThirdPersonCharacter.
This is why i use Game Instance in Menu (and in the In Game Menu) to pass value to our character, in this way you don’t need to bind the menu to a specific character, for example, if you made a drivable vehicle you can easily bind the new character to Menu values via Game Instance, the Menu is free from any character dependency.
[/spoiler]