HAxis sos

This commit is contained in:
guus
2018-08-11 16:46:35 +02:00
commit 510654f8a1
480 changed files with 54126 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "ButtonHintBar.h"

View File

@@ -0,0 +1,13 @@
// Project Lab - NHTV Igad
#pragma once
#include "UserWidget.h"
#include "ButtonHintBar.generated.h"
UCLASS()
class UNREALPROJECT_API UButtonHintBar : public UUserWidget
{
GENERATED_BODY()
public:
};

View File

@@ -0,0 +1,246 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "GameList.h"
#include "GameListItem.h"
#include "DefaultGameInstance.h"
#include "SessionManager.h"
#include "ScreenOverlay.h"
#include "PlayerControllerBase.h"
static UClass* itemWidgetClass;
UGameList::UGameList(const FObjectInitializer& init)
: Super(init)
{
itemWidgetClass = ConstructorHelpers::FClassFinder<UGameListItem>(TEXT("/Game/Assets/GUI/Components/WEEGEE_GameListItem")).Class;
m_quickJoining = false;
}
void UGameList::NativeConstruct()
{
Super::NativeConstruct();
m_searchingSessions = false;
m_joining = false;
UWorld* world = this->GetWorld();
if (!world)
return;
m_gameInstance = Cast<UDefaultGameInstance>(world->GetGameInstance());
if (!m_gameInstance)
return;
onItemSelectionConfirmed.AddDynamic(this, &UGameList::OnListItemPressed);
}
void UGameList::NativeDestruct()
{
m_StopPingingGames();
Super::NativeDestruct();
}
void UGameList::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
}
void UGameList::OnStartSearching_Implementation()
{
}
void UGameList::OnEndSearching_Implementation(int32)
{
}
void UGameList::SetInitialSearchMode_Implementation(bool useLan)
{
}
void UGameList::OnQuickJoin()
{
OnSearchButton();
m_quickJoining = true;
}
void UGameList::JoinSession(const class FOnlineSessionSearchResult& result)
{
m_StopPingingGames();
if(!m_joining)
{
APlayerControllerBase* pc = Cast<APlayerControllerBase>(GetOwningPlayer());
check(!m_joiningOverlay);
m_joiningOverlay = pc->overlay->ShowOverlay("Joining game");
m_joining = true;
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
inst->sessionManager->onJoinSessionComplete.AddUObject(this, &UGameList::m_OnJoinSessionComplete);
inst->sessionManager->JoinSession(result);
}
}
void UGameList::SetSearchMode(bool useLan)
{
}
void UGameList::OnSearchButton()
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
if (!m_searchingSessions)
{
check(!m_searchingOverlay);
inst->sessionManager->onFindSessionsComplete.AddUObject(this, &UGameList::m_OnFindSessionsComplete);
inst->sessionManager->FindSessions();
m_searchingSessions = true;
container->ClearChildren();
m_StopPingingGames();
m_gameItems.SetNum(0);
RescanItems();
OnStartSearching();
APlayerControllerBase* pc = Cast<APlayerControllerBase>(GetOwningPlayer());
m_searchingOverlay = pc->overlay->ShowOverlay("Searching for games");
}
}
void UGameList::OnListItemPressed(UMenuItemBase* item)
{
GWPRINT(L"Pressed item " + item);
UGameListItem* listItem = Cast<UGameListItem>(item);
if(listItem)
{
JoinSession(*listItem->result);
}
}
void UGameList::m_OnFindSessionsComplete(bool success)
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
// Hide searching overlay
m_searchingOverlay->Close();
m_searchingOverlay = nullptr;
m_searchingSessions = false;
if(!success)
{
APlayerControllerBase* pc = Cast<APlayerControllerBase>(GetOwningPlayer());
TArray<FString> options;
options.Add("OK");
pc->overlay->ShowMessageBox("Error", "Error occured while fining games", options);
return;
}
if(m_quickJoining)
{
// Find session with most players in, that is not full
auto& results = inst->sessionManager->sessionSearch->SearchResults;
if(results.Num() > 0)
{
FOnlineSessionSearchResult* optimal = &results[0];
int32 largest = 0;
for(int32 i = 1; i < results.Num(); i++)
{
const int32 available = results[i].Session.NumOpenPublicConnections;
if(available == 0) continue;
const int32 max = results[i].Session.SessionSettings.NumPublicConnections;
const int32 current = max - available;
if(current > largest)
{
optimal = &results[i];
largest = current;
}
}
JoinSession(*optimal);
}
m_quickJoining = false;
}
else
{
// Get session results
auto& results = inst->sessionManager->sessionSearch->SearchResults;
for(int32 i = 0; i < results.Num(); i++)
{
// Create a selectable slot in the game list
UGameListItem* childItem = CreateWidget<UGameListItem>(GetWorld(), itemWidgetClass);
check(container);
container->AddChild(childItem);
childItem->SetItem(this, results[i]);
m_gameItems.Add(childItem);
}
OnEndSearching(results.Num());
RescanItems();
// Give ping data for games
m_StartPingingGames();
}
m_searchingSessions = false;
// Remove callback
inst->sessionManager->onFindSessionsComplete.RemoveAll(this);
}
void UGameList::m_OnJoinSessionComplete(int32 result)
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
APlayerControllerBase* pc = Cast<APlayerControllerBase>(GetOwningPlayer());
m_joining = false;
// Remove callback
inst->sessionManager->onJoinSessionComplete.RemoveAll(this);
if(result != EOnJoinSessionCompleteResult::Success)
{
FString error = "Unknown";
switch(result)
{
case EOnJoinSessionCompleteResult::AlreadyInSession:
error = "Already in session";
break;
case EOnJoinSessionCompleteResult::CouldNotRetrieveAddress:
error = "Could not retrieve address";
break;
case EOnJoinSessionCompleteResult::SessionDoesNotExist:
error = "Session does not exist";
break;
case EOnJoinSessionCompleteResult::SessionIsFull:
error = "Session is full";
break;
}
// Close joining overlay
m_joiningOverlay->Close();
m_joiningOverlay = nullptr;
// Show error message
TArray<FString> options;
options.Add("OK");
pc->overlay->ShowMessageBox("Error", FString("Failed to join session:\n") + error, options);
}
else
{
// Travel to destination player lobby
pc->ClientTravel(inst->sessionManager->joinConnectionString, ETravelType::TRAVEL_Absolute, false);
//UWorld* world = GetWorld();
//world->SeamlessTravel(inst->sessionManager->joinConnectionString, true);
//world->ServerTravel()
//AMenuGameMode* mgm = Cast<AMenuGameMode>(world->GetAuthGameMode());
}
}
void UGameList::m_PingGames()
{
for(auto& g : m_gameItems)
{
g->Ping();
}
}
void UGameList::m_StartPingingGames()
{
GetGameInstance()->GetTimerManager().SetTimer(m_pinger, this, &UGameList::m_PingGames, 1.0f, true);
}
void UGameList::m_StopPingingGames()
{
GetGameInstance()->GetTimerManager().ClearTimer(m_pinger);
for(UGameListItem* i : m_gameItems)
{
i->Ping();
}
}

View File

@@ -0,0 +1,75 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuScreen.h"
#include "OnlineSessionInterface.h"
#include "GameList.generated.h"
using namespace std;
struct SessionItemData
{
FString name;
int32 index;
FOnlineSessionSearchResult* result;
};
UCLASS()
class UNREALPROJECT_API UGameList : public UPersistentSideView
{
GENERATED_BODY()
public:
UGameList(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
UFUNCTION(BlueprintNativeEvent, Category = "UI")
void OnStartSearching();
UFUNCTION(BlueprintNativeEvent, Category = "UI")
void OnEndSearching(int32 gamesFound);
UFUNCTION(BlueprintNativeEvent, Category = "UI")
void SetInitialSearchMode(bool useLan);
UFUNCTION(BlueprintCallable, Category = "UI")
void OnQuickJoin();
void JoinSession(const class FOnlineSessionSearchResult& result);
UFUNCTION(BlueprintCallable, Category = "UI")
void SetSearchMode(bool useLan);
UFUNCTION(BlueprintCallable, Category = "UI")
void OnSearchButton();
UFUNCTION()
void OnListItemPressed(UMenuItemBase* item);
UFUNCTION(BlueprintCallable, Category = "UI")
int32 GetNumGames() const { return m_gameItems.Num(); }
UFUNCTION(BlueprintCallable, Category = "UI")
TArray<class UGameListItem*> GetGames() const { return m_gameItems; }
private:
UFUNCTION()
void m_OnFindSessionsComplete(bool success);
UFUNCTION()
void m_OnJoinSessionComplete(int32 result);
void m_PingGames();
void m_StartPingingGames();
void m_StopPingingGames();
FTimerHandle m_pinger;
TArray<class UGameListItem*> m_gameItems;
bool m_searchingSessions;
bool m_quickJoining;
bool m_joining;
UPROPERTY()
class UOverlayInfo* m_searchingOverlay;
UPROPERTY()
class UOverlayInfo* m_joiningOverlay;
UPROPERTY()
class UDefaultGameInstance* m_gameInstance;
};

View File

@@ -0,0 +1,53 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "GameListItem.h"
#include "GameList.h"
#include "DefaultGameInstance.h"
#include "SessionManager.h"
UGameListItem::UGameListItem(const FObjectInitializer& init)
: Super(init)
{
valid = false;
}
void UGameListItem::NativeConstruct()
{
Super::NativeConstruct();
OnGameInfoUpdated();
}
void UGameListItem::SetItem(UGameList* parent, const FOnlineSessionSearchResult& _result)
{
// Set the sesion result to be displayed in this item
check(parent);
m_parent = parent;
result = &_result;
valid = result != nullptr;
if(valid)
{
UWorld* world = GetWorld();
check(world);
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(world->GetGameInstance());
FString nickname = inst->sessionManager->GetPlayerName(*result->Session.OwningUserId);
// Game Name = Online platform nickname of host
gameHost = nickname;
gamePing = result->PingInMs;
if(!result->Session.SessionSettings.Get(SETTING_MAPNAME, gameMapName))
{
GWWARNING(L"Game does not have a map name set");
}
gamePlayersMax = result->Session.SessionSettings.NumPublicConnections;
gamePlayers = gamePlayersMax - result->Session.NumOpenPublicConnections;
}
OnGameInfoUpdated();
}
void UGameListItem::Ping()
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetWorld()->GetGameInstance());
inst->sessionManager->PingResult(*result);
OnGameInfoUpdated();
}

View File

@@ -0,0 +1,41 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuButton.h"
#include "GameListItem.generated.h"
UCLASS()
class UNREALPROJECT_API UGameListItem : public UMenuButton
{
GENERATED_BODY()
public:
UGameListItem(const FObjectInitializer& init);
virtual void NativeConstruct() override;
void SetItem(class UGameList* parent, const FOnlineSessionSearchResult& result);
UFUNCTION(BlueprintImplementableEvent, Category = "UI")
void OnGameInfoUpdated();
void Ping();
UPROPERTY(BlueprintReadOnly, Category = "Game")
bool valid;
UPROPERTY(BlueprintReadOnly, Category = "Game")
FString gameHost;
UPROPERTY(BlueprintReadOnly, Category = "Game")
FString gameMapName;
UPROPERTY(BlueprintReadOnly, Category = "Game")
int32 gamePlayers;
UPROPERTY(BlueprintReadOnly, Category = "Game")
int32 gamePlayersMax;
UPROPERTY(BlueprintReadOnly, Category = "Game")
int32 gamePing;
const class FOnlineSessionSearchResult* result;
private:
UPROPERTY()
class UGameList* m_parent;
};

View File

