Hi Guys ,
This week I worked on dynamically binding input keyboard and mouse values based on user’s choice in keyboard customization section. Here is how I did it.
First I created an excel table containing the following columns. I only gave two examples of keyboard actions, you will have as many rows as the number of different action event and key binding you want to have in your game.
[table=“width: 500, class: grid”]
**Action**
**DefaultInput**
**Input**
**Category**
Primary Attack
MouseLeftButton
MouseLeftButton
Combat
Move Forward
W
W
Movement
Next I created a struct for my datatable in my HUD header class in C++. Make sure to add the following namespace
“Runtime/Engine/Classes/Engine/DataTable.h” in the header class.
USTRUCT(Blueprintable)
struct FKeyBoardBindingTable : public FTableRowBase
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadOnly, Category = "KeyBinding")
FString Action;
UPROPERTY(BlueprintReadOnly, Category = "KeyBinding")
FString DefaultInput;
UPROPERTY(BlueprintReadOnly, Category = "KeyBinding")
FString Input;
UPROPERTY(BlueprintReadOnly, Category = "KeyBinding")
FString Category;
};
Now define your datatable in HUD header -
UDataTable* KeyBoardBindingTable;
and initialize it the HUD constructor class
static ConstructorHelpers::FObjectFinder<UDataTable> KeyBoardBindingTable_BP(TEXT("DataTable'/Game/DataTables/KeyboardConfig.KeyboardConfig'"));
KeyBoardBindingTable = KeyBoardBindingTable_BP.Object;
I imported this excel as datatable in UE4 , inherited from the struct FKeyBoardBindingTable.
Then , I created userinterface class for keyboard configuration menu. , deriving from SCompounWidget , and wrote the UI in slate architecture. The UI is simple key customization UI that you can see in any game , when user clicks on an input , it asks you to bind it to a new value , then you have to press a key to bind to it. See below.
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SKeyConfigUI::Construct(const FArguments& InArgs)
{
GameHUD = InArgs._GameHUD;
MenuStyle = &FMenuStyles::Get().GetWidgetStyle<FGlobalStyle>("Global");
ChildSlot.Padding(50 , 10)
SNew(SOverlay)
+ SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding(40)
SNew(SBorder)
.ColorAndOpacity(FLinearColor::White)
.BorderImage(&MenuStyle->BorderBrush1)
.Content()
SNew(SOverlay)
+ SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
SAssignNew(ParentWidget , SScrollBox)
+ SScrollBox::Slot()
.HAlign(HAlign_Left)
.Padding(40, 80, 0, 0)
SNew(STextBlock)
.Text(FText::FromString("KeyBoard Configuration"))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/jancient.ttf"), 30))
.ColorAndOpacity(FLinearColor(0.8, 0.7, 0.3, 1))
.ShadowColorAndOpacity(FLinearColor(0.16, 0.06, 0.05, 1))
]
+ SScrollBox::Slot()
.HAlign(HAlign_Fill)
.Padding(40, 60, 40, 0)
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Top)
SAssignNew(GeneralButton, SButton)
.ButtonStyle(&MenuStyle->KeyConfigHeaderStyle)
.ContentPadding(2)
.HAlign(HAlign_Center)
.VAlign(VAlign_Top)
.Content()
SAssignNew(GeneralText, STextBlock)
.Text(FText::FromString("General"))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf"), 15))
.ColorAndOpacity(FLinearColor(0.1, 0.1, 0.03, 1))
.ShadowColorAndOpacity(FLinearColor(0.93, 1, 0.6, 1))
.ShadowOffset(FVector2D(1, 1))
]
.OnClicked(this, &SKeyConfigUI::InputCaterGoryButonClicked, FString("General"))
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Top)
SAssignNew(MovementButton, SButton)
.ButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle)
.ContentPadding(2)
.HAlign(HAlign_Center)
.VAlign(VAlign_Top)
.Content()
SAssignNew(MovementText, STextBlock)
.Text(FText::FromString("Movement"))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf"), 15))
.ColorAndOpacity(FLinearColor(0.12, 0.1, 0.03, 1))
.ShadowColorAndOpacity(FLinearColor(0.93, 1, 0.6, 1))
.ShadowOffset(FVector2D(1, 1))
]
.OnClicked(this, &SKeyConfigUI::InputCaterGoryButonClicked, FString("Movement"))
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Top)
SAssignNew(CombatButton, SButton)
.ButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle)
.ContentPadding(2)
.HAlign(HAlign_Center)
.VAlign(VAlign_Top)
.Content()
SAssignNew(CombatText, STextBlock)
.Text(FText::FromString("Combat"))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf"), 15))
.ColorAndOpacity(FLinearColor(0.12, 0.1, 0.03, 1))
.ShadowColorAndOpacity(FLinearColor(0.93, 1, 0.6, 1))
.ShadowOffset(FVector2D(1, 1))
]
.OnClicked(this, &SKeyConfigUI::InputCaterGoryButonClicked, FString("Combat"))
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Top)
SAssignNew(MiscellaneousButton, SButton)
.ButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle)
.ContentPadding(2)
.HAlign(HAlign_Center)
.VAlign(VAlign_Top)
.Content()
SAssignNew(MiscellaneousText, STextBlock)
.Text(FText::FromString("Miscellaneous"))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf"), 15))
.ColorAndOpacity(FLinearColor(0.12, 0.1, 0.03, 1))
.ShadowColorAndOpacity(FLinearColor(0.93, 1, 0.6, 1))
.ShadowOffset(FVector2D(1, 1))
]
.OnClicked(this, &SKeyConfigUI::InputCaterGoryButonClicked, FString("Miscellaneous"))
]
]
]
+ SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding(FMargin(100, 250, 0, 50))
SNew(SScrollBox)
+ SScrollBox::Slot()
.HAlign(HAlign_Fill)
SAssignNew(KeyBindingWidget, SWidgetSwitcher)
+ SWidgetSwitcher::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Top)
SAssignNew(GeneralWidget, SScrollBox)
]
+ SWidgetSwitcher::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Top)
SAssignNew(MovementWidget, SScrollBox)
]
+ SWidgetSwitcher::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Top)
SAssignNew(CombatWidget, SScrollBox)
]
+ SWidgetSwitcher::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Top)
SAssignNew(MiscellaneousWidget, SScrollBox)
]
]
]
+ SOverlay::Slot()
.HAlign(HAlign_Center)
.VAlign(VAlign_Bottom)
.Padding(10)
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding(10, 0)
SNew(SButton)
.ButtonStyle(&MenuStyle->GeneralButtonStyle1)
.ContentPadding(2)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Content()
SNew(STextBlock)
.Text(FText::FromString(" Cancel "))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf"), 15))
.ColorAndOpacity(FLinearColor(0.7, 0.6, 0.1, 1))
]
.OnClicked(this, &SKeyConfigUI::CancelButtonClicked)
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Center)
.Padding(10, 0)
SNew(SButton)
.ButtonStyle(&MenuStyle->GeneralButtonStyle1)
.ContentPadding(2)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Content()
SNew(STextBlock)
.Text(FText::FromString(" Save "))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf"), 15))
.ColorAndOpacity(FLinearColor(0.7, 0.6, 0.1, 1))
]
.OnClicked(this, &SKeyConfigUI::SaveButtonClicked)
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Center)
.Padding(10, 0)
SNew(SButton)
.ButtonStyle(&MenuStyle->GeneralButtonStyle1)
.ContentPadding(2)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.Content()
SNew(STextBlock)
.Text(FText::FromString(" Reset "))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf"), 15))
.ColorAndOpacity(FLinearColor(0.7, 0.6, 0.1, 1))
]
.OnClicked(this, &SKeyConfigUI::ResetButtonClicked)
]
]
]
]
];
DrawKeyBindingWidget();
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SKeyConfigUI::DrawKeyBindingWidget()
{
int32 rowindex = 1;
GeneralWidget->ClearChildren();
MovementWidget->ClearChildren();
CombatWidget->ClearChildren();
MiscellaneousWidget->ClearChildren();
while (true)
{
FKeyBoardBindingTable* LookUpRow = GameHUD->KeyBoardBindingTable->FindRow<FKeyBoardBindingTable>(FName(*FString::FromInt(rowindex)), TEXT("Look Up"));
if (LookUpRow)
{
if (LookUpRow->Category == "General")
{
GeneralIndex++;
GeneralWidget->AddSlot().HAlign(HAlign_Fill).Padding(10)
DrawKeyBindingWidgetRow(LookUpRow->Action, LookUpRow->Input, LookUpRow->Category, rowindex).ToSharedRef()
];
}
else if (LookUpRow->Category == "Movement")
{
MovementIndex++;
MovementWidget->AddSlot().HAlign(HAlign_Fill).Padding(10)
DrawKeyBindingWidgetRow(LookUpRow->Action, LookUpRow->Input, LookUpRow->Category, rowindex).ToSharedRef()
];
}
else if (LookUpRow->Category == "Combat")
{
CombatIndex++;
CombatWidget->AddSlot().HAlign(HAlign_Fill).Padding(10)
DrawKeyBindingWidgetRow(LookUpRow->Action, LookUpRow->Input, LookUpRow->Category, rowindex).ToSharedRef()
];
}
else if (LookUpRow->Category == "Miscellaneous")
{
MiscellaneousIndex++;
MiscellaneousWidget->AddSlot().HAlign(HAlign_Fill).Padding(10)
DrawKeyBindingWidgetRow(LookUpRow->Action, LookUpRow->Input, LookUpRow->Category, rowindex).ToSharedRef()
];
}
rowindex++;
}
else
{
break;
}
}
}
TSharedPtr<class SHorizontalBox> SKeyConfigUI::DrawKeyBindingWidgetRow(FString Action, FString Input, FString Category , int32 Index)
{
return
SNew(SHorizontalBox)
+ SHorizontalBox::Slot().HAlign(HAlign_Center).VAlign(VAlign_Center)
SAssignNew(BindingText, STextBlock).Text(FText::FromString(Action))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/jancient.ttf"), 15))
.ColorAndOpacity(FLinearColor(0.8, 0.7, 0.3, 1))
.ShadowColorAndOpacity(FLinearColor(0.93, 1, 0.6, 1))
]
+ SHorizontalBox::Slot().HAlign(HAlign_Fill).VAlign(VAlign_Fill)
SAssignNew(BindingButton, SButton).ButtonStyle(&MenuStyle->KeyBindingButtonStyle).HAlign(HAlign_Center).VAlign(VAlign_Center)
.Content()
SAssignNew(BindingText, STextBlock).Text(FText::FromString(Input))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/jancient.ttf"), 15))
.ColorAndOpacity(FLinearColor(0.8, 0.7, 0.3, 1))
.ShadowColorAndOpacity(FLinearColor(0.93, 1, 0.6, 1))
]
.OnClicked(this, &SKeyConfigUI::KeyCustomizationButtonClicked, FString(Action), FString(Input) , Index)
];
}
FReply SKeyConfigUI::SaveButtonClicked()
{
int32 rowindex;
for (rowindex = 1; rowindex < TempIndex.Num(); rowindex++)
{
FKeyBoardBindingTable* LookUpRow = GameHUD->KeyBoardBindingTable->FindRow<FKeyBoardBindingTable>(FName(*TempIndex[rowindex]), TEXT("Look Up"));
if (LookUpRow)
{
LookUpRow->Input = TempInput[rowindex];
}
else
{
break;
}
}
return FReply::Handled();
}
FReply SKeyConfigUI::CancelButtonClicked()
{
DrawKeyBindingWidget();
return FReply::Handled();
}
FReply SKeyConfigUI::ResetButtonClicked()
{
int32 rowindex = 1;
while (true)
{
FKeyBoardBindingTable* LookUpRow = GameHUD->KeyBoardBindingTable->FindRow<FKeyBoardBindingTable>(FName(*FString::FromInt(rowindex)), TEXT("Look Up"));
if (LookUpRow)
{
LookUpRow->Input = LookUpRow->DefaultInput;
DrawKeyBindingWidget();
rowindex++;
}
else
{
break;
}
}
return FReply::Handled();
}
FReply SKeyConfigUI::InputCaterGoryButonClicked(FString Category)
{
if (Category == "General")
{
GeneralButton->SetButtonStyle(&MenuStyle->KeyConfigHeaderStyle);
MovementButton->SetButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle);
CombatButton->SetButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle);
MiscellaneousButton->SetButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle);
KeyBindingWidget->SetActiveWidgetIndex(0);
}
else if (Category == "Movement")
{
GeneralButton->SetButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle);
MovementButton->SetButtonStyle(&MenuStyle->KeyConfigHeaderStyle);
CombatButton->SetButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle);
MiscellaneousButton->SetButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle);
KeyBindingWidget->SetActiveWidgetIndex(1);
}
else if (Category == "Combat")
{
GeneralButton->SetButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle);
MovementButton->SetButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle);
CombatButton->SetButtonStyle(&MenuStyle->KeyConfigHeaderStyle);
MiscellaneousButton->SetButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle);
KeyBindingWidget->SetActiveWidgetIndex(2);
}
else if (Category == "Miscellaneous")
{
GeneralButton->SetButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle);
MovementButton->SetButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle);
CombatButton->SetButtonStyle(&MenuStyle->KeyConfigNotSelectedStyle);
MiscellaneousButton->SetButtonStyle(&MenuStyle->KeyConfigHeaderStyle);
KeyBindingWidget->SetActiveWidgetIndex(3);
TSharedPtr<class SWidget> border = SNew(SCanvas);
}
return FReply::Handled();
}
FReply SKeyConfigUI::KeyCustomizationButtonClicked(FString Key, FString Value , int32 Index)
{
Selectedkey = Key;
SelectedOldValue = Value;
SelectedIndex = Index-1;
CustomKeyWidget = SNew(SOverlay)
+ SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill).Padding(400)
SNew(SBorder)
.BorderImage(&MenuStyle->BorderBrush2)
.VAlign(VAlign_Center)
.Content()
SNew(SScrollBox)
+ SScrollBox::Slot()
.HAlign(HAlign_Center)
SNew(STextBlock)
.Text(FText::FromString("Press any key to map to"))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf"), 15))
.ColorAndOpacity(FLinearColor(0.7, 0.6, 0.1, 1))
]
+ SScrollBox::Slot()
.HAlign(HAlign_Center)
SNew(STextBlock)
.Text(FText::FromString(Key))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf"), 17))
.ColorAndOpacity(FLinearColor(0.46, 0.77, 0.73, 1))
]
+ SScrollBox::Slot()
.HAlign(HAlign_Center)
SNew(STextBlock)
.Text(FText::FromString("Press ESC to cancel"))
.Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf"), 15))
.ColorAndOpacity(FLinearColor(0.46, 0.77, 0.73, 1))
.ColorAndOpacity(FLinearColor(0.7, 0.6, 0.1, 1))
]
]
];
GEngine->GameViewport->AddViewportWidgetContent(CustomKeyWidget.ToSharedRef());
KeyBindingWidget->SetEnabled(false);
ParentWidget->SetEnabled(false);
return FReply::Handled();
}
FReply SKeyConfigUI::OnKeyDown(const FGeometry& MyGeometry, const FKeyboardEvent& InKeyboardEvent)
{
GEngine->GameViewport->RemoveViewportWidgetContent(CustomKeyWidget.ToSharedRef());
if (InKeyboardEvent.GetKey() == EKeys::Escape)
{
KeyBindingWidget->SetEnabled(true);
ParentWidget->SetEnabled(true);
}
else
{
NewKey = InKeyboardEvent.GetKey();
KeyBindingWidget->SetEnabled(true);
ParentWidget->SetEnabled(true);
FKeyBoardBindingTable* LookUpRow = GameHUD->KeyBoardBindingTable->FindRow<FKeyBoardBindingTable>(*FString::FromInt(SelectedIndex+1), TEXT("Look Up"));
int32 widgetIndex;
if (NewKey != FKey(FName(*LookUpRow->Input)))
{
TempIndex.Add(FString::FromInt(SelectedIndex + 1));
TempInput.Add(NewKey.ToString());
int index =KeyBindingWidget->GetActiveWidgetIndex();
switch (index)
{
case 0:
GeneralWidget->RemoveSlot(SelectedIndex);
GeneralWidget->InsertSlot(SelectedIndex).HAlign(HAlign_Fill).Padding(10)
DrawKeyBindingWidgetRow(LookUpRow->Action, NewKey.ToString(), LookUpRow->Category, SelectedIndex + 1).ToSharedRef()
];
break;
case 1:
widgetIndex = SelectedIndex - GeneralIndex;
MovementWidget->RemoveSlot(widgetIndex);
MovementWidget->InsertSlot(widgetIndex).HAlign(HAlign_Fill).Padding(10)
DrawKeyBindingWidgetRow(LookUpRow->Action, NewKey.ToString(), LookUpRow->Category, SelectedIndex + 1).ToSharedRef()
];
break;
case 2:
widgetIndex = SelectedIndex - GeneralIndex - MovementIndex;
CombatWidget->RemoveSlot(widgetIndex);
CombatWidget->InsertSlot(widgetIndex).HAlign(HAlign_Fill).Padding(10)
DrawKeyBindingWidgetRow(LookUpRow->Action, NewKey.ToString(), LookUpRow->Category, SelectedIndex + 1).ToSharedRef()
];
break;
case 3:
widgetIndex = SelectedIndex - GeneralIndex - MovementIndex - CombatIndex;
MiscellaneousWidget->RemoveSlot(widgetIndex);
MiscellaneousWidget->InsertSlot(widgetIndex).HAlign(HAlign_Fill).Padding(10)
DrawKeyBindingWidgetRow(LookUpRow->Action, NewKey.ToString(), LookUpRow->Category, SelectedIndex + 1).ToSharedRef()
];
break;
default:
break;
}
}
}
return FReply::Handled();
}
OK , and the below is my header class , in case you are confused with the above code.
class FORGOTTENLEGION_API SKeyConfigUI : public SCompoundWidget
{
SLATE_BEGIN_ARGS(SKeyConfigUI)
{
}
SLATE_ARGUMENT(TWeakObjectPtr<class AGameHUD>, GameHUD);
SLATE_END_ARGS()
public:
/** Constructs this widget with InArgs */
void Construct(const FArguments& InArgs);
bool FileIO__SaveStringTextToFile(FString SaveDirectory, FString FileName, FString SaveText, bool AllowOverWriting);
bool FileIO_ReadFileToString(FString FileDirectory, FString FilePath);
/** Overridden from SWidget: Called when a key is pressed down - capturing copy */
virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyboardEvent& InKeyboardEvent) override;
private:
SScrollBox::FSlot& InsertSlot(int32 Index);
FReply InputCaterGoryButonClicked(FString Category);
TSharedPtr<class SHorizontalBox> DrawKeyBindingWidgetRow(FString Action, FString Input, FString Category , int32 Index);
void DrawKeyBindingWidget();
const struct FGlobalStyle* MenuStyle;
FReply SaveButtonClicked();
FReply CancelButtonClicked();
FReply ResetButtonClicked();
FReply KeyCustomizationButtonClicked(FString Key, FString Value , int32 Index);
TWeakObjectPtr<class AGameHUD> GameHUD;
TSharedPtr<class SWidgetSwitcher> KeyBindingWidget = SNew(SWidgetSwitcher);
TSharedRef<FSlateGameResources> SlateResources = FSlateGameResources::New(FName("BorderBrush1"), "/Game/Blueprints/Widgets/Styles", "/Game/Blueprints/Widgets/Styles");
FSlateGameResources& Style = SlateResources.Get();
const FSlateBrush* BorderBrush1 = Style.GetBrush("BorderBrush1");
FLinearColor SelectedButtonColor = FLinearColor(0.58, 0.74, 0.89, 1);
FString Selectedkey;
FString SelectedOldValue;
int32 SelectedIndex;
int32 GeneralIndex = 0;;
int32 MovementIndex = 0;
int32 CombatIndex = 0;
int32 MiscellaneousIndex = 0;
TArray<FString> TempIndex;
TArray<FString> TempInput;
FKey NewKey;
TSharedPtr<class SButton> GeneralButton = SNew(SButton);
TSharedPtr<class SButton> MovementButton = SNew(SButton);
TSharedPtr<class SButton> CombatButton = SNew(SButton);
TSharedPtr<class SButton> MiscellaneousButton = SNew(SButton);
TSharedPtr<class STextBlock> GeneralText = SNew(STextBlock);
TSharedPtr<class STextBlock> MovementText = SNew(STextBlock);
TSharedPtr<class STextBlock> CombatText = SNew(STextBlock);
TSharedPtr<class STextBlock> MiscellaneousText = SNew(STextBlock);
TSharedPtr<class SScrollBox> ParentWidget = SNew(SScrollBox);
TSharedPtr<class SScrollBox> GeneralWidget = SNew(SScrollBox);
TSharedPtr<class SScrollBox> MovementWidget = SNew(SScrollBox);
TSharedPtr<class SScrollBox> CombatWidget = SNew(SScrollBox);
TSharedPtr<class SScrollBox> MiscellaneousWidget = SNew(SScrollBox);
TSharedPtr<class STextBlock> BindingText = SNew(STextBlock);
TSharedPtr<class SButton> BindingButton = SNew(SButton);
TSharedPtr<class SOverlay> CustomKeyWidget = SNew(SOverlay);
};
Now you can easily add this UI in your viewport in your HUD class , whenever required , using the below line
SAssignNew(KeyConfigUI, SKeyConfigUI).GameHUD(this);
if (GEngine && GEngine->GameViewport)
{
Viewport = GEngine->GameViewport;
Viewport->AddViewportWidgetContent(
SNew(SWeakWidget).PossiblyNullContent(KeyConfigUI.ToSharedRef())
);
}
Now our keyconfiguraiton UI is ready(please let me know if you find any bug). SO next I need to tell unreal engine , that whatever current value is present in the Input column of the datatable , I am supposed to use that for desired set of action. So for that I wrote the following code in my playercharacter class. Here , make sure you include the below namespaces
#include "Runtime/Engine/Classes/GameFramework/PlayerInput.h"
#include "Runtime/Engine/Classes/GameFramework/InputSettings.h"
#include "Runtime/CoreUObject/Public/UObject/UObjectGlobals.h"
void AForgottenLegionCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
// Set up gameplay key bindings
check(InputComponent);
const UInputSettings* InputSettings = GetDefault<UInputSettings>();
int rowindex = 1;
while (true)
{
FKeyBoardBindingTable* LookUpRow = KeyBoardBindingTable->FindRow<FKeyBoardBindingTable>(FName(*FString::FromInt(rowindex)), TEXT("Look Up"));
if (LookUpRow)
{
if (LookUpRow->Action == "Move Forward")
{
const FInputAxisKeyMapping axismapping(FName("Move Forward"), FKey(FName(*LookUpRow->Input)) , 1);
((UInputSettings*)InputSettings)->AddAxisMapping(axismapping);
}
else if (LookUpRow->Action == "Move Backward")
{
const FInputAxisKeyMapping axismapping(FName("Move Forward"), FKey(FName(*LookUpRow->Input)), -1);
((UInputSettings*)InputSettings)->AddAxisMapping(axismapping);
}
else if (LookUpRow->Action == "Move Left")
{
const FInputAxisKeyMapping axismapping(FName("Move Right"), FKey(FName(*LookUpRow->Input)), -1);
((UInputSettings*)InputSettings)->AddAxisMapping(axismapping);
}
else if (LookUpRow->Action == "Move Right")
{
const FInputAxisKeyMapping axismapping(FName("Move Right"), FKey(FName(*LookUpRow->Input)), 1);
((UInputSettings*)InputSettings)->AddAxisMapping(axismapping);
}
else
{
const FInputActionKeyMapping actionmapping(FName(*LookUpRow->Action), FKey(FName(*LookUpRow->Input)), false , false , false , false);
((UInputSettings*)InputSettings)->AddActionMapping(actionmapping);
}
rowindex++;
}
else
{
break;
}
}
const FInputAxisKeyMapping turnaxismapping(FName("Turn"), FKey(FName("MouseX")), 1);
((UInputSettings*)InputSettings)->AddAxisMapping(turnaxismapping);
const FInputAxisKeyMapping lookupaxismapping(FName("LookUp"), FKey(FName("MouseY")), 1);
((UInputSettings*)InputSettings)->AddAxisMapping(lookupaxismapping);
((UInputSettings*)InputSettings)->SaveKeyMappings();
InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
InputComponent->BindAxis("Move Forward", this, &AForgottenLegionCharacter::MoveForward);
InputComponent->BindAxis("Move Right", this, &AForgottenLegionCharacter::MoveRight);
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
}
And thats it, when you hit on play , all this settings will be saved and you can verify by going to the project settings->input section.
Thanks and Regards ,
Sameek Kundu