@@ -0,0 +1,103 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SelectButton.h"
#include "DefaultGameInstance.h"
#include "Prefs.h"
#include "GraphicsMenu.h"
void UGraphicsMenu::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
m_Update();
}
void UGraphicsMenu::InitButtons(USelectButton* templateButton, TArray<USelectButton*> settingButtons)
{
m_templateButton = templateButton;
m_settingButtons = settingButtons;
if (!IsValid(m_templateButton) || m_settingButtons.Num() != 6)
{
JERROR("InitButton arguments invalid");
m_templateButton = nullptr;
return;
}
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(GetGameInstance());
if (!IsValid(instance))
return;
UPrefs* const prefs = instance->GetPrefs();
int32 fetchPreset = instance->GetScalabilityQuality();
templateButton->selected = prefs->usingCustomGraphicsSettings ? 0 : fetchPreset + 1;
m_previousSelection = -5;
m_Update();
}
void UGraphicsMenu::Apply()
{
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(GetGameInstance());
if (!IsValid(m_templateButton) || !IsValid(instance))
return;
const int32 current = m_templateButton->selected;
if (current > 0)
{
// Store the template setting
instance->SetScalabilityQuality(current - 1);
}
else
{
// Store the individual sub settings
instance->SetScalabilityQualityValues(
m_settingButtons[0]->selected,
m_settingButtons[1]->selected,
m_settingButtons[2]->selected,
m_settingButtons[3]->selected,
m_settingButtons[4]->selected,
m_settingButtons[5]->selected);
}
}
void UGraphicsMenu::m_Update()
{
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(GetGameInstance());
if (!IsValid(m_templateButton) || !IsValid(instance))
return;
const int32 current = m_templateButton->selected;
if (m_previousSelection != current) // Only update when the value has changed
{
if (current == 0)
{
// Enable the sub setting buttons
for (int32 i = 0; i < m_settingButtons.Num(); i++)
m_settingButtons[i]->SetIsEnabled(true);
// Fetch the stored sub settings from prefs
TArray<int32> customSettings = instance->GetPrefs()->customGraphicsSettings;
if (customSettings.Num() <= m_settingButtons.Num())
{
for (int32 i = 0; i < customSettings.Num(); i++)
m_settingButtons[i]->selected = customSettings[i];
}
}
else
{
// Disable the subsetting buttons and set them to the template
for (int32 i = 0; i < m_settingButtons.Num(); i++)
{
m_settingButtons[i]->SetIsEnabled(false);
m_settingButtons[i]->selected = current - 1;
}
}
m_previousSelection = current;
}
}

View File

@@ -0,0 +1,32 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuScreen.h"
#include "GraphicsMenu.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UGraphicsMenu : public USideView
{
GENERATED_BODY()
public:
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
UFUNCTION(BlueprintCallable, Category = "Graphics")
void InitButtons(class USelectButton* templateButton, TArray<class USelectButton*> settingButtons);
UFUNCTION(BlueprintCallable, Category = "Graphics")
void Apply();
private:
void m_Update();
USelectButton* m_templateButton;
TArray<USelectButton*> m_settingButtons;
int32 m_previousSelection;
};

View File

@@ -0,0 +1,161 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MapData.h"
#include "MapSelectionScreen.h"
#include "AssetRegistryModule.h"
#include "DefaultGameInstance.h"
#include "MapSlot.h"
#include "SessionManager.h"
#include "MenuController.h"
#include "ScreenOverlay.h"
#include "MenuGameMode.h"
static UClass* mapSlotWidgetClass;
UMapSelectionScreen::UMapSelectionScreen(const FObjectInitializer& init)
: Super(init)
{
mapSlotWidgetClass = ConstructorHelpers::FClassFinder<UMapSlot>(TEXT("/Game/Assets/GUI/Components/WEEGEE_MapListItem")).Class;
}
void UMapSelectionScreen::NativeConstruct()
{
Super::NativeConstruct();
if(container)
{
// Get all the available maps and create selection buttons for them
UWorld* world = GetWorld();
check(world);
auto& maps = Cast<UDefaultGameInstance>(world->GetGameInstance())->maps;
for(int32 i = 0; i < maps.Num(); i++)
{
UMapSlot* mapSlot = CreateWidget<UMapSlot>(GetWorld(), mapSlotWidgetClass);
check(mapSlot);
mapSlot->mapData = maps[i];
mapSlot->mapScreen = this;
mapSlot->OnMapSet();
m_mapSlots.Add(mapSlot);
container->AddChild(mapSlot);
}
onItemSelected.RemoveAll(this);
onItemSelected.AddDynamic(this, &UMapSelectionScreen::OnSelectionChanged);
RescanItems();
SelectMap(0);
}
}
void UMapSelectionScreen::OnSelectionChanged(UMenuItemBase* item)
{
UMapSlot* mapSlot = Cast<UMapSlot>(item);
if(mapSlot)
{
//SelectMap(mapSlot->index);
}
}
void UMapSelectionScreen::SelectMap(int32 index)
{
UWorld* world = GetWorld();
check(world);
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(world->GetGameInstance());
TArray<UMapData*>& maps = inst->maps;
if(index < 0 || index > maps.Num())
{
GWERROR(L"Selected map out of range");
return;
}
mapData = maps[index];
check(mapData);
// Set the asset path to the selected map in the session manager
AMenuGameMode* gameMode = Cast<AMenuGameMode>(world->GetAuthGameMode());
if(gameMode)
{
gameMode->SetMap(mapData->pathToAsset);
GWPRINT(L"Selected map: " + maps[index]->friendlyName);
}
else
{
GWARNING(L"Can't select map, no game mode!");
}
}
void UMapSelectionScreen::CreateGame()
{
SelectMap(GetSelectedItem());
if(!mapData)
{
GERROR("No map set");
return;
}
UWorld* world = GetWorld();
check(world);
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(world->GetGameInstance());
AMenuGameMode* gameMode = Cast<AMenuGameMode>(world->GetAuthGameMode());
if(!gameMode)
{
GERROR("Can't start game, no menu game mode set");
return;
}
APlayerControllerBase* pc = Cast<APlayerControllerBase>(GetOwningPlayer());
if(m_sessionCreateOverlay)
return; // Already creating session
m_sessionCreateOverlay = pc->overlay->ShowOverlay("Creating session");
// Set the map setting in the game mode
gameMode->AssignMapData(mapData);
// Make sure there is no connection already
inst->sessionManager->CloseNetConnections();
// Set host settings
FOnlineSessionSettings& sessionSettings = inst->sessionManager->sessionSettings;
sessionSettings.NumPublicConnections = mapData->maxTeamCount * 2; // Team count setting translated to max connected players (x2)
sessionSettings.Set(SETTING_MAPNAME, mapData->pathToAsset, EOnlineDataAdvertisementType::ViaOnlineServiceAndPing);
// Create a new session
inst->sessionManager->onCreateSessionComplete.AddUObject(this, &UMapSelectionScreen::m_OnCreateSessionCompleted);
inst->sessionManager->CreateSession();
}
void UMapSelectionScreen::m_OnCreateSessionCompleted(bool success)
{
if(m_sessionCreateOverlay)
{
m_sessionCreateOverlay->Close();
m_sessionCreateOverlay = nullptr;
}
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetWorld()->GetGameInstance());
inst->sessionManager->onCreateSessionComplete.RemoveAll(this);
AMenuController* mc = Cast<AMenuController>(GetOwningPlayer());
if(success)
{
// Goto the lobby
mc->OnEnterLobby();
// Start the session, @note not sure if this is needed
//inst->sessionManager->StartSession();
}
else
{
TArray<FString> options;
options.Add("OK");
mc->overlay->ShowMessageBox("Error", "Failed to create game session", options);
}
}
UMapData::UMapData()
{
maxTeamCount = 3;
friendlyName = "Map Name";
description = "<Designers: Add description>";
}

View File

@@ -0,0 +1,40 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuScreen.h"
#include "MapSelectionScreen.generated.h"
UCLASS()
class UNREALPROJECT_API UMapSelectionScreen : public USideView
{
GENERATED_BODY()
public:
UMapSelectionScreen(const FObjectInitializer& init);
virtual void NativeConstruct() override;
UFUNCTION()
void OnSelectionChanged(UMenuItemBase* item);
UFUNCTION(BlueprintCallable, Category = "Level")
void SelectMap(int32 mapIndex);
UFUNCTION(BlueprintCallable, Category = "Level")
void CreateGame();
// Currently Selected map data
UPROPERTY(BlueprintReadOnly, Category = "Level")
class UMapData* mapData;
UPROPERTY()
class UMapSlot* selectedMapSlot;
private:
UPROPERTY()
class UOverlayInfo* m_sessionCreateOverlay;
UFUNCTION()
void m_OnCreateSessionCompleted(bool success);
UPROPERTY()
TArray<class UMapSlot*> m_mapSlots;
};

View File

@@ -0,0 +1,14 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MapSlot.h"
#include "MapData.h"
void UMapSlot::NativeConstruct()
{
Super::NativeConstruct();
}
void UMapSlot::OnMapSet_Implementation()
{
}

View File

@@ -0,0 +1,26 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuButton.h"
#include "MapSlot.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UMapSlot : public UMenuButton
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
UFUNCTION(BlueprintNativeEvent, Category = "Slot")
void OnMapSet();
UPROPERTY(BlueprintReadOnly, Category = "Slot")
class UMapSelectionScreen* mapScreen;
UPROPERTY(BlueprintReadOnly, Category = "Slot")
class UMapData* mapData;
};

View File

@@ -0,0 +1,32 @@
#pragma once
#include "MenuAction.Generated.h"
UENUM(BlueprintType)
enum class EMenuActionBinding : uint8
{
Confirm,
Back,
Opt1,
Opt2,
Start,
Up,
Down,
Left,
Right,
Repeat_Left,
Repeat_Right,
Trigger_Left,
Trigger_Right,
Shoulder_Left,
Shoulder_Right,
Options,
// These are actually just for button hint icons
// don't use these for actual actions as they wont get used
LeftStick,
RightStick,
DPad,
ShoulderButtons,
Triggers
};

View File

@@ -0,0 +1,147 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MenuButton.h"
#include "SubMenu.h"
FLinearColor uiIdleColor = FLinearColor(1.0f, 1.0f, 1.0f, 0.3f);
FLinearColor uiSelectedColor = FLinearColor(0.8f, 1.0f, 0.8f, 0.4f);
FLinearColor uiSelectedFocusedColor = FLinearColor(0.2f, 1.0f, 0.2f, 0.8f);
TSubclassOf<class UMenuButton> defaultButtonClass;
UMenuButton::UMenuButton(const FObjectInitializer& init)
: Super(init)
{
defaultButtonClass = ConstructorHelpers::FClassFinder<UMenuButton>(L"/Game/Assets/GUI/Components/WEEGEE_Button").Class;
}
void UMenuButton::NativeConstruct()
{
m_button = WidgetTree->FindWidget<UButton>("Button");
m_label = WidgetTree->FindWidget<UTextBlock>("Label");
m_textOverlay = WidgetTree->FindWidget<UOverlay>("TextOverlay");
if(!m_button)
{
GWERROR(L"Widget " + GetName() + L" does not contain a Button componenet");
}
else
{
m_button->OnClicked.AddDynamic(this, &UMenuButton::m_OnPressed);
}
NativeOnSelectionChanged(false, true);
Super::NativeConstruct();
if(m_textOverlay && m_label)
{
m_blurOverlay = NewObject<UOverlay>(GetWorld());
UOverlaySlot* slot = m_textOverlay->AddChildToOverlay(m_blurOverlay);
slot->SetHorizontalAlignment(EHorizontalAlignment::HAlign_Fill);
slot->SetVerticalAlignment(EVerticalAlignment::VAlign_Fill);
UOverlaySlot* labelSlot = Cast<UOverlaySlot>(m_label->Slot);
EHorizontalAlignment textAlignH = labelSlot ? labelSlot->HorizontalAlignment.GetValue() : HAlign_Fill;
EVerticalAlignment textAlignV = labelSlot ? labelSlot->VerticalAlignment.GetValue() : VAlign_Fill;
m_label->RemoveFromParent();
static int32 quality = 16;
static float distance = 2.0f;
for(int32 i = 0; i < quality; i++)
{
float r = (float)i / (float)(quality - 1);
FVector2D offset = FVector2D(cos(r*PI), sin(r*PI)) * distance;
// Create Glow effect
UTextBlock* blurText = NewObject<UTextBlock>(GetWorld());
UOverlaySlot* textSlot = m_blurOverlay->AddChildToOverlay(blurText);
textSlot->SetHorizontalAlignment(textAlignH);
textSlot->SetVerticalAlignment(textAlignV);
FSlateFontInfo font = m_label->Font;
blurText->SetFont(font);
blurText->SetColorAndOpacity(FSlateColor(FLinearColor(0.6f, 0.6f, 1.0f, 0.5f)));
blurText->SetRenderTranslation(offset);
blurText->SetText(m_label->GetText());
}
m_blurOverlay->SetVisibility(ESlateVisibility::Hidden);
labelSlot = m_textOverlay->AddChildToOverlay(m_label);
labelSlot->SetHorizontalAlignment(textAlignH);
labelSlot->SetVerticalAlignment(textAlignV);
}
}
void UMenuButton::FocusTick(float DeltaTime)
{
Super::FocusTick(DeltaTime);
if(m_button && m_button->IsHovered() && GetSubMenu()->GetSelectedItem() != index)
{
GetSubMenu()->SelectNewItemByIndex(index);
}
}
void UMenuButton::m_OnPressed()
{
if(HasFocus())
{
NativeOnPressed(false);
}
}
void UMenuButton::NativeOnMenuAction(EMenuActionBinding binding)
{
Super::NativeOnMenuAction(binding);
if(binding == EMenuActionBinding::Confirm)
NativeOnPressed(true);
}
void UMenuButton::NativeOnSelectionChanged(bool selected, bool controller)
{
Super::NativeOnSelectionChanged(selected, controller);
if(!m_button)
return;
UScrollBox* sb = Cast<UScrollBox>(Slot->Parent);
if(sb && selected)
{
sb->ScrollWidgetIntoView(this, true);
}
if(m_blurOverlay)
{
if(selected)
{
m_blurOverlay->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
else
{
m_blurOverlay->SetVisibility(ESlateVisibility::Hidden);
}
}
}
void UMenuButton::NativeOnPressed(bool controllerInput)
{
// Simulate OnSelectionConfirmed event if this button is pressed by a mouse
GetSubMenu()->NativeOnSelectionConfirmed(this);
onPressed.Broadcast();
}
void UMenuButton::SetButtonText(FString textStr)
{
FText text = FText::FromString(textStr);
if(m_label)
m_label->SetText(text);
if(m_blurOverlay)
{
for(int32 i = 0; i < m_blurOverlay->GetChildrenCount(); i++)
{
UTextBlock* tb = Cast<UTextBlock>(m_blurOverlay->GetChildAt(i));
if(tb)
{
tb->SetText(text);
}
}
}
}

View File

@@ -0,0 +1,48 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/MenuItemBase.h"
#include "MenuButton.generated.h"
extern FLinearColor uiIdleColor;
extern FLinearColor uiSelectedColor;
/**
*
*/
UCLASS()
class UNREALPROJECT_API UMenuButton : public UMenuItemBase
{
GENERATED_BODY()
public:
UMenuButton(const FObjectInitializer& init);
void NativeConstruct() override;
void FocusTick(float DeltaTime) override;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FButtonPressed);
UPROPERTY(BlueprintAssignable, Category = "MenuButton")
FButtonPressed onPressed;
virtual void NativeOnMenuAction(EMenuActionBinding binding) override;
virtual void NativeOnSelectionChanged(bool selected, bool controller) override;
// Called when this button is pressed, and if it was a controller or a mouse
virtual void NativeOnPressed(bool controllerInput);
// Updates button text + glow
UFUNCTION(BlueprintCallable, Category="MenuButton")
void SetButtonText(FString text);
void SimulatePressed() { m_OnPressed(); }
protected:
UFUNCTION()
virtual void m_OnPressed();
UOverlay* m_textOverlay;
UOverlay* m_blurOverlay;
UPROPERTY()
UTextBlock* m_label;
UButton* m_button;
};

View File

@@ -0,0 +1,9 @@
#pragma once
UENUM(BlueprintType)
enum class MenuButtonID : uint8
{
OptionsGeneral,
OptionsGraphics,
Options,
};

View File

@@ -0,0 +1,92 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MenuItemBase.h"
#include "SubMenu.h"
#include "PlayerControllerBase.h"
#include "ScreenOverlay.h"
void UMenuItemBase::NativeConstruct()
{
m_selected = false;
blockInput = false;
m_subMenu = nullptr;
priority = 0;
Super::NativeConstruct();
}
void UMenuItemBase::NativeDestruct()
{
Super::NativeDestruct();
}
void UMenuItemBase::FocusTick(float DeltaTime)
{
}
UMsgBoxInfo* UMenuItemBase::ShowMessageBox(FOverlayItemClosed cb, FString caption, FString message, TArray<FString> options, FString defaultOption)
{
UScreenOverlay* overlay = Cast<APlayerControllerBase>(GetOwningPlayer())->overlay;
if(!overlay)
return nullptr;
return overlay->ShowMessageBoxCallback(cb, caption, message, options, defaultOption);
}
void UMenuItemBase::NativeOnSelectionChanged(bool selected, bool controller)
{
if(selected != m_selected)
{
m_selected = selected;
onSelectionChanged.Broadcast(selected);
}
}
bool UMenuItemBase::IsSelected() const
{
return m_selected;
}
class USubMenu* UMenuItemBase::GetSubMenu() const
{
return m_subMenu;
}
bool UMenuItemBase::HasFocus() const
{
return GetSubMenu() && GetSubMenu()->HasFocus();
}
void UMenuItemBase::AddActionBinding(EMenuActionBinding binding, FMenuAction action)
{
m_actionBindings.Add(binding, action);
}
void UMenuItemBase::ClearActionBinding(EMenuActionBinding binding)
{
m_actionBindings.Remove(binding);
}
void UMenuItemBase::OnMenuAction_Implementation(EMenuActionBinding binding)
{
}
void UMenuItemBase::NativeOnMenuAction(EMenuActionBinding binding)
{
OnMenuAction(binding);
TArray<FMenuAction> actions;
m_actionBindings.MultiFind(binding, actions);
for(int32 i = 0; i < actions.Num(); i++)
{
actions[i].Execute();
}
}
FText UMenuItemBase::GetDescription_Implementation()
{
return FText();
}
FText UMenuItemBase::GetTitle_Implementation()
{
return FText();
}

View File

@@ -0,0 +1,83 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuAction.h"
#include "Blueprint/UserWidget.h"
#include "MenuItemBase.generated.h"
DECLARE_DYNAMIC_DELEGATE_OneParam(FOverlayItemClosed, int32, choice);
extern TSubclassOf<class UMenuButton> defaultButtonClass;
extern TSubclassOf<class UMenuSlider> defaultSliderClass;
/**
* Base class for a menu item with controller navigation/input support
*/
UCLASS()
class UNREALPROJECT_API UMenuItemBase : public UUserWidget
{
GENERATED_BODY()
public:
void NativeConstruct() override;
void NativeDestruct() override;
// Called when the item's menu has focus every frame
virtual void FocusTick(float DeltaTime);
UFUNCTION(BlueprintCallable, Category="MenuItem")
virtual void NativeOnSelectionChanged(bool selected, bool controller);
// Shows a message box and fires a delegate when the user chooses an option
UFUNCTION(BlueprintCallable, Category = "SubMenu")
class UMsgBoxInfo* ShowMessageBox(FOverlayItemClosed cb, FString caption, FString message, TArray<FString> options, FString defaultOption);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSelectionChanged, bool, selected);
UPROPERTY(BlueprintAssignable, Category = "MenuItem")
FSelectionChanged onSelectionChanged;
// UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "MenuItem")
int32 priority;
UPROPERTY(BlueprintReadOnly)
int32 index;
UFUNCTION(BlueprintCallable, Category = "MenuItem")
bool IsSelected() const;
UFUNCTION(BlueprintCallable, Category = "MenuItem")
class USubMenu* GetSubMenu() const;
UFUNCTION(BlueprintCallable, Category = "MenuItem")
bool HasFocus() const;
DECLARE_DYNAMIC_DELEGATE(FMenuAction);
UFUNCTION(BlueprintCallable, Category = "MenuItem")
void AddActionBinding(EMenuActionBinding binding, FMenuAction action);
UFUNCTION(BlueprintCallable, Category = "MenuItem")
void ClearActionBinding(EMenuActionBinding binding);
UFUNCTION(BlueprintNativeEvent, Category = "MenuItem")
void OnMenuAction(EMenuActionBinding binding);
virtual void NativeOnMenuAction(EMenuActionBinding binding);
UFUNCTION(BlueprintNativeEvent, Category = "MenuItem")
FText GetDescription();
UFUNCTION(BlueprintNativeEvent, Category = "MenuItem")
FText GetTitle();
UPROPERTY(BlueprintReadOnly, Category="MenuItem")
bool blockInput;
private:
bool m_selected;
friend class APlayerControllerBase;
friend class USubMenu;
TMultiMap<EMenuActionBinding, FMenuAction> m_actionBindings;
protected:
UPROPERTY()
class USubMenu* m_subMenu;
};

View File

@@ -0,0 +1,337 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MenuScreen.h"
#include "ButtonHintBar.h"
#include "SubMenu.h"
#include "MenuButton.h"
void UMenuScreen::NativeConstruct()
{
Super::NativeConstruct();
m_sideViewActive = false;
m_buttonHintBar = Cast<UButtonHintBar>(WidgetTree->FindWidget("ButtonHintBar"));
m_menuContainer = Cast<UOverlay>(WidgetTree->FindWidget("MenuContainer"));
m_sideViewContainer = Cast<UOverlay>(WidgetTree->FindWidget("SideViewContainer"));
m_menuTitle = Cast<UTextBlock>(WidgetTree->FindWidget("MenuTitle"));
m_sideViewTitle = Cast<UTextBlock>(WidgetTree->FindWidget("SideViewTitle"));
}
UMenuPanel* UMenuScreen::OpenScreenMenu(TSubclassOf<UMenuPanel> cls)
{
UMenuPanel* menu = CreateWidget<UMenuPanel>(GetWorld(), cls);
if(!menu)
{
GERROR("Failed to create menu for screen " + GetName());
return nullptr;
}
m_menuContainer->AddChildToOverlay(menu);
OpenSubMenu(menu);
menu->FadeIn();
return menu;
}
USideView* UMenuScreen::OpenSideView(TSubclassOf<class USideView> cls)
{
USideView* view = CreateWidget<USideView>(GetWorld(), cls);
if(!view)
{
GERROR("Failed to create side view for screen " + GetName());
return nullptr;
}
UPersistentSideView* pview = Cast<UPersistentSideView>(view);
UOverlaySlot* slot = m_sideViewContainer->AddChildToOverlay(view);
slot->SetHorizontalAlignment(HAlign_Fill);
slot->SetVerticalAlignment(VAlign_Fill);
if(pview)
m_OpenSubMenu(view, false);
else
OpenSubMenu(view);
view->FadeIn();
// Switch side view active event
if(!m_sideViewActive)
{
SwitchSideViewVisible(true);
m_sideViewActive = true;
}
return view;
}
void UMenuScreen::CloseAllSideViews()
{
TArray<USideView*> viewsToRemove;
for(int32 i = 0; i < m_sideViewContainer->GetChildrenCount(); i++)
{
USideView* sv = Cast<USideView>(m_sideViewContainer->GetChildAt(i));
if(sv && !sv->IsA<UPersistentSideView>())
viewsToRemove.Add(sv);
}
for(USideView* sv : viewsToRemove)
{
CloseSubMenu(sv);
}
}
void UMenuScreen::OpenPersistentSideView(UPersistentSideView* sideView)
{
UMenuScreen::OpenSubMenu(sideView);
}
void UMenuScreen::ClosePersistentSideView(UPersistentSideView* sideView)
{
m_CloseSubMenu(sideView, true);
}
void UMenuScreen::OpenSubMenu(class USubMenu* submenu)
{
m_OpenSubMenu(submenu);
}
void UMenuScreen::CloseSubMenu(class USubMenu* submenu)
{
m_CloseSubMenu(submenu);
}
void UMenuScreen::m_OpenSubMenu(class USubMenu* submenu, bool transferFocus)
{
CloseAllSideViews();
if(transferFocus)
Super::OpenSubMenu(submenu);
UMenuScreenSubMenu* mssm = Cast<UMenuScreenSubMenu>(submenu);
if(!m_menuTitle || !mssm)
return;
// Update titles and sub-titles
UMenuPanel* panel = Cast<UMenuPanel>(submenu);
if(panel)
m_menuTitle->SetText(FText::FromString(mssm->menuName));
USideView* view = Cast<USideView>(submenu);
if(view)
{
m_sideViewTitle->SetText(FText::FromString(mssm->menuName));
m_currentSideViews.Add(view);
}
}
void UMenuScreen::m_CloseSubMenu(class USubMenu* submenu, bool persist)
{
UMenuScreenSubMenu* mssm = Cast<UMenuScreenSubMenu>(submenu);
if(mssm)
mssm->m_isBeingClosed = true;
UMenuPanel* panel = Cast<UMenuPanel>(submenu);
if(panel)
{
CloseAllSideViews();
panel->onAnimationEnded.RemoveAll(this);
panel->onAnimationEnded.AddUObject(this, &UMenuScreen::m_OnFadeOutComplete);
panel->FadeOut();
}
USideView* view = Cast<USideView>(submenu);
if(view && !persist)
{
view->onAnimationEnded.RemoveAll(this);
view->onAnimationEnded.AddUObject(this, &UMenuScreen::m_OnFadeOutComplete);
view->FadeOut();
// Switch side view active event
if(m_currentSideViews.Num() > 0 && m_sideViewActive)
{
SwitchSideViewVisible(false);
m_sideViewActive = false;
}
m_currentSideViews.Remove(view);
}
Super::CloseSubMenu(submenu);
// Update titles and sub-titles
if(panel)
{
if(m_subMenus.Num() > 0)
{
UMenuPanel* newMenu = Cast<UMenuPanel>(m_subMenus.Last());
if(newMenu)
m_menuTitle->SetText(FText::FromString(newMenu->menuName));
else
goto _set_no_panel_text;
}
else
{
_set_no_panel_text:
m_menuTitle->SetText(FText());
}
}
else if(view && !persist)
{
if(m_subMenus.Num() > 0)
{
USideView* newMenu = Cast<USideView>(m_subMenus.Last());
if(newMenu)
m_sideViewTitle->SetText(FText::FromString(newMenu->menuName));
else
goto _set_no_view_text;
}
else
{
_set_no_view_text:
m_sideViewTitle->SetText(FText());
}
}
if(mssm)
mssm->m_isBeingClosed = false;
}
void UMenuScreen::m_OnFadeOutComplete(UMenuScreenSubMenu* panel, int32 type)
{
if(type == 2) // Fade Out
{
panel->RemoveFromParent();
}
}
static float animationDuration = 0.35f;
void UMenuScreenSubMenu::NativeConstruct()
{
m_animationType = 0;
m_animationTime = 0.0f;
m_isBeingClosed = false;
Super::NativeConstruct();
}
UMenuScreenSubMenu::UMenuScreenSubMenu(const FObjectInitializer& init)
: Super(init)
{
menuName = "<Unnamed Menu!>";
}
void UMenuScreenSubMenu::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
float r = FMath::Clamp(m_animationTime / animationDuration, 0.0f, 1.0f);
if(m_animationType == 1) // Fade In
{
SetRenderTranslation(FVector2D(0.0f - ((1.0f - r)*(1.0f - r)) * 100.0f, 0.0f));
FLinearColor color = FLinearColor(1.0f, 1.0f, 1.0f, r);
SetColorAndOpacity(color);
}
else if(m_animationType == 2) // Fade Out
{
SetRenderTranslation(FVector2D(0.0f + (r*r) * 100.0f, 0.0f));
FLinearColor color = FLinearColor(1.0f, 1.0f, 1.0f, (1.0f-r));
SetColorAndOpacity(color);
}
if(m_animationType != 0)
{
if(r >= 1.0f)
{
onAnimationEnded.Broadcast(this, m_animationType);
m_animationType = 0;
}
m_animationTime += InDeltaTime;
}
}
void UMenuScreenSubMenu::OnSuspend(USubMenu* newSubMenu, bool loseFocus)
{
// Don't fade out / lose focus when side view is opened
USideView* sv = Cast<USideView>(newSubMenu);
if(sv)
loseFocus = false;
else
FadeOut();
Super::OnSuspend(newSubMenu, loseFocus);
}
void UMenuScreenSubMenu::OnRestore(USubMenu* removedMenu)
{
// To prevent playing the fade in animation again when we receive focus from closing side views when we are actually closing this menu
if(!m_isBeingClosed && !Cast<USideView>(removedMenu))
FadeIn();
Super::OnRestore(removedMenu);
}
void UMenuScreenSubMenu::FadeIn()
{
m_animationTime = 0.0f;
m_animationType = 1;
}
void UMenuScreenSubMenu::FadeOut()
{
m_animationTime = 0.0f;
m_animationType = 2;
}
UMenuPanel* UMenuScreenSubMenu::OpenScreenMenu(TSubclassOf<class UMenuPanel> cls)
{
UMenuScreen* screen = Cast<UMenuScreen>(GetScreen());
if(screen)
{
return screen->OpenScreenMenu(cls);
}
return nullptr;
}
USideView* UMenuScreenSubMenu::OpenSideView(TSubclassOf<class USideView> cls)
{
UMenuScreen* screen = Cast<UMenuScreen>(GetScreen());
if(screen)
{
screen->CloseAllSideViews();
return screen->OpenSideView(cls);
}
return nullptr;
}
class UMenuButton* UMenuScreenSubMenu::AddButton(FString label)
{
if(!container)
return nullptr;
UMenuButton* button = CreateWidget<UMenuButton>(GetWorld(), defaultButtonClass);
if(!button)
return nullptr;
container->AddChild(button);
RescanItems();
button->SetButtonText(label);
return button;
}
void UMenuScreenSubMenu::RescanItems()
{
m_backButton = nullptr;
Super::RescanItems();
for(UMenuItemBase* item : m_items)
{
UMenuButton* button = Cast<UMenuButton>(item);
if(button)
{
if(button->GetName() == "Back")
{
m_backButton = button;
break;
}
}
}
}
void UMenuScreenSubMenu::NativeOnMenuAction(EMenuActionBinding binding)
{
Super::NativeOnMenuAction(binding);
if(binding == EMenuActionBinding::Back)
{
if(m_backButton)
{
m_backButton->SimulatePressed();
}
}
}

View File

@@ -0,0 +1,135 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/MenuScreenBase.h"
#include "SubMenu.h"
#include "MenuScreen.generated.h"
UCLASS()
class UMenuScreenSubMenu : public USubMenu
{
GENERATED_BODY()
public:
UMenuScreenSubMenu(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
virtual void OnSuspend(USubMenu* newSubMenu, bool loseFocus);
virtual void OnRestore(USubMenu* removedMenu);
void FadeIn();
void FadeOut();
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
UMenuPanel* OpenScreenMenu(TSubclassOf<class UMenuPanel> cls);
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
USideView* OpenSideView(TSubclassOf<class USideView> cls);
class UMenuButton* AddButton(FString label);
virtual void RescanItems() override;
virtual void NativeOnMenuAction(EMenuActionBinding binding) override;
// Assigns a button to be the back button, this button will receive a pressed event when the player presses the circle button
void SetBackButton(UMenuButton* button) { m_backButton = button; }
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnAnimationEnded, UMenuScreenSubMenu*, int32)
FOnAnimationEnded onAnimationEnded;
UPROPERTY(EditDefaultsOnly, Category = "SubMenu")
FString menuName;
private:
UPROPERTY()
UMenuButton* m_backButton;
int32 m_animationType;
float m_animationTime;
bool m_isBeingClosed;
friend class UMenuScreen;
};
UCLASS()
class USideView : public UMenuScreenSubMenu
{
GENERATED_BODY()
public:
virtual ~USideView() = default;
};
UCLASS()
class UPersistentSideView : public USideView
{
GENERATED_BODY()
public:
virtual ~UPersistentSideView() = default;
};
UCLASS()
class UMenuPanel : public UMenuScreenSubMenu
{
GENERATED_BODY()
public:
};
/**
*
*/
UCLASS()
class UNREALPROJECT_API UMenuScreen : public UMenuScreenBase
{
GENERATED_BODY()
public:
void NativeConstruct() override;
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
UMenuPanel* OpenScreenMenu(TSubclassOf<class UMenuPanel> cls);
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
USideView* OpenSideView(TSubclassOf<class USideView> cls);
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
void CloseAllSideViews();
UFUNCTION(BlueprintImplementableEvent, Category="Menu Screen")
void SwitchSideViewVisible(bool visible);
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
void OpenPersistentSideView(UPersistentSideView* sideView);
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
void ClosePersistentSideView(UPersistentSideView* sideView);
// When this menu needs to be hidden
UFUNCTION(BlueprintImplementableEvent)
void OnShow();
// When this menu needs to be shown
UFUNCTION(BlueprintImplementableEvent)
void OnHide();
virtual void OpenSubMenu(class USubMenu* submenu) override;
virtual void CloseSubMenu(class USubMenu* submenu) override;
private:
void m_OpenSubMenu(class USubMenu* submenu, bool transferFocus = true);
void m_CloseSubMenu(class USubMenu* submenu, bool persist = false);
void m_OnFadeOutComplete(UMenuScreenSubMenu* panel, int32 type);
bool m_sideViewActive;
UButtonHintBar* m_buttonHintBar;
UOverlay* m_menuContainer;
UOverlay* m_sideViewContainer;
UTextBlock* m_menuTitle;
UTextBlock* m_sideViewTitle;
UPROPERTY()
TArray<USideView*> m_currentSideViews;
friend class UMenuScreenSubMenu;
};
UCLASS()
class UMainMenuScreen : public UMenuScreen
{
GENERATED_BODY()
public:
};

View File

@@ -0,0 +1,122 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MenuScreenBase.h"
#include "PlayerControllerBase.h"
#include "SubMenu.h"
#include "WidgetBlueprintLibrary.h"
UMenuScreenBase::UMenuScreenBase(const FObjectInitializer& init)
: Super(init)
{
registerMenuActions = false;
}
void UMenuScreenBase::NativeConstruct()
{
Super::NativeConstruct();
APlayerControllerBase* pcb = Cast<APlayerControllerBase>(GetWorld()->GetFirstPlayerController());
if(registerMenuActions && pcb)
{
pcb->AddMenuInputItem(this);
}
}
void UMenuScreenBase::NativeDestruct()
{
Super::NativeDestruct();
APlayerControllerBase* pcb = Cast<APlayerControllerBase>(GetWorld()->GetFirstPlayerController());
if(registerMenuActions && pcb)
{
pcb->RemoveMenuInputItem(this);
}
}
void UMenuScreenBase::OpenSubMenu(class USubMenu* submenu)
{
if(m_subMenus.Contains(submenu))
{
GWARNING("Submenu already active");
return;
}
if(!submenu)
{
GWWARNING(L"Invalid submenu passed to OpenSubMenu");
return;
}
submenu->OnEnter(this);
if(m_subMenus.Num() > 0)
{
m_subMenus.Last()->OnSuspend(submenu, true);
}
m_subMenus.Add(submenu);
UWidgetBlueprintLibrary::SetFocusToGameViewport();
}
void UMenuScreenBase::CloseActiveSubMenu()
{
if(m_subMenus.Num() > 0)
{
CloseSubMenu(m_subMenus.Last());
}
else
{
GWWARNING("No SubMenus to close");
return;
}
}
void UMenuScreenBase::CloseSubMenu(class USubMenu* submenu)
{
if(!m_subMenus.Contains(submenu))
return;
submenu->OnLeave();
// Restore underlying menus if that was the top-most menu
if(m_subMenus.Num() > 1)
{
bool wasLast = m_subMenus.Last() == submenu;
m_subMenus.Remove(submenu);
if(wasLast)
{
m_subMenus.Last()->OnRestore(submenu);
}
}
else
{
// Remove last menu
m_subMenus.Remove(submenu);
}
UWidgetBlueprintLibrary::SetFocusToGameViewport();
}
TArray<class USubMenu*> UMenuScreenBase::GetSubMenus()
{
return m_subMenus;
}
class USubMenu* UMenuScreenBase::GetActiveSubMenu()
{
if(m_subMenus.Num() == 0)
return nullptr;
return m_subMenus.Last();
}
void UMenuScreenBase::SetButtonHintBar(class UButtonHintBar* bar)
{
m_buttonHintBar = bar;
}
class UButtonHintBar* UMenuScreenBase::GetButtonHintBar() const
{
return m_buttonHintBar;
}
void UMenuScreenBase::NativeOnMenuAction(EMenuActionBinding action)
{
if(m_subMenus.Num() > 0)
{
m_subMenus.Last()->NativeOnMenuAction(action);
}
Super::NativeOnMenuAction(action);
}

View File

@@ -0,0 +1,52 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuItemBase.h"
#include "Blueprint/UserWidget.h"
#include "MenuScreenBase.generated.h"
/**
* Base class for a menu item with controller navigation/input support
*/
UCLASS()
class UNREALPROJECT_API UMenuScreenBase : public UMenuItemBase
{
GENERATED_BODY()
public:
UMenuScreenBase(const FObjectInitializer& init);
void NativeConstruct() override;
void NativeDestruct() override;
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
virtual void OpenSubMenu(class USubMenu* submenu);
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
void CloseActiveSubMenu();
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
virtual void CloseSubMenu(class USubMenu* submenu);
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
TArray<class USubMenu*> GetSubMenus();
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
class USubMenu* GetActiveSubMenu();
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
void SetButtonHintBar(class UButtonHintBar* bar);
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
class UButtonHintBar* GetButtonHintBar() const;
void OnItemSelected();
virtual void NativeOnMenuAction(EMenuActionBinding action) override;
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "MenuScreen")
bool registerMenuActions;
protected:
UPROPERTY()
TArray<class USubMenu*> m_subMenus;
UPROPERTY()
class UButtonHintBar* m_buttonHintBar;
};

View File

@@ -0,0 +1,139 @@
#include "UnrealProject.h"
#include "MenuSlider.h"
#include "SizeBorder.h"
void UMenuSliderButton::NativeConstruct()
{
m_button = Cast<UButton>(WidgetTree->FindWidget("Button"));
m_dragBorder = Cast<UBorder>(WidgetTree->FindWidget("DragBorder"));
m_text = Cast<UTextBlock>(WidgetTree->FindWidget("Text"));
m_button->OnPressed.AddDynamic(this, &UMenuSliderButton::m_OnPressed);
m_panel = Cast<UCanvasPanel>(GetParent());
m_dragging = false;
Super::NativeConstruct();
SetSelected(false);
}
void UMenuSliderButton::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
UCanvasPanelSlot* canvasSlot = Cast<UCanvasPanelSlot>(Slot);
canvasSlot->SetAlignment(FVector2D(0.0f, 0.0f));
if(m_parent)
{
float totalPixelWidth = m_parent->m_sizeBorder->pixelSize.X;
float totalWidth = m_parent->m_sizeBorder->viewportSize.X;
float totalHeight = m_parent->m_sizeBorder->viewportSize.Y;
float buttonSize = totalWidth * 0.2f;
totalPixelWidth -= totalPixelWidth * 0.2f;
totalWidth -= buttonSize;
if(m_dragging)
{
float mouseX, mouseY;
GetOwningPlayer()->GetMousePosition(mouseX, mouseY);
float delta = (mouseX - m_dragStart.X) / (totalPixelWidth);
m_parent->value = FMath::Clamp(m_dragStartValue + delta, 0.0f, 1.0f);
if(!m_dragBorder->IsHovered() && !m_button->IsHovered())
{
m_EndDrag();
}
UpdateText();
}
canvasSlot->SetPosition(FVector2D(m_parent->value * totalWidth, 0.0f));
canvasSlot->SetSize(FVector2D(buttonSize, totalHeight));
}
Super::NativeTick(MyGeometry, InDeltaTime);
}
void UMenuSliderButton::UpdateText()
{
FString txt = FString::FromInt((int)(m_parent->value * 100)) + "%";
m_text->SetText(FText::FromString(txt));
}
void UMenuSliderButton::SetSelected(bool selected)
{
if(selected)
{
m_button->SetBackgroundColor(FLinearColor(0.3f, 0.3f, 1.0f, 0.8f));
}
else
{
m_button->SetBackgroundColor(FLinearColor(0.7f, 0.7f, 0.7f, 0.8f));
}
}
void UMenuSliderButton::m_OnPressed()
{
float mouseX, mouseY;
if(m_parent)
{
GetOwningPlayer()->GetMousePosition(mouseX, mouseY);
m_dragStart = FVector2D(mouseX, mouseY);
m_dragStartValue = m_parent->value;
m_dragging = true;
m_dragBorder->SetVisibility(ESlateVisibility::Visible);
}
}
void UMenuSliderButton::m_EndDrag()
{
m_dragging = false;
m_dragBorder->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
FReply UMenuSliderButton::NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
m_EndDrag();
return FReply::Unhandled();
}
UMenuSlider::UMenuSlider(const FObjectInitializer& init)
: Super(init)
{
stepGranularity = 1.0f / 25;
value = 0.5f;
}
void UMenuSlider::NativeConstruct()
{
Super::NativeConstruct();
m_sizeBorder = Cast<USizeBorder>(WidgetTree->FindWidget("SizeBorder"));
m_sliderButton = Cast<UMenuSliderButton>(WidgetTree->FindWidget("SliderButton"));
m_sliderButton->m_parent = this;
}
void UMenuSlider::SetValue(float _value)
{
value = FMath::Clamp(_value, 0.0f, 1.0f);
m_sliderButton->UpdateText();
}
void UMenuSlider::NativeOnMenuAction(EMenuActionBinding binding)
{
switch(binding)
{
case EMenuActionBinding::Left:
case EMenuActionBinding::Repeat_Left:
SetValue(value - stepGranularity);
break;
case EMenuActionBinding::Right:
case EMenuActionBinding::Repeat_Right:
SetValue(value + stepGranularity);
break;
}
Super::NativeOnMenuAction(binding);
}
void UMenuSlider::NativeOnSelectionChanged(bool selected, bool controller)
{
Super::NativeOnSelectionChanged(selected, controller);
if(m_sliderButton)
m_sliderButton->SetSelected(selected);
}

View File

@@ -0,0 +1,61 @@
#pragma once
#include "MenuButton.h"
#include "MenuSlider.generated.h"
UCLASS()
class UMenuSliderButton : public UMenuItemBase
{
GENERATED_BODY()
public:
void NativeConstruct() override ;
void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
void UpdateText();
void SetSelected(bool selected);
private:
UFUNCTION()
void m_OnPressed();
void m_EndDrag();
FReply NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
friend class UMenuSlider;
class UMenuSlider* m_parent;
UBorder* m_dragBorder;
UButton* m_button;
UTextBlock* m_text;
UCanvasPanel* m_panel;
FVector2D m_dragStart;
float m_dragStartValue;
bool m_dragging;
};
UCLASS()
class UMenuSlider : public UMenuButton
{
GENERATED_BODY()
public:
UMenuSlider(const FObjectInitializer& init);
void NativeConstruct() override;
void NativeOnMenuAction(EMenuActionBinding binding) override;
void NativeOnSelectionChanged(bool selected, bool controller) override;
UFUNCTION(BlueprintCallable, Category="Slider")
void SetValue(float value);
UPROPERTY(BlueprintReadOnly, Category = "Slider")
float value;
UPROPERTY(EditAnywhere, Category = "Slider")
float stepGranularity;
private:
friend class UMenuSliderButton;
class USizeBorder* m_sizeBorder;
class UMenuSliderButton* m_sliderButton;
};

View File

@@ -0,0 +1,78 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "OverlayMessageBox.h"
#include "ScreenOverlay.h"
void UOverlayMessageBoxItem::NativeConstruct()
{
m_label = Cast<UTextBlock>(WidgetTree->FindWidget("Label"));
onPressed.AddDynamic(this, &UOverlayMessageBoxItem::m_OnPressed);
Super::NativeConstruct();
}
void UOverlayMessageBoxItem::SetText(const FString& text)
{
if(!m_label)
{
GERROR("UOverlayMessageBoxItem Label not set on " + GetName());
return;
}
m_label->SetText(FText::FromString(text));
}
void UOverlayMessageBoxItem::m_OnPressed()
{
//Cast<UOverlayMessageBox>(GetSubMenu())->returnCode = index;
//UMenuScreenBase* screen = GetSubMenu()->GetScreen();
//check(m_info);
m_info->Close(index);
//if(screen)
// screen->CloseActiveSubMenu();
//else
// GERROR("screen == nullptr, should not happen");
}
void UOverlayMessageBox::NativeConstruct()
{
Super::NativeConstruct();
m_buttonContainer = Cast<UHorizontalBox>(WidgetTree->FindWidget("ButtonContainer"));
m_caption = Cast<UTextBlock>(WidgetTree->FindWidget("Caption"));
m_message = Cast<UTextBlock>(WidgetTree->FindWidget("Message"));
}
void UOverlayMessageBox::SetMessageBoxInfo(class UMsgBoxInfo* info)
{
if(!buttonItemClass)
{
GERROR("Button item class not set on " + GetName());
return;
}
m_buttonContainer->ClearChildren();
m_info = info;
UOverlayMessageBoxItem* selectItem = 0;
for(FString option : info->options)
{
UOverlayMessageBoxItem* item = CreateWidget<UOverlayMessageBoxItem>(GetWorld(), buttonItemClass);
item->m_info = info;
UHorizontalBoxSlot* slot = Cast<UHorizontalBoxSlot>(m_buttonContainer->AddChild(item));
slot->SetHorizontalAlignment(HAlign_Fill);
slot->SetVerticalAlignment(VAlign_Fill);
slot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
item->SetButtonText(option);
if(option == info->defaultOption)
selectItem = item;
else if(!selectItem)
selectItem = item;
}
m_caption->SetText(FText::FromString(info->caption));
m_message->SetText(FText::FromString(info->message));
RescanItems();
SelectNewItem(selectItem);
}

View File

@@ -0,0 +1,46 @@
// Project Lab - NHTV Igad
#pragma once
#include "SubMenu.h"
#include "MenuButton.h"
#include "OverlayMessageBox.generated.h"
UCLASS()
class UOverlayMessageBoxItem : public UMenuButton
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
void SetText(const FString& text);
private:
void m_OnPressed();
friend class UOverlayMessageBox;
UTextBlock* m_label;
class UMsgBoxInfo* m_info;
};
UCLASS()
class UOverlayMessageBox : public USubMenu
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
void SetMessageBoxInfo(class UMsgBoxInfo* info);
UPROPERTY(BlueprintReadWrite, Category = "MsgBox")
int32 returnCode;
UPROPERTY(EditDefaultsOnly, Category = "MsgBox")
TSubclassOf<UOverlayMessageBoxItem> buttonItemClass;
private:
UPROPERTY()
UHorizontalBox* m_buttonContainer;
UPROPERTY()
UTextBlock* m_caption;
UPROPERTY()
UTextBlock* m_message;
class UMsgBoxInfo* m_info;
};

View File

@@ -0,0 +1,24 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "OverlayProgressBar.h"
#include "ScreenOverlay.h"
void UOverlayProgressBar::NativeConstruct()
{
m_text = Cast<UTextBlock>(WidgetTree->FindWidget("Text"));
if(!m_text)
{
GERROR("No \"Text\" element in overlay progress bar");
}
Super::NativeConstruct();
}
void UOverlayProgressBar::SetOverlayInfo(class UOverlayInfo* info)
{
if(m_text)
{
m_text->SetText(FText::FromString(info->message));
}
}

View File

@@ -0,0 +1,23 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/SubMenu.h"
#include "OverlayProgressBar.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UOverlayProgressBar : public USubMenu
{
GENERATED_BODY()
public:
virtual void NativeConstruct();
// Updates the text,etc. on the progress bar overlay
void SetOverlayInfo(class UOverlayInfo* info);
private:
UTextBlock* m_text;
};

View File

@@ -0,0 +1,78 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "ScoreBoardSlot.h"
#include "DefaultPlayerState.h"
#include "GameStateBase.h"
#include "TeamData.h"
#include "ScoreBoard.h"
UScoreBoard::UScoreBoard(const FObjectInitializer& init) : Super(init)
{
teamData = ConstructorHelpers::FObjectFinder<UTeamData>(TEXT("/Game/Assets/TeamData")).Object;
}
void UScoreBoard::Init(UVerticalBox* container)
{
m_container = container;
// Precreate the widgets so that we can utilize them later
if (IsValid(scoreBoardSlot) && IsValid(m_container))
{
for (int32 i = 0; i < 8; i++)
{
UScoreBoardSlot* widget = CreateWidget<UScoreBoardSlot>(GetWorld(), scoreBoardSlot);
m_container->AddChild(widget);
m_slots.Add(widget);
widget->SetVisibility(ESlateVisibility::Hidden);
}
}
}
void UScoreBoard::UpdateScoreBoard(int32 sort)
{
UWorld* world = GetWorld();
if (!IsValid(world)) return;
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
if (!IsValid(gameState)) return;
TArray<APlayerStateBase*> players = gameState->GetPlayers();
// Fetch and update
TArray<ADefaultPlayerState*> playersSorted;
for (int32 i = 0; i < players.Num(); i++)
{
ADefaultPlayerState* const state = Cast<ADefaultPlayerState>(players[i]);
if (!IsValid(state)) continue;
state->UpdatePersona();
playersSorted.Add(state);
}
// Sort based on category
if(sort == 0)
playersSorted.Sort([](const ADefaultPlayerState& i, const ADefaultPlayerState& j)->bool { return i.nickname < j.nickname; });
else if(sort == 1)
playersSorted.Sort([](const ADefaultPlayerState& i, const ADefaultPlayerState& j)->bool { return i.kills > j.kills; });
else if (sort == 2)
playersSorted.Sort([](const ADefaultPlayerState& i, const ADefaultPlayerState& j)->bool { return i.deaths > j.deaths; });
else if (sort == 3)
playersSorted.Sort([](const ADefaultPlayerState& i, const ADefaultPlayerState& j)->bool { return float(i.kills) / float(i.deaths) > float(j.kills) / float(j.deaths); });
// Activate the slots and set them to the person
for (int32 i = 0; i < m_slots.Num(); i++)
{
if (i >= playersSorted.Num())
{
m_slots[i]->SetVisibility(ESlateVisibility::Hidden);
continue;
}
else
m_slots[i]->SetVisibility(ESlateVisibility::Visible);
ADefaultPlayerState* const state = playersSorted[i];
m_slots[i]->SetVisibility(ESlateVisibility::Visible);
m_slots[i]->Update(state->avatar, FText::FromString(state->nickname), state->kills, state->deaths, teamData->GetTeamColor(state->GetTeam()));
}
}

View File

@@ -0,0 +1,34 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "ScoreBoard.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UScoreBoard : public UUserWidget
{
GENERATED_BODY()
public:
UScoreBoard(const FObjectInitializer& init);
UPROPERTY(EditAnywhere, Category = "Score Board")
TSubclassOf<class UScoreBoardSlot> scoreBoardSlot;
UFUNCTION(BlueprintCallable, Category = "Score Board")
void Init(class UVerticalBox* container);
UFUNCTION(BlueprintCallable, Category = "Score Board")
void UpdateScoreBoard(int32 sort);
UPROPERTY(BlueprintReadonly, Category = "SCore Board")
class UTeamData* teamData;
private:
class UVerticalBox* m_container;
TArray<class UScoreBoardSlot*> m_slots;
};

View File

@@ -0,0 +1,4 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "ScoreBoardSlot.h"

View File

@@ -0,0 +1,19 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "ScoreBoardSlot.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UScoreBoardSlot : public UUserWidget
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintImplementableEvent, Category = "Score Board Slot")
void Update(UTexture2D* image, const FText& name, int32 kills, int32 deaths, const FLinearColor& color);
};

View File

@@ -0,0 +1,195 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "ScreenOverlay.h"
#include "OverlayMessageBox.h"
#include "OverlayProgressBar.h"
UScreenOverlay::UScreenOverlay(const FObjectInitializer& init)
: Super(init)
{
}
void UScreenOverlay::NativeConstruct()
{
Super::NativeConstruct();
m_msgBox = Cast<UOverlayMessageBox>(WidgetTree->FindWidget("MessageBox"));
m_progressBar = Cast<UOverlayProgressBar>(WidgetTree->FindWidget("ProgressBar"));
m_background = Cast<UBorder>(WidgetTree->FindWidget("Background"));
check(m_msgBox);
check(m_progressBar);
check(m_background);
UpdateOverlays();
UpdateMsgBoxes();
UpdateVisiblity();
m_msgBox->onClosed.AddDynamic(this, &UScreenOverlay::OnMessageBoxClosed);
}
void UScreenOverlay::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
// Check all active screen overlays, remove the inactive ones
for(int32 i = 0; i < m_overlayItems.Num();)
{
if(m_overlayItems[i]->m_remove)
{
m_overlayItems.RemoveAt(i);
UpdateOverlays();
UpdateVisiblity();
}
else
{
i++;
}
}
// Check the currently shown message box, remove it if inactive
UMsgBoxInfo* msgBox = GetMessageBoxItem();
if(msgBox && msgBox->m_remove)
{
if(msgBox->onClosed.IsBound())
{
msgBox->onClosed.Execute(msgBox->m_closeResult);
}
m_messageBoxItems.Remove(msgBox);
UpdateMsgBoxes();
UpdateVisiblity();
// Show remaining message boxes
if(m_messageBoxItems.Num() > 0)
{
OpenSubMenu(m_msgBox);
}
}
}
UOverlayInfo* UScreenOverlay::ShowOverlay(FString message)
{
// Add a new overlay item to the list
UOverlayInfo* info = NewObject<UOverlayInfo>();
info->message = message;
m_overlayItems.Add(info);
// Update Widget blueprint
UpdateOverlays();
UpdateVisiblity();
return info;
}
UMsgBoxInfo* UScreenOverlay::ShowMessageBox(FString caption, FString message, TArray<FString> options, FString def /*= FString()*/)
{
// Add a new message box at the end of the queue
UMsgBoxInfo* info = NewObject<UMsgBoxInfo>();
info->message = message;
info->caption = caption;
info->options = options;
info->defaultOption = def;
m_messageBoxItems.Add(info);
// Update Widget blueprint
UpdateMsgBoxes();
UpdateVisiblity();
OpenSubMenu(m_msgBox);
return info;
}
UMsgBoxInfo* UScreenOverlay::ShowMessageBoxCallback(FOverlayItemClosed cb, FString caption, FString message, TArray<FString> options, FString def /*= ""*/)
{
// Add a new message box at the end of the queue
UMsgBoxInfo* info = NewObject<UMsgBoxInfo>();
info->message = message;
info->caption = caption;
info->options = options;
info->onClosed = cb;
info->defaultOption = def;
m_messageBoxItems.Add(info);
// Update Widget blueprint
UpdateMsgBoxes();
UpdateVisiblity();
OpenSubMenu(m_msgBox);
return info;
}
void UScreenOverlay::OnMessageBoxClosed()
{
m_messageBoxItems.Last()->Close(m_msgBox->returnCode);
}
TArray<UOverlayInfo*>& UScreenOverlay::GetOverlayItems()
{
return m_overlayItems;
}
UMsgBoxInfo* UScreenOverlay::GetMessageBoxItem()
{
if(m_messageBoxItems.Num() == 0)
{
return nullptr;
}
return m_messageBoxItems[0];
}
void UScreenOverlay::UpdateOverlays()
{
if(m_overlayItems.Num() != 0)
{
m_progressBar->SetVisibility(ESlateVisibility::Visible);
m_progressBar->SetOverlayInfo(m_overlayItems.Last());
}
else
{
m_progressBar->SetVisibility(ESlateVisibility::Hidden);
}
}
void UScreenOverlay::UpdateMsgBoxes()
{
if(m_messageBoxItems.Num() != 0)
{
m_msgBox->SetMessageBoxInfo(GetMessageBoxItem());
m_msgBox->SetVisibility(ESlateVisibility::Visible);
}
else
{
m_msgBox->SetVisibility(ESlateVisibility::Hidden);
}
}
void UScreenOverlay::UpdateVisiblity()
{
if(m_messageBoxItems.Num() != 0 || m_overlayItems.Num() != 0)
{
m_background->SetVisibility(ESlateVisibility::Visible);
blockInput = true;
}
else
{
m_background->SetVisibility(ESlateVisibility::Hidden);
blockInput = false;
}
}
UOverlayQueueItem::UOverlayQueueItem(const FObjectInitializer& init)
: Super(init)
{
m_remove = false;
}
void UOverlayQueueItem::Close(int32 result)
{
m_remove = true;
m_closeResult = result;
}
UMsgBoxInfo::UMsgBoxInfo(const FObjectInitializer& init)
: Super(init)
{
message = "nothing";
options.Add("OK");
}
UOverlayInfo::UOverlayInfo(const FObjectInitializer& init)
: Super(init)
{
blockInput = true;
message = "nothing";
}

View File

@@ -0,0 +1,104 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuScreenBase.h"
#include "ScreenOverlay.generated.h"
UENUM(BlueprintType)
enum class EMessageBoxType : uint8
{
Ok,
OkCancel,
YesNo
};
UCLASS()
class UOverlayQueueItem : public UObject
{
GENERATED_BODY()
public:
UOverlayQueueItem(const FObjectInitializer& init);
// Close this overlay item
UFUNCTION(BlueprintCallable, Category = "Overlay")
void Close(int32 result = 0);
UPROPERTY()
FOverlayItemClosed onClosed;
private:
friend class UScreenOverlay;
bool m_remove;
int32 m_closeResult;
};
UCLASS()
class UMsgBoxInfo : public UOverlayQueueItem
{
GENERATED_BODY()
public:
UMsgBoxInfo(const FObjectInitializer& init);
UPROPERTY(BlueprintReadOnly, Category = "MsgBox")
FString caption;
UPROPERTY(BlueprintReadOnly, Category = "MsgBox")
FString message;
UPROPERTY(BlueprintReadOnly, Category = "MsgBox")
TArray<FString> options;
UPROPERTY(BlueprintReadOnly, Category = "MsgBox")
FString defaultOption;
};
UCLASS()
class UOverlayInfo : public UOverlayQueueItem
{
GENERATED_BODY()
public:
UOverlayInfo(const FObjectInitializer& init);
UPROPERTY(BlueprintReadOnly, Category = "MsgBox")
FString message;
UPROPERTY(BlueprintReadOnly, Category = "Overlay")
bool blockInput;
};
UCLASS()
class UNREALPROJECT_API UScreenOverlay : public UMenuScreenBase
{
GENERATED_BODY()
public:
UScreenOverlay(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
UFUNCTION(BlueprintCallable, Category = "Overlay")
UOverlayInfo* ShowOverlay(FString message);
UMsgBoxInfo* ShowMessageBox(FString caption, FString message, TArray<FString> options, FString def = "");
UMsgBoxInfo* ShowMessageBoxCallback(FOverlayItemClosed cb, FString caption, FString message, TArray<FString> options, FString def);
//UFUNCTION(BlueprintCallable, Category = "Overlay")
//UMsgBoxInfo* ShowMessageBox(FOnOverlayItemClosedSingle cb, FString caption, FString message, TArray<FString> options, FString def = "");
UFUNCTION()
void OnMessageBoxClosed();
UFUNCTION(BlueprintCallable, Category = "Overlay")
TArray<UOverlayInfo*>& GetOverlayItems();
UFUNCTION(BlueprintCallable, Category = "Overlay")
UMsgBoxInfo* GetMessageBoxItem();
private:
void UpdateOverlays();
void UpdateMsgBoxes();
void UpdateVisiblity();
UPROPERTY()
class UOverlayMessageBox* m_msgBox;
UPROPERTY()
class UOverlayProgressBar* m_progressBar;
UPROPERTY()
class UBorder* m_background;
UPROPERTY()
TArray<UOverlayInfo*> m_overlayItems;
UPROPERTY()
TArray<UMsgBoxInfo*> m_messageBoxItems;
};

View File

@@ -0,0 +1,10 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SelectButton.h"
USelectButton::USelectButton(const FObjectInitializer& init) : UMenuButton(init)
{
selected = 0;
}

View File

@@ -0,0 +1,22 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuButton.h"
#include "SelectButton.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API USelectButton : public UMenuButton
{
GENERATED_BODY()
public:
USelectButton(const FObjectInitializer& init);
UPROPERTY(BlueprintReadWrite, Category = "SelectButton")
int32 selected;
};

View File

@@ -0,0 +1,130 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SkillSelector.h"
#include "SkillTreeObject.h"
#include "SkillObject.h"
#include "SkillWidget.h"
#include "SkillSelectorItem.h"
#include "SkillTreeWidget.h"
#include "BaseSkillObject.h"
#include "AbilityInfo.h"
TSubclassOf<class USkillSelector> defaultSkillSelector;
USkillSelector::USkillSelector(const FObjectInitializer& init)
: Super(init)
{
defaultSkillSelector = ConstructorHelpers::FClassFinder<USkillSelector>(L"/Game/Assets/GUI/Components/WEEGEE_SkillSelector").Class;
}
void USkillSelector::NativeConstruct()
{
Super::NativeConstruct();
m_selectedItem = 0;
TArray<UWidget*> swidgets;
m_scrollBox = WidgetTree->FindWidget<UScrollBox>("ScrollBox");
m_border = WidgetTree->FindWidget<UBorder>("Border");
m_sizeRoot = WidgetTree->FindWidget<USizeBox>("SizeRoot");
if(!m_sizeRoot)
{
GWERROR(L"USkillSelector doesn't have a SizeRoot set");
}
if(!m_scrollBox)
{
GWERROR(L"USkillSelector doesn't have a ScrollBox set");
}
if(!ItemWidgetTemplate)
{
GWERROR(L"USkillSelector doesn't have a Item Widget Template set");
return;
}
}
void USkillSelector::NativeOnMenuAction(EMenuActionBinding binding)
{
switch(binding)
{
case EMenuActionBinding::Left:
SetSelection(m_selectedItem - 1);
break;
case EMenuActionBinding::Right:
SetSelection(m_selectedItem + 1);
break;
default:
Super::NativeOnMenuAction(binding);
break;
}
}
void USkillSelector::SetSelection(int32 idx)
{
if(idx < 0 || idx >= m_scrollBox->GetChildrenCount())
return;
if(m_selectedItem < m_scrollBox->GetChildrenCount())
SetItemSelected(m_selectedItem, false);
SetItemSelected(idx, true);
m_selectedItem = idx;
if(m_sizeRoot && m_scrollBox)
{
m_scrollBox->ScrollWidgetIntoView(m_items[m_selectedItem], true);
}
}
void USkillSelector::SetItemSelected(int32 item, bool selected)
{
m_items[item]->NativeOnSelectionChanged(selected, false);
if(selected)
onSkillSelectionChanged.Broadcast(m_items[item]);
}
void USkillSelector::SetSkillTree(class USkillTreeWidget* skillTree)
{
if(!skillTree)
return;
this->skillTree = skillTree;
USkillTreeObject* treeObject = skillTree->skillTreeAsset;
if(!m_scrollBox)
return;
if(!ItemWidgetTemplate)
return;
m_scrollBox->ClearChildren();
m_items.SetNum(0);
uint32 itemIndex = 0;
for(int32 i = 0; i < treeObject->skills.Num(); i++)
{
UBaseSkillObject* skill = treeObject->skills[i]->GetDefaultObject<UBaseSkillObject>();
if(!skillShapeFilter.Contains(skill->skillShapeType))
continue;
USkillSelectorItem* widget = CreateWidget<USkillSelectorItem>(GetWorld(), ItemWidgetTemplate);
widget->baseSkillObject = skill;
widget->index = itemIndex++;
widget->parent = this;
widget->skillTree = skillTree;
m_scrollBox->AddChild(widget);
m_items.Add(widget);
}
SetSelection(FMath::Min(m_selectedItem, m_items.Num() - 1));
}
USkillWidget* USkillSelector::GetSelectedSkillWidget()
{
if(m_selectedItem >= m_items.Num())
return nullptr;
return m_items[m_selectedItem]->GetSkillWidget();
}
UBaseSkillObject* USkillSelector::GetSelectedSkillAsset()
{
if(m_selectedItem >= m_items.Num())
return nullptr;
return m_items[m_selectedItem]->baseSkillObject;
}

View File

@@ -0,0 +1,54 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/MenuButton.h"
#include "BaseSkillObject.h"
#include "SkillSelector.generated.h"
extern TSubclassOf<class USkillSelector> defaultSkillSelector;
UCLASS()
class UNREALPROJECT_API USkillSelector : public UMenuButton
{
GENERATED_BODY()
public:
USkillSelector(const FObjectInitializer& init);
void NativeConstruct() override;
virtual void NativeOnMenuAction(EMenuActionBinding binding) override;
UFUNCTION(BlueprintCallable, Category="Skill Selector")
void SetSkillTree(class USkillTreeWidget* skillTree);
UPROPERTY(BlueprintReadOnly, Category = "UI")
class USkillTreeWidget* skillTree;
UPROPERTY(EditAnywhere, Category = "UI")
TArray<ESkillShapeType> skillShapeFilter;
int32 GetSelection() const { return m_selectedItem; }
void SetSelection(int32 idx);
void SetItemSelected(int32 item, bool selected);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSkillSelectionChanged, USkillSelectorItem*, item);
UPROPERTY(BlueprintAssignable)
FOnSkillSelectionChanged onSkillSelectionChanged;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
TSubclassOf<class USkillSelectorItem> ItemWidgetTemplate;
UFUNCTION(BlueprintCallable, Category = UI)
class USkillWidget* GetSelectedSkillWidget();
UFUNCTION(BlueprintCallable, Category = UI)
class UBaseSkillObject* GetSelectedSkillAsset();
private:
UPROPERTY()
TArray<class USkillSelectorItem*> m_items;
int32 m_selectedItem;
USizeBox* m_sizeRoot;
UScrollBox* m_scrollBox;
UBorder* m_border;
};

View File

@@ -0,0 +1,76 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SkillSelectorItem.h"
#include "SkillSelector.h"
#include "SkillWidget.h"
#include "SkillTreeWidget.h"
void USkillSelectorItem::NativeConstruct()
{
Super::NativeConstruct();
m_skillWidget = WidgetTree->FindWidget<USkillWidget>("InnerSkillWidget");
if(!m_skillWidget)
{
GWERROR(L"InnerSkillWidget not found on " + GetName());
return;
}
else
{
m_skillWidget->skillAsset = baseSkillObject;
m_skillWidget->parent = skillTree;
m_skillWidget->UpdateSkill();
}
if(m_button)
{
m_button->OnPressed.AddDynamic(this, &USkillSelectorItem::m_OnPressed1);
}
else
{
GWERROR(L"No button found on " + GetName());
}
}
void USkillSelectorItem::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
if(skillTree->IsUsingSkill(baseSkillObject))
{
this->SetIsEnabled(false);
}
else
{
this->SetIsEnabled(true);
}
}
void USkillSelectorItem::NativeOnSelectionChanged(bool selected, bool controller)
{
if(selected)
{
m_button->SetBackgroundColor(FLinearColor(1.0f, 1.0f, 1.0f, 0.5f));
}
else
{
m_button->SetBackgroundColor(FLinearColor(1.0f, 1.0f, 1.0f, 0.0f));
}
}
void USkillSelectorItem::m_OnPressed1()
{
if(!GetIsEnabled())
return;
if(parent)
{
USubMenu* menu = parent->GetSubMenu();
if(menu)
menu->SelectNewItem(parent);
//if(parent->GetSelection() == index)
skillTree->NewSkill(m_skillWidget);
parent->SetSelection(index);
//else
}
}

View File

@@ -0,0 +1,50 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuButton.h"
#include "SkillSelectorItem.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API USkillSelectorItem : public UMenuButton
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
virtual void NativeOnSelectionChanged(bool selected, bool controller) override;
//virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
int32 index;
class USkillSelector* parent;
class UBaseSkillObject* baseSkillObject;
UPROPERTY(BlueprintReadOnly, Category = UI)
class USkillTreeWidget* skillTree;
UPROPERTY(EditDefaultsOnly, Category = UI)
TSubclassOf<class USkillWidget> skillWidget;
UFUNCTION(BlueprintCallable, Category = UI)
USkillSelector* GetSelector() const
{
return parent;
}
UFUNCTION(BlueprintCallable, Category = UI)
class USkillWidget* GetSkillWidget()
{
return m_skillWidget;
}
private:
UFUNCTION()
void m_OnPressed1();
class USkillWidget* m_skillWidget;
};

View File

@@ -0,0 +1,96 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SplashScreen.h"
#include "SplashScreenItem.h"
#include "DefaultGameInstance.h"
#include "SessionManager.h"
USplashScreen::USplashScreen(const FObjectInitializer& init)
: Super(init)
{
}
void USplashScreen::NativeConstruct()
{
m_currentItem = -1;
Super::NativeConstruct();
}
void USplashScreen::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
if(m_currentItem == -1)
return;
if(m_currentItem >= m_activeScreens.Num())
{
OnEnd();
// Send done event
onDone.Broadcast();
m_currentItem = -1;
}
else
{
m_switcher->SetActiveWidget(m_activeScreens[m_currentItem]);
if(m_activeScreens[m_currentItem]->GetRate() >= 1.0f)
{
m_activeScreens[m_currentItem]->End();
m_currentItem++;
if(m_currentItem < m_activeScreens.Num())
{
m_activeScreens[m_currentItem]->Start();
}
}
}
}
void USplashScreen::NativeOnMenuAction(EMenuActionBinding binding)
{
if(binding == EMenuActionBinding::Start)
{
if(m_currentItem >= 0 && m_currentItem < m_activeScreens.Num())
{
m_activeScreens[m_currentItem]->Skip();
}
}
}
void USplashScreen::OnEnd_Implementation()
{
}
void USplashScreen::Init(UWidgetSwitcher* witcher)
{
m_switcher = witcher;
m_currentItem = 0;
for(int32 i = 0; i < splashItems.Num(); i++)
{
USplashScreenItem* item = CreateWidget<USplashScreenItem>(GetWorld(), splashItems[i]);
m_activeScreens.Add(item);
m_switcher->AddChild(item);
if(i == 0)
{
item->Start();
if((i + 1) == splashItems.Num())
item->fadeEnabled = false;
m_switcher->SetActiveWidget(item);
}
}
}
float USplashScreen::GetFade() const
{
if(m_currentItem == -1)
return 0.0f;
if(m_currentItem >= m_activeScreens.Num())
{
return 0.0f;
}
if((m_currentItem + 1) == m_activeScreens.Num())
{
return m_activeScreens[m_currentItem]->GetFadeOut();
}
return 1.0f;
}

View File

@@ -0,0 +1,45 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuScreenBase.h"
#include "SplashScreen.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API USplashScreen : public UMenuScreenBase
{
GENERATED_BODY()
public:
USplashScreen(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
virtual void NativeOnMenuAction(EMenuActionBinding binding) override;
UFUNCTION(BlueprintNativeEvent, Category = "Splash")
void OnEnd();
UFUNCTION(BlueprintCallable, Category="Splash")
void Init(UWidgetSwitcher* witcher);
UFUNCTION(BlueprintCallable, Category="Splash")
float GetFade() const;
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category="Splash")
TArray<TSubclassOf<class USplashScreenItem>> splashItems;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSplashScreenDone);
UPROPERTY(BlueprintAssignable, Category = "Splash")
FOnSplashScreenDone onDone;
public:
UPROPERTY()
TArray<class USplashScreenItem*> m_activeScreens;
int32 m_currentItem;
UWidgetSwitcher* m_switcher;
};

View File

@@ -0,0 +1,104 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SplashScreenItem.h"
#include "MediaTexture.h"
USplashScreenItem::USplashScreenItem(const FObjectInitializer& init)
: Super(init)
{
duration = 2.0f;
fadeDuration = 0.5f;
m_lifetime = 0.0f;
fadeEnabled = true;
}
void USplashScreenItem::NativeConstruct()
{
if(duration <= 0.0f)
{
GWWARNING(L"Splash screen duration is zero! " + GetName());
duration = 1.0f;
}
if(fadeDuration * 2.0f > duration)
{
GWWARNING(L"Splash screen fade time is greater than duration! " + GetName());
fadeDuration = duration * 0.25f;
}
m_started = false;
Super::NativeConstruct();
}
void USplashScreenItem::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
if(m_started)
m_lifetime += InDeltaTime;
Super::NativeTick(MyGeometry, InDeltaTime);
}
void USplashScreenItem::OnStarted_Implementation()
{
}
void USplashScreenItem::OnEnd_Implementation()
{
}
void USplashScreenItem::Skip()
{
if((m_lifetime + fadeDuration) < duration)
{
m_lifetime = duration - fadeDuration;
}
}
void USplashScreenItem::Start()
{
if(!m_started)
{
m_started = true;
m_lifetime = 0.0f;
OnStarted();
}
}
void USplashScreenItem::End()
{
if(m_started)
{
OnEnd();
m_started = false;
}
}
FVector2D USplashScreenItem::GetMediaSize(UMediaTexture* mediaTexture) const
{
if(!mediaTexture)
return FVector2D();
return FVector2D(mediaTexture->GetSurfaceWidth(), mediaTexture->GetSurfaceHeight());
}
float USplashScreenItem::GetFade() const
{
float base = GetFadeOut();
if(m_lifetime < fadeDuration)
{
base *= m_lifetime / fadeDuration;
}
return base;
}
float USplashScreenItem::GetFadeOut() const
{
if((duration - m_lifetime) < fadeDuration)
{
return FMath::Min((duration - m_lifetime) / fadeDuration, 1.0f);
}
else
{
return 1.0f;
}
}
float USplashScreenItem::GetRate() const
{
return FMath::Clamp(m_lifetime / duration, 0.0f, 1.0f);
}

View File

@@ -0,0 +1,52 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuItemBase.h"
#include "SplashScreenItem.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API USplashScreenItem : public UMenuItemBase
{
GENERATED_BODY()
public:
USplashScreenItem(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
UFUNCTION(BlueprintNativeEvent, Category = "SplashItem")
void OnStarted();
UFUNCTION(BlueprintNativeEvent, Category = "SplashItem")
void OnEnd();
UFUNCTION(BlueprintCallable, Category = "SplashItem")
void Skip();
void Start();
void End();
UFUNCTION(BlueprintCallable, Category="Media")
FVector2D GetMediaSize(class UMediaTexture* mediaTexture) const;
UFUNCTION(BlueprintCallable, Category="SplashItem")
float GetFade() const;
UFUNCTION(BlueprintCallable, Category = "SplashItem")
float GetFadeOut() const;
UFUNCTION(BlueprintCallable, Category="SplashItem")
float GetRate() const;
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "SplashItem")
float fadeDuration;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="SplashItem")
float duration;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SplashItem")
bool fadeEnabled;
private:
bool m_started;
float m_lifetime;
};

View File

@@ -0,0 +1,10 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "StartPromptScreen.h"
void UStartPromptScreen::Close()
{
onClosed.Broadcast();
OnClose();
}

View File

@@ -0,0 +1,26 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/MenuScreenBase.h"
#include "StartPromptScreen.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UStartPromptScreen : public UMenuScreenBase
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category="Start Prompt")
void Close();
UFUNCTION(BlueprintImplementableEvent)
void OnClose();
DECLARE_MULTICAST_DELEGATE(FClosedEvent)
FClosedEvent onClosed;
};

View File

@@ -0,0 +1,319 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SubMenu.h"
#include "PlayerControllerBase.h"
#include "MenuButton.h"
#include "DefaultGameInstance.h"
#include "MenuScreenBase.h"
#include "EffectFunctionLibrary.h"
#include "ScreenOverlay.h"
USoundBase* confirmAudioClass;
USoundBase* backAudioClass;
USoundBase* selectionChangedAudioClass;
USoundBase* clickAudioClass;
USubMenu::USubMenu(const FObjectInitializer& init)
{
selectionChangedAudioClass = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Game/Assets/GUI/Components/InterfaceSounds/Select2")).Object;
backAudioClass = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Game/Assets/GUI/Components/InterfaceSounds/Select0")).Object;
confirmAudioClass = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Game/Assets/GUI/Components/InterfaceSounds/Select1")).Object;
clickAudioClass = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Game/Assets/Sound/T_SkillDrop")).Object;
}
void USubMenu::NativeConstruct()
{
m_focus = false;
m_layoutDirection = 0;
container = Cast<UPanelWidget>(WidgetTree->FindWidget("SubMenuContainer"));
if(!container)
{
container = Cast<UPanelWidget>(GetRootWidget());
if(!container)
{
GERROR("No \"Container\" found in widget " + GetName() + ", root widget was not a PanelWidget");
}
}
Super::NativeConstruct();
m_selectedItem = 0;
RescanItems();
}
void USubMenu::NativeDestruct()
{
Super::NativeDestruct();
}
void USubMenu::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
if(HasFocus())
{
for(UMenuItemBase* item : m_items)
{
item->FocusTick(InDeltaTime);
}
}
}
void USubMenu::m_ScanContainer(UWidget* widget, int32 depth /*= 0*/)
{
// Loop through panel widgets
UPanelWidget* panel = Cast<UPanelWidget>(widget);
if(panel)
{
for(int32 i = 0; i < panel->GetChildrenCount(); i++)
{
m_ScanContainer(panel->GetChildAt(i), depth+1);
}
return;
}
// Process Menu items
UMenuItemBase* item = Cast<UMenuItemBase>(widget);
if(item)
{
bool hidden = item->GetVisibility() == ESlateVisibility::Hidden || item->GetVisibility() == ESlateVisibility::Collapsed;
if(!hidden)
{
m_items.Add(item);
// Assign item to a submenu
item->m_subMenu = this;
}
return;
}
UPanelUserWidget* puw = Cast<UPanelUserWidget>(widget);
if(puw)
{
m_ScanContainer(puw->GetRootWidget());
return;
}
}
void USubMenu::RescanItems()
{
m_items.SetNum(0);
if(container)
{
if(Cast<UHorizontalBox>(container))
{
m_layoutDirection = 1;
}
else
{
m_layoutDirection = 0;
}
// Scan for buttons
m_ScanContainer(container);
int32 childCount = m_items.Num();
// Clamp selection
m_selectedItem = FMath::Clamp(m_selectedItem, 0, childCount - 1);
// Set active selection
for(int32 i = 0; i < childCount; i++)
{
UMenuItemBase* item = m_items[i];
item->index = i;
if(i == m_selectedItem)
{
item->NativeOnSelectionChanged(true, true);
}
else
{
item->NativeOnSelectionChanged(false, true);
}
}
// Reselect single items
if(childCount == 1)
{
m_items[0]->NativeOnSelectionChanged(false, true);
m_items[0]->NativeOnSelectionChanged(true, true);
}
if(childCount > 0)
{
// Selection event
onItemSelected.Broadcast(m_items[m_selectedItem]);
}
}
else
{
GWWARNING(L"SubMenu Container not set");
return;
}
}
void USubMenu::CloseSubMenu()
{
if(m_screen)
m_screen->CloseSubMenu(this);
}
void USubMenu::OnEnter(class UMenuScreenBase* screen)
{
check(screen);
m_screen = screen;
SetButtonHints();
onOpened.Broadcast();
m_SetFocus(true);
}
void USubMenu::OnLeave()
{
onClosed.Broadcast();
m_screen = nullptr;
m_SetFocus(false);
}
bool USubMenu::HasFocus() const
{
return m_focus;
}
void USubMenu::OnSuspend(USubMenu* newSubMenu, bool loseFocus)
{
if(loseFocus)
m_SetFocus(false);
onSuspend.Broadcast();
}
void USubMenu::OnRestore(USubMenu* removedMenu)
{
SetButtonHints();
m_SetFocus(true);
onRestore.Broadcast();
}
class UButtonHintBar* USubMenu::GetButtonHintBar() const
{
return GetScreen()->GetButtonHintBar();
}
class UCharacterSettings* USubMenu::GetCharacterSettings() const
{
return GetGameInstance()->GetCharacterSettings();
}
class UPrefs* USubMenu::GetPrefs() const
{
return GetGameInstance()->GetPrefs();
}
class UDefaultGameInstance* USubMenu::GetGameInstance() const
{
return Cast<UDefaultGameInstance>(GetWorld()->GetGameInstance());
}
void USubMenu::NativeOnMenuAction(EMenuActionBinding binding)
{
if(m_items.Num() > 0)
m_items[m_selectedItem]->NativeOnMenuAction(binding);
switch(binding)
{
case EMenuActionBinding::Down:
if(m_items.Num() > 0 && m_layoutDirection == 0)
SelectNewItemByIndex(FMath::Clamp(m_selectedItem + 1, 0, GetNumItems()-1), true);
break;
case EMenuActionBinding::Up:
if(m_items.Num() > 0 && m_layoutDirection == 0)
SelectNewItemByIndex(FMath::Clamp(m_selectedItem - 1, 0, GetNumItems()-1), true);
break;
case EMenuActionBinding::Left:
if(m_items.Num() > 0 && m_layoutDirection == 1)
SelectNewItemByIndex(FMath::Clamp(m_selectedItem - 1, 0, GetNumItems() - 1), true);
break;
case EMenuActionBinding::Right:
if(m_items.Num() > 0 && m_layoutDirection == 1)
SelectNewItemByIndex(FMath::Clamp(m_selectedItem + 1, 0, GetNumItems() - 1), true);
break;
case EMenuActionBinding::Confirm:
break;
case EMenuActionBinding::Back:
onBack.Broadcast();
UEffectFunctionLibrary::PlaySoundEffect2D(GetWorld(), backAudioClass);
break;
}
Super::NativeOnMenuAction(binding);
}
void USubMenu::NativeOnSelectionConfirmed(UMenuItemBase* item)
{
UEffectFunctionLibrary::PlaySoundEffect2D(GetWorld(), clickAudioClass);
onItemSelectionConfirmed.Broadcast(m_items[m_selectedItem]);
}
UMenuItemBase* USubMenu::GetItem(int32 index) const
{
if(index >= m_items.Num() || index < 0)
{
GWWARNING(L"Item index out of range");
return nullptr;
}
return m_items[index];
}
int32 USubMenu::GetNumItems() const
{
return m_items.Num();
}
int32 USubMenu::GetSelectedItem() const
{
return m_selectedItem;
}
void USubMenu::SelectNewItemByIndex(int32 idx)
{
SelectNewItemByIndex(idx, false);
}
void USubMenu::SelectNewItemByIndex(int32 idx, bool controller)
{
if(idx >= m_items.Num() || idx < 0)
{
GWWARNING(L"Item index out of range");
return;
}
if(m_selectedItem != idx)
{
check(m_selectedItem < m_items.Num());
m_items[m_selectedItem]->NativeOnSelectionChanged(false, controller);
m_items[idx]->NativeOnSelectionChanged(true, controller);
m_selectedItem = idx;
onItemSelected.Broadcast(m_items[m_selectedItem]);
SetButtonHints();
UEffectFunctionLibrary::PlaySoundEffect2D(GetWorld(), selectionChangedAudioClass);
}
}
void USubMenu::SelectNewItem(UMenuItemBase* item)
{
int32 idx = 0;
if (m_items.Find(item, idx))
{
SelectNewItemByIndex(idx);
}
}
int32 USubMenu::GetItemIndex(UMenuItemBase* item)
{
int32 idx = 0;
if (m_items.Find(item, idx))
return idx;
return -1;
}
UMenuScreenBase* USubMenu::GetScreen() const
{
return m_screen;
}
void USubMenu::m_SetFocus(bool focus)
{
m_focus = focus;
// Get Selected button
UMenuItemBase* item = GetItem(GetSelectedItem());
if (item)
{
item->NativeOnSelectionChanged(true, false);
}
}

View File

@@ -0,0 +1,127 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/MenuItemBase.h"
#include "SubMenu.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMenuChange);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSubMenuItemSelected, UMenuItemBase*, item);
UCLASS()
class UNREALPROJECT_API UPanelUserWidget : public UUserWidget
{
GENERATED_BODY()
public:
};
UCLASS()
class UNREALPROJECT_API USubMenu : public UMenuItemBase
{
GENERATED_BODY()
public:
USubMenu(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
virtual void RescanItems();
UFUNCTION(BlueprintCallable, Category = "SubMenu")
void CloseSubMenu();
virtual void OnEnter(class UMenuScreenBase* screen);
virtual void OnLeave();
UFUNCTION(BlueprintCallable, Category = "SubMenu")
bool HasFocus() const;
virtual void OnSuspend(USubMenu* newSubMenu, bool loseFocus);
virtual void OnRestore(USubMenu* removedMenu);
// Callback that gets called whenever the button hits should update when showing this menu
UFUNCTION(BlueprintImplementableEvent, Category = "SubMenu")
void SetButtonHints();
UFUNCTION(BlueprintCallable, Category = "SubMenu")
class UButtonHintBar* GetButtonHintBar() const;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
class UCharacterSettings* GetCharacterSettings() const;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
class UPrefs* GetPrefs() const;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
class UDefaultGameInstance* GetGameInstance() const;
virtual void NativeOnMenuAction(EMenuActionBinding binding) override;
virtual void NativeOnSelectionConfirmed(UMenuItemBase* item);
UFUNCTION(BlueprintCallable, Category="SubMenu")
UMenuItemBase* GetItem(int32 itemIndex) const;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
int32 GetNumItems() const;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
int32 GetSelectedItem() const;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
void SelectNewItemByIndex(int32 idx);
UFUNCTION(BlueprintCallable, Category = "SubMenu")
void SelectNewItem(UMenuItemBase* item);
void SelectNewItemByIndex(int32 idx, bool controller);
UFUNCTION(BlueprintCallable, Category = "SubMenu")
int32 GetItemIndex(UMenuItemBase* item);
UFUNCTION(BlueprintCallable, Category = "SubMenu")
UMenuScreenBase* GetScreen() const;
// Called when an item is selected
UPROPERTY(BlueprintAssignable, Category="SubMenu")
FSubMenuItemSelected onItemSelected;
// Called when a button is pressed
UPROPERTY(BlueprintAssignable, Category="SubMenu")
FSubMenuItemSelected onItemSelectionConfirmed;
// Called when the back button is pressed
UPROPERTY(BlueprintAssignable, Category = "SubMenu")
FMenuChange onBack;
UPROPERTY(BlueprintAssignable, Category = "SubMenu")
FMenuChange onClosed;
UPROPERTY(BlueprintAssignable, Category = "SubMenu")
FMenuChange onOpened;
// Called when another menu is opened on top of this menu
UPROPERTY(BlueprintAssignable, Category = "SubMenu")
FMenuChange onSuspend;
// Called when this menu is displayed again after a menu was closed on top of this
UPROPERTY(BlueprintAssignable, Category = "SubMenu")
FMenuChange onRestore;
// The panel widget that contains all the buttons selectable in this menu
UPROPERTY(BlueprintReadWrite, Category="SubMenu")
UPanelWidget* container;
protected:
void m_ScanContainer(UWidget* widget, int32 depth = 0);
// Set the allowance of mouse focus & presses of the buttons in this menu
void m_SetFocus(bool focus);
// The list of selectable items in the menu
UPROPERTY()
TArray<UMenuItemBase*> m_items;
private:
friend class UMenuItemBase;
int32 m_selectedItem;
// Horizontal or vertical input handling
int m_layoutDirection;
UPROPERTY()
class UMenuScreenBase* m_screen;
// True if this is the topmost screen and if the buttons in this screen are allowed to be pressed.
bool m_focus;
};