HAxis sos
This commit is contained in:
22
Source/UnrealProject/GUI/CombatText/CombatTextText.cpp
Normal file
22
Source/UnrealProject/GUI/CombatText/CombatTextText.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "CombatTextText.h"
|
||||
|
||||
|
||||
|
||||
void UCombatTextText::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
lifeTime = 0;
|
||||
}
|
||||
|
||||
|
||||
void UCombatTextText::SetText_Implementation(const FString& text)
|
||||
{
|
||||
|
||||
}
|
||||
void UCombatTextText::SetColor_Implementation(const FLinearColor& color)
|
||||
{
|
||||
|
||||
}
|
||||
29
Source/UnrealProject/GUI/CombatText/CombatTextText.h
Normal file
29
Source/UnrealProject/GUI/CombatText/CombatTextText.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "CombatTextText.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UCombatTextText : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct();
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "CombatText")
|
||||
void SetText(const FString& text);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "CombatText")
|
||||
void SetColor(const FLinearColor& color);
|
||||
|
||||
|
||||
float lifeTime;
|
||||
FVector position;
|
||||
};
|
||||
106
Source/UnrealProject/GUI/CombatText/CombatTextWidget.cpp
Normal file
106
Source/UnrealProject/GUI/CombatText/CombatTextWidget.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "CombatTextWidget.h"
|
||||
#include "CombatTextText.h"
|
||||
#include "WidgetLayoutLibrary.h"
|
||||
|
||||
#define TEXT_LIFETIME 1.0f
|
||||
|
||||
void UCombatTextWidget::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
|
||||
|
||||
}
|
||||
void UCombatTextWidget::NativeDestruct()
|
||||
{
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
|
||||
void UCombatTextWidget::NativeTick(const FGeometry& geometry, float deltaTime)
|
||||
{
|
||||
Super::NativeTick(geometry, deltaTime);
|
||||
|
||||
UWorld* const world = GetWorld();
|
||||
if (!IsValid(world)) return;
|
||||
APlayerController* const controller = world->GetFirstPlayerController();
|
||||
if (!IsValid(controller)) return;
|
||||
|
||||
const FVector2D screenSize = FVector2D(world->GetGameViewport()->Viewport->GetSizeXY().X, world->GetGameViewport()->Viewport->GetSizeXY().Y);
|
||||
const float viewportScale = UWidgetLayoutLibrary::GetViewportScale(this);
|
||||
|
||||
|
||||
for (int32 i = 0; i < m_combatTexts.Num();)
|
||||
{
|
||||
// Check the lifetime
|
||||
CombatText& text = m_combatTexts[i];
|
||||
text.lifeTime += deltaTime;
|
||||
if (text.lifeTime >= TEXT_LIFETIME)
|
||||
{
|
||||
text.textObject->RemoveFromParent();
|
||||
m_combatTexts.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set the color
|
||||
UCanvasPanelSlot* asSlot = Cast<UCanvasPanelSlot>(text.textObject->Slot);
|
||||
if (asSlot)
|
||||
{
|
||||
const float progress = text.lifeTime / TEXT_LIFETIME;
|
||||
text.textObject->SetColor(FLinearColor(text.color.R, text.color.G, text.color.B, text.color.A * (1.0f - progress)));
|
||||
|
||||
// Update position
|
||||
FVector2D position;
|
||||
if (controller->ProjectWorldLocationToScreen(text.position + FVector(0, 0, progress * 200), position))
|
||||
{
|
||||
if (text.arc)
|
||||
{
|
||||
position -= FVector2D(progress * text.arcDir.X, FMath::Sin(progress * PI) * text.arcDir.Y) * 100;
|
||||
asSlot->SetPosition(position / viewportScale);
|
||||
}
|
||||
else
|
||||
asSlot->SetPosition(position / viewportScale);
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UCombatTextWidget::CreateCombatText(const FVector& position, const FString& text, const FLinearColor& color)
|
||||
{
|
||||
CombatText combatText(position, text, color, false);
|
||||
|
||||
combatText.textObject = m_CreateTextObject(text, color);
|
||||
|
||||
m_combatTexts.Add(combatText);
|
||||
}
|
||||
void UCombatTextWidget::CreateArcingCombatText(const FVector& position, const FString& text, const FLinearColor& color)
|
||||
{
|
||||
CombatText combatText(position, text, color, true);
|
||||
|
||||
combatText.arcDir.X = FMath::SRand() * 0.5f + 0.3f;
|
||||
combatText.arcDir.Y = FMath::SRand() * 0.5f + 0.3f;
|
||||
if (FMath::Rand() % 2 == 0)
|
||||
combatText.arcDir.X = -combatText.arcDir.X;
|
||||
|
||||
combatText.textObject = m_CreateTextObject(text, color);
|
||||
|
||||
m_combatTexts.Add(combatText);
|
||||
}
|
||||
|
||||
UCombatTextText* UCombatTextWidget::m_CreateTextObject(const FString& text, const FLinearColor& color)
|
||||
{
|
||||
UCombatTextText* newObject = CreateWidget<UCombatTextText>(GetWorld(), textWidget);
|
||||
canvas->AddChild(newObject);
|
||||
UCanvasPanelSlot* asSlot = Cast<UCanvasPanelSlot>(newObject->Slot);
|
||||
asSlot->SetSize(FVector2D(1000, 500));
|
||||
asSlot->SetAlignment(FVector2D(0.5f, 0.05f));
|
||||
|
||||
newObject->SetText(text);
|
||||
newObject->SetColor(color);
|
||||
|
||||
return newObject;
|
||||
}
|
||||
54
Source/UnrealProject/GUI/CombatText/CombatTextWidget.h
Normal file
54
Source/UnrealProject/GUI/CombatText/CombatTextWidget.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "CombatTextWidget.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UCombatTextWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct();
|
||||
virtual void NativeDestruct();
|
||||
virtual void NativeTick(const FGeometry& geometry, float deltaTime) override;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "CombatText")
|
||||
TSubclassOf<class UCombatTextText> textWidget;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category = "CombatText")
|
||||
class UCanvasPanel* canvas;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "CombatText")
|
||||
void CreateCombatText(const FVector& position, const FString& text, const FLinearColor& color);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "CombatText")
|
||||
void CreateArcingCombatText(const FVector& position, const FString& text, const FLinearColor& color);
|
||||
|
||||
|
||||
private:
|
||||
struct CombatText
|
||||
{
|
||||
CombatText(const FVector& position, const FString& text, const FLinearColor& color, bool arc) : position(position), text(text), color(color), lifeTime(0), arc(arc) {}
|
||||
FVector position;
|
||||
FString text;
|
||||
FLinearColor color;
|
||||
float lifeTime;
|
||||
bool arc;
|
||||
FVector2D arcDir;
|
||||
|
||||
UCombatTextText* textObject;
|
||||
};
|
||||
|
||||
TArray<CombatText> m_combatTexts;
|
||||
|
||||
UCombatTextText* m_CreateTextObject(const FString& text, const FLinearColor& color);
|
||||
};
|
||||
19
Source/UnrealProject/GUI/EventHUD.cpp
Normal file
19
Source/UnrealProject/GUI/EventHUD.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "EventHUD.h"
|
||||
|
||||
void UEventHUD::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
|
||||
void UEventHUD::NativeDestruct()
|
||||
{
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
|
||||
void UEventHUD::AddEvent_Implementation( const FEventObject& event )
|
||||
{
|
||||
|
||||
}
|
||||
47
Source/UnrealProject/GUI/EventHUD.h
Normal file
47
Source/UnrealProject/GUI/EventHUD.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "EventHUD.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EEventType : uint8
|
||||
{
|
||||
ET_Kill UMETA(DisplayName = "Kill Event"),
|
||||
ET_Capture UMETA(DisplayName = "Capture Event"),
|
||||
ET_KOTH UMETA(DisplayName = "KOTH Event")
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct FEventObject
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Event Struct")
|
||||
FText Source;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Event Struct")
|
||||
FText Target;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Event Struct")
|
||||
EEventType Type;
|
||||
|
||||
UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Event Struct" )
|
||||
int32 SourceNum;
|
||||
|
||||
UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Event Struct" )
|
||||
int32 TargetNum;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UEventHUD : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "UI")
|
||||
void AddEvent( const FEventObject& event );
|
||||
};
|
||||
19
Source/UnrealProject/GUI/EventWidget.cpp
Normal file
19
Source/UnrealProject/GUI/EventWidget.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "EventWidget.h"
|
||||
|
||||
void UEventWidget::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
|
||||
void UEventWidget::NativeDestruct()
|
||||
{
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
|
||||
void UEventWidget::SetVariables_Implementation(const FEventObject& event)
|
||||
{
|
||||
|
||||
}
|
||||
18
Source/UnrealProject/GUI/EventWidget.h
Normal file
18
Source/UnrealProject/GUI/EventWidget.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "EventHUD.h"
|
||||
#include "EventWidget.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UEventWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "UI")
|
||||
void SetVariables( const FEventObject& event );
|
||||
};
|
||||
61
Source/UnrealProject/GUI/HUD/AbilityButton.cpp
Normal file
61
Source/UnrealProject/GUI/HUD/AbilityButton.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "AbilityButton.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "AbilityState.h"
|
||||
|
||||
void UAbilityButton::NativeConstruct()
|
||||
{
|
||||
m_globalEnable = true;
|
||||
Super::NativeConstruct();
|
||||
SetCooldown(1.0f);
|
||||
SetToggled(false);
|
||||
SetupButton(nullptr, false);
|
||||
}
|
||||
|
||||
UAbilityInfo* UAbilityButton::GetAbility()
|
||||
{
|
||||
return m_abilityInfo;
|
||||
}
|
||||
|
||||
void UAbilityButton::InitButton(class UAbilityInfo* info, int32 index, bool alt)
|
||||
{
|
||||
if (info)
|
||||
{
|
||||
SetCooldown(1.0f);
|
||||
SetToggled(false);
|
||||
SetupButton(info, false);
|
||||
SetIndex(index, alt);
|
||||
SetAbilityAvailable(true);
|
||||
m_index = index;
|
||||
m_abilityInfo = info;
|
||||
}
|
||||
}
|
||||
|
||||
bool UAbilityButton::IsAssigned() const
|
||||
{
|
||||
return m_abilityInfo != nullptr;
|
||||
}
|
||||
|
||||
void UAbilityButton::UpdateState(class AAbilityState* state, ANetworkCharacter* character)
|
||||
{
|
||||
SetToggled(state->toggleState);
|
||||
SetCooldown(state->cooldownRate);
|
||||
SetAbilityAvailable(character->GetMana() >= state->info->mana);
|
||||
}
|
||||
|
||||
void UAbilityButton::SetButtonEnabled(bool enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
if (m_abilityInfo)
|
||||
{
|
||||
SetupButton(m_abilityInfo, m_enabled && m_globalEnable);
|
||||
}
|
||||
}
|
||||
void UAbilityButton::SetGlobalEnable(bool enabled)
|
||||
{
|
||||
m_globalEnable = enabled;
|
||||
SetupButton(m_abilityInfo, m_enabled && m_globalEnable);
|
||||
}
|
||||
42
Source/UnrealProject/GUI/HUD/AbilityButton.h
Normal file
42
Source/UnrealProject/GUI/HUD/AbilityButton.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "AbilityButton.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UAbilityButton : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
void NativeConstruct() override;
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Button")
|
||||
void SetupButton(class UAbilityInfo* info, bool enabled);
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Button")
|
||||
void SetCooldown(float cooldown);
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Button")
|
||||
void SetToggled(bool toggled);
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Button")
|
||||
void SetIndex(int32 index, bool alt);
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Button")
|
||||
void SetAbilityAvailable(bool available);
|
||||
|
||||
class UAbilityInfo* GetAbility();
|
||||
|
||||
void InitButton(class UAbilityInfo* info, int32 index, bool alt);
|
||||
bool IsAssigned() const;
|
||||
void UpdateState(class AAbilityState* state, class ANetworkCharacter* character);
|
||||
void SetButtonEnabled(bool enabled);
|
||||
void SetGlobalEnable(bool enabled);
|
||||
private:
|
||||
int32 m_index;
|
||||
bool m_enabled;
|
||||
bool m_globalEnable;
|
||||
class UAbilityInfo* m_abilityInfo;
|
||||
};
|
||||
160
Source/UnrealProject/GUI/HUD/ButtonBarSwitcher.cpp
Normal file
160
Source/UnrealProject/GUI/HUD/ButtonBarSwitcher.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "AbilityButton.h"
|
||||
#include "ButtonBarSwitcher.h"
|
||||
#include "BaseSkillObject.h"
|
||||
#include "AbilityState.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "IngameSkillTree.h"
|
||||
#include "AbilityInfo.h"
|
||||
|
||||
void UButtonBarSwitcher::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
|
||||
m_globalEnable = true;
|
||||
m_alternateSkillSet = false;
|
||||
m_FindAndAddButtonBar("ButtonBarMain");
|
||||
m_FindAndAddButtonBar("ButtonBarAlt");
|
||||
}
|
||||
|
||||
bool UButtonBarSwitcher::AssignSkillButton(const FIngameSkillTreeSkill& skill, bool isAlt)
|
||||
{
|
||||
if (m_mappedButtons.Find(skill.selectedEffect))
|
||||
return true;
|
||||
|
||||
int32 bar = isAlt ? 1 : 0;
|
||||
if (bar >= m_buttonBars.Num())
|
||||
return false;
|
||||
|
||||
UAbilityButton* targetButton;
|
||||
if(!m_buttonBars[bar]->AssignSkillButton(skill, targetButton, isAlt))
|
||||
{
|
||||
GERROR("Too many skills, can't assign " + skill.selectedEffect->GetName() + " to bar " + bar);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_mappedButtons.Add(skill.selectedEffect, targetButton);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UButtonBarSwitcher::UpdateCooldowns(TArray<class AAbilityState*> abilityStates, ANetworkPlayer* player)
|
||||
{
|
||||
for (int32 i = 0; i < abilityStates.Num(); i++)
|
||||
{
|
||||
UAbilityButton** button = m_mappedButtons.Find(abilityStates[i]->info);
|
||||
if (button)
|
||||
{
|
||||
(*button)->UpdateState(abilityStates[i], player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UButtonBar* UButtonBarSwitcher::m_FindAndAddButtonBar(const FName& name)
|
||||
{
|
||||
UButtonBar* bar = Cast<UButtonBar>(WidgetTree->FindWidget(name));
|
||||
if (bar)
|
||||
{
|
||||
m_buttonBars.Add(bar);
|
||||
}
|
||||
return bar;
|
||||
}
|
||||
|
||||
void UButtonBarSwitcher::SetSkillButtonEnabled(const FIngameSkillTreeSkill& skill, bool enabled)
|
||||
{
|
||||
UAbilityButton** button = m_mappedButtons.Find(skill.selectedEffect);
|
||||
if (button)
|
||||
{
|
||||
(*button)->SetButtonEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void UButtonBarSwitcher::SetGlobalEnable(bool enabled)
|
||||
{
|
||||
for(UButtonBar* bb : m_buttonBars)
|
||||
{
|
||||
bb->SetGlobalEnable(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
class UAbilityInfo* UButtonBarSwitcher::GetAbilityMapping(int32 index)
|
||||
{
|
||||
if(m_buttonBars.Num() != 2)
|
||||
{
|
||||
GERROR("m_buttonBars.Num() != 2");
|
||||
return nullptr;
|
||||
}
|
||||
UButtonBar* currentBar = m_buttonBars[m_alternateSkillSet ? 1 : 0];
|
||||
if(index < 0 || index >= currentBar->buttons.Num())
|
||||
return nullptr;
|
||||
return currentBar->buttons[index]->GetAbility();
|
||||
}
|
||||
class UAbilityInfo* UButtonBarSwitcher::GetAbilityMapping(int32 index, bool isAlt)
|
||||
{
|
||||
if(m_buttonBars.Num() != 2)
|
||||
{
|
||||
GERROR("m_buttonBars.Num() != 2");
|
||||
return nullptr;
|
||||
}
|
||||
UButtonBar* currentBar = m_buttonBars[isAlt ? 1 : 0];
|
||||
if(index < 0 || index >= currentBar->buttons.Num())
|
||||
return nullptr;
|
||||
return currentBar->buttons[index]->GetAbility();
|
||||
}
|
||||
|
||||
void UButtonBarSwitcher::TransitionToAlternateSkillSet_Implementation()
|
||||
{
|
||||
}
|
||||
void UButtonBarSwitcher::TransitionToMainSkillSet_Implementation()
|
||||
{
|
||||
}
|
||||
void UButtonBarSwitcher::ToggleSkillSet(bool useAlternateSkillSet)
|
||||
{
|
||||
if (m_alternateSkillSet != useAlternateSkillSet)
|
||||
{
|
||||
if(useAlternateSkillSet)
|
||||
TransitionToAlternateSkillSet();
|
||||
else
|
||||
TransitionToMainSkillSet();
|
||||
m_alternateSkillSet = useAlternateSkillSet;
|
||||
}
|
||||
}
|
||||
|
||||
void UButtonBar::NativeConstruct()
|
||||
{
|
||||
TArray<UWidget*> widgets;
|
||||
WidgetTree->GetAllWidgets(widgets);
|
||||
for (int32 i = 0; i < widgets.Num(); i++)
|
||||
{
|
||||
UAbilityButton* button = Cast<UAbilityButton>(widgets[i]);
|
||||
if (button)
|
||||
{
|
||||
buttons.Add(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UButtonBar::AssignSkillButton(const FIngameSkillTreeSkill& skill, class UAbilityButton*& buttonOut, bool alt)
|
||||
{
|
||||
for (int32 i = 0; i < buttons.Num(); i++)
|
||||
{
|
||||
if (buttons[i]->IsAssigned())
|
||||
continue;
|
||||
buttonOut = buttons[i];
|
||||
buttonOut->InitButton(skill.selectedEffect, i, alt);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UButtonBar::SetGlobalEnable(bool enabled)
|
||||
{
|
||||
for(UAbilityButton* button : buttons)
|
||||
{
|
||||
if(button)
|
||||
{
|
||||
button->SetGlobalEnable(enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Source/UnrealProject/GUI/HUD/ButtonBarSwitcher.h
Normal file
58
Source/UnrealProject/GUI/HUD/ButtonBarSwitcher.h
Normal file
@@ -0,0 +1,58 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "ButtonBarSwitcher.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UButtonBar : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
bool AssignSkillButton(const struct FIngameSkillTreeSkill& skill, class UAbilityButton*& buttonOut, bool alt);
|
||||
|
||||
void SetGlobalEnable(bool enabled);
|
||||
|
||||
UPROPERTY()
|
||||
TArray<class UAbilityButton*> buttons;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UButtonBarSwitcher : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
bool AssignSkillButton(const struct FIngameSkillTreeSkill& skill, bool isAlt);
|
||||
void UpdateCooldowns(TArray<class AAbilityState*> abilityStates, class ANetworkPlayer* player);
|
||||
|
||||
void SetSkillButtonEnabled(const struct FIngameSkillTreeSkill& skill, bool enabled);
|
||||
|
||||
// Set the global enabled button state
|
||||
void SetGlobalEnable(bool enabled);
|
||||
|
||||
class UAbilityInfo* GetAbilityMapping(int32 index);
|
||||
class UAbilityInfo* GetAbilityMapping(int32 index, bool isAlt);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category="Animation")
|
||||
void TransitionToAlternateSkillSet();
|
||||
UFUNCTION(BlueprintNativeEvent, Category="Animation")
|
||||
void TransitionToMainSkillSet();
|
||||
|
||||
void ToggleSkillSet(bool useAlternateSkillSet);
|
||||
|
||||
private:
|
||||
UButtonBar* m_FindAndAddButtonBar(const FName& name);
|
||||
|
||||
bool m_globalEnable;
|
||||
bool m_alternateSkillSet;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<UButtonBar*> m_buttonBars;
|
||||
UPROPERTY()
|
||||
TMap<class UAbilityInfo*, UAbilityButton*> m_mappedButtons;
|
||||
};
|
||||
126
Source/UnrealProject/GUI/HUD/HealthBar.cpp
Normal file
126
Source/UnrealProject/GUI/HUD/HealthBar.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "HealthBar.h"
|
||||
#include "StatBar.h"
|
||||
|
||||
// 1 = Crown
|
||||
// 2 = Mini-Boss
|
||||
// 3 = Dot
|
||||
// 4 = Camp
|
||||
// 5 = PlayerIcon
|
||||
static UTexture2D* barIcons[6];
|
||||
UHealthBar::UHealthBar(const FObjectInitializer& init) : Super(init)
|
||||
{
|
||||
barIcons[0] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/Minimap/T_IconCrown").Object;
|
||||
barIcons[1] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/Minimap/T_IconMini").Object;
|
||||
barIcons[2] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/Minimap/T_IconDot").Object;
|
||||
barIcons[3] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/Minimap/T_IconCamp").Object;
|
||||
barIcons[4] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/Minimap/T_IconPlayer").Object;
|
||||
}
|
||||
|
||||
void UHealthBar::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
m_currentIcon = (EMinimapIcon)-1;
|
||||
m_HP = Cast<UStatBar>(WidgetTree->FindWidget("HP"));
|
||||
m_mana = Cast<UStatBar>(WidgetTree->FindWidget("Mana"));
|
||||
m_styleSwitcher = Cast<UWidgetSwitcher>(WidgetTree->FindWidget("StyleSwitcher"));
|
||||
m_HP1 = Cast<UStatBar>(WidgetTree->FindWidget("HP1"));
|
||||
m_icon = Cast<UImage>(WidgetTree->FindWidget("Icon"));
|
||||
m_name = Cast<UTextBlock>(WidgetTree->FindWidget("Name"));
|
||||
SetName("");
|
||||
SetIcon(EMinimapIcon::None);
|
||||
if(m_mana)
|
||||
{
|
||||
m_mana->SetBarColor(EBarColor::Blue);
|
||||
}
|
||||
m_style = 0;
|
||||
m_friendly = false;
|
||||
}
|
||||
|
||||
void UHealthBar::NativeDestruct()
|
||||
{
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
|
||||
void UHealthBar::SetIcon(EMinimapIcon _iconID)
|
||||
{
|
||||
if(!m_icon)
|
||||
return;
|
||||
if(_iconID == m_currentIcon)
|
||||
return;
|
||||
int32 iconID = FMath::Clamp((int32)_iconID, 0, 5);
|
||||
if(iconID == 0)
|
||||
m_icon->SetVisibility(ESlateVisibility::Collapsed);
|
||||
else
|
||||
{
|
||||
m_icon->SetBrushFromTexture(barIcons[iconID - 1]);
|
||||
m_icon->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
|
||||
}
|
||||
}
|
||||
|
||||
void UHealthBar::SetStyle(int32 style)
|
||||
{
|
||||
if(style == m_style)
|
||||
return;
|
||||
m_style = FMath::Clamp(style, 0, 1);
|
||||
m_styleSwitcher->SetActiveWidgetIndex(m_style);
|
||||
}
|
||||
void UHealthBar::UpdateHealth(int32 curr, int32 max)
|
||||
{
|
||||
m_updateCount++;
|
||||
bool showAnimation = m_updateCount > 8;
|
||||
if(m_style == 0)
|
||||
{
|
||||
m_HP->SetStat(curr, max, showAnimation);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HP1->SetStat(curr, max, showAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
void UHealthBar::UpdateMana(int32 curr, int32 max, float blocked)
|
||||
{
|
||||
if(m_style == 0)
|
||||
{
|
||||
m_mana->SetStat(curr, max, true);
|
||||
}
|
||||
}
|
||||
|
||||
void UHealthBar::UpdateAlignment(bool friendly)
|
||||
{
|
||||
m_friendly = friendly;
|
||||
|
||||
const FLinearColor colorFriendly = FLinearColor(0.18f, 0.8f, 0.23f);
|
||||
const FLinearColor colorEnemy = FLinearColor(0.73f, 0.1f, 0.13f);
|
||||
|
||||
if(m_style == 0)
|
||||
{
|
||||
m_HP->SetBarColor(friendly ? EBarColor::Green : EBarColor::Red);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HP1->SetBarColor(friendly ? EBarColor::Green : EBarColor::Red);
|
||||
}
|
||||
}
|
||||
|
||||
void UHealthBar::SetName(FString name)
|
||||
{
|
||||
if(!m_name)
|
||||
return;
|
||||
if(name.IsEmpty())
|
||||
{
|
||||
m_name->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(name.Len() > 16)
|
||||
{
|
||||
name = name.Left(13) + "...";
|
||||
}
|
||||
m_name->SetText(FText::FromString(name));
|
||||
m_name->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
|
||||
}
|
||||
}
|
||||
53
Source/UnrealProject/GUI/HUD/HealthBar.h
Normal file
53
Source/UnrealProject/GUI/HUD/HealthBar.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "HealthBar.generated.h"
|
||||
|
||||
UENUM()
|
||||
enum class EMinimapIcon : uint8
|
||||
{
|
||||
None = 0,
|
||||
Crown,
|
||||
MiniBoss,
|
||||
Dot,
|
||||
Camp,
|
||||
Player
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UHealthBar : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UHealthBar(const FObjectInitializer& init);
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
|
||||
// Icon ID's
|
||||
// 0 = nothing
|
||||
// 1 = Crown
|
||||
// 2 = Mini-Boss
|
||||
// 3 = Dot
|
||||
// 4 = Camp
|
||||
// 5 = PlayerIcon
|
||||
void SetIcon(EMinimapIcon iconID);
|
||||
void SetStyle(int32 style);
|
||||
void UpdateHealth(int32 curr, int32 max);
|
||||
void UpdateMana(int32 curr, int32 max, float blocked = 0.0f);
|
||||
void UpdateAlignment(bool friendly);
|
||||
void SetName(FString name);
|
||||
|
||||
private:
|
||||
int32 m_style;
|
||||
bool m_friendly;
|
||||
class UWidgetSwitcher* m_styleSwitcher;
|
||||
class UStatBar* m_HP;
|
||||
class UStatBar* m_mana;
|
||||
class UStatBar* m_HP1;
|
||||
UTextBlock* m_name;
|
||||
UImage* m_icon;
|
||||
EMinimapIcon m_currentIcon;
|
||||
int32 m_updateCount = 0;
|
||||
};
|
||||
347
Source/UnrealProject/GUI/HUD/IngameHUD.cpp
Normal file
347
Source/UnrealProject/GUI/HUD/IngameHUD.cpp
Normal file
@@ -0,0 +1,347 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "DefaultPlayerController.h"
|
||||
#include "IngameHUD.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "AbilityState.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "HealthBar.h"
|
||||
#include "CreatureSpawn.h"
|
||||
#include "PlayerSlot.h"
|
||||
#include "DefaultGameState.h"
|
||||
#include "DefaultPlayerState.h"
|
||||
#include "MiniMapWidget.h"
|
||||
#include "ToolTipWidget.h"
|
||||
#include "CombatText/CombatTextWidget.h"
|
||||
#include "SpellInfoDisplay.h"
|
||||
#include "ButtonBarSwitcher.h"
|
||||
#include "BaseSkillObject.h"
|
||||
#include "WidgetLayoutLibrary.h"
|
||||
#include "NetworkGhost.h"
|
||||
#include "TouhouBoss.h"
|
||||
#include "MiniBossCreature.h"
|
||||
#include "StatBar.h"
|
||||
#include <algorithm>
|
||||
|
||||
static UClass* healthBarWidgetClass;
|
||||
static UClass* playerSlotWidgetClass;
|
||||
|
||||
UIngameHUD::UIngameHUD(const FObjectInitializer& init)
|
||||
:Super(init)
|
||||
{
|
||||
ConstructorHelpers::FClassFinder<UHealthBar> HealthBarWidgetCF(TEXT("/Game/Assets/GUI/WEEGEE_HealthBar"));
|
||||
healthBarWidgetClass = HealthBarWidgetCF.Class;
|
||||
|
||||
miniMap = nullptr;
|
||||
}
|
||||
|
||||
void UIngameHUD::NativeConstruct()
|
||||
{
|
||||
m_myTeam = 0;
|
||||
m_character = nullptr;
|
||||
Super::NativeConstruct();
|
||||
|
||||
m_spellInfoDisplay = Cast<USpellInfoDisplay>(WidgetTree->FindWidget("SpellInfoDisplay"));
|
||||
miniMap = Cast<UMiniMapWidget>(WidgetTree->FindWidget("Minimap"));
|
||||
toolTips = Cast<UToolTipWidget>(WidgetTree->FindWidget("ToolTipLayer"));
|
||||
combatText = Cast<UCombatTextWidget>(WidgetTree->FindWidget("CombatTextLayer"));
|
||||
buttonBarSwitcher = Cast<UButtonBarSwitcher>(WidgetTree->FindWidget("ButtonBarSwitcher"));
|
||||
healthBarLayer = Cast<UCanvasPanel>(WidgetTree->FindWidget("HealthBarLayer"));
|
||||
|
||||
// Find stat display items
|
||||
m_playerStatDisplay.hpBar = Cast<UStatBar>(WidgetTree->FindWidget("HP"));
|
||||
m_playerStatDisplay.manaBar = Cast<UStatBar>(WidgetTree->FindWidget("Mana"));
|
||||
m_playerStatDisplay.expBar = Cast<UExperienceBar>(WidgetTree->FindWidget("Exp"));
|
||||
m_allyStatDisplay.hpBar = Cast<UStatBar>(WidgetTree->FindWidget("HP1"));
|
||||
m_allyStatDisplay.manaBar = Cast<UStatBar>(WidgetTree->FindWidget("Mana1"));
|
||||
m_allyStatDisplay.name = Cast<UTextBlock>(WidgetTree->FindWidget("Name1"));
|
||||
m_InitStatDisplay(m_playerStatDisplay);
|
||||
m_InitStatDisplay(m_allyStatDisplay);
|
||||
|
||||
check(healthBarLayer);
|
||||
}
|
||||
void UIngameHUD::NativeDestruct()
|
||||
{
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
void UIngameHUD::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
|
||||
{
|
||||
Super::NativeTick(MyGeometry, InDeltaTime);
|
||||
auto p = m_healthBars.CreateIterator();
|
||||
while (p)
|
||||
{
|
||||
m_UpdateHealthBar(p.Key(), p.Value());
|
||||
++p;
|
||||
}
|
||||
|
||||
ADefaultPlayerState* ps = Cast<ADefaultPlayerState>(GetOwningPlayer()->PlayerState);
|
||||
if (IsValid(ps))
|
||||
{
|
||||
m_UpdateStatDisplay(ps, m_playerStatDisplay);
|
||||
m_UpdateStatDisplay(ps->teamMate, m_allyStatDisplay);
|
||||
}
|
||||
|
||||
//UWorld* world = GetWorld();
|
||||
//check(world);
|
||||
//ADefaultGameState* state = Cast<ADefaultGameState>(world->GetGameState());
|
||||
}
|
||||
|
||||
void UIngameHUD::NativeOnAssignCharacter(class ANetworkPossessable* pawn /*= nullptr*/, TArray<FIngameSkillTreeSkill> skillsetPrototype /*= TArray<FIngameSkillTreeSkill>()*/)
|
||||
{
|
||||
m_character = pawn;
|
||||
|
||||
// Cast to ghost or player
|
||||
ANetworkPlayer* player = Cast<ANetworkPlayer>(pawn);
|
||||
ANetworkGhost* ghost = Cast<ANetworkGhost>(pawn);
|
||||
if(ghost)
|
||||
{
|
||||
// Grey out buttons in ghost mode
|
||||
buttonBarSwitcher->SetGlobalEnable(false);
|
||||
}
|
||||
else if(player)
|
||||
{
|
||||
// Enable buttons back in player mode
|
||||
buttonBarSwitcher->SetGlobalEnable(true);
|
||||
|
||||
// Set skill prototypes
|
||||
if(buttonBarSwitcher)
|
||||
{
|
||||
for(int32 i = 0; i < skillsetPrototype.Num(); i++)
|
||||
{
|
||||
// Don't assign buttons for passive skills
|
||||
if(skillsetPrototype[i].selectedEffect->passive)
|
||||
continue;
|
||||
|
||||
switch(skillsetPrototype[i].abilityType)
|
||||
{
|
||||
case 0:
|
||||
buttonBarSwitcher->AssignSkillButton(skillsetPrototype[i], false);
|
||||
break;
|
||||
case 1:
|
||||
buttonBarSwitcher->AssignSkillButton(skillsetPrototype[i], true);
|
||||
break;
|
||||
case 2:
|
||||
if(skillsetPrototype[i].selectedEffect && !skillsetPrototype[i].selectedEffect->passive)
|
||||
{
|
||||
GERROR("Non-passive skill added to skill with shape type == passive, " +
|
||||
skillsetPrototype[i].skillObject->GetName() + " <= " +
|
||||
skillsetPrototype[i].selectedEffect->GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_myTeam = player->GetTeam();
|
||||
}
|
||||
|
||||
OnAssignCharacter(pawn);
|
||||
}
|
||||
|
||||
void UIngameHUD::OnLevelUp(TArray<FIngameSkillTreeSkill> updatedSkills)
|
||||
{
|
||||
if (m_spellInfoDisplay && GetPlayer())
|
||||
m_spellInfoDisplay->OnLevelUp(GetPlayer(), updatedSkills);
|
||||
|
||||
if (buttonBarSwitcher)
|
||||
{
|
||||
for (int32 i = 0; i < updatedSkills.Num(); i++)
|
||||
{
|
||||
buttonBarSwitcher->SetSkillButtonEnabled(updatedSkills[i], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UIngameHUD::UpdateCooldowns(TArray<class AAbilityState*> abilityStates, ANetworkPlayer* player)
|
||||
{
|
||||
if (buttonBarSwitcher)
|
||||
buttonBarSwitcher->UpdateCooldowns(abilityStates, player);
|
||||
}
|
||||
|
||||
void UIngameHUD::OnCharacterCreated(ANetworkCharacter* character)
|
||||
{
|
||||
if (m_healthBars.Find(character))
|
||||
{
|
||||
// When OnCharacterCreated is called twice
|
||||
GWWARNING(L"OnCharacterCreated called more thatn once");
|
||||
return;
|
||||
}
|
||||
UHealthBar* widget = CreateWidget<UHealthBar>(GetOwningPlayer(), healthBarWidgetClass);
|
||||
check(widget);
|
||||
healthBarLayer->AddChild(widget);
|
||||
m_healthBars.Add(character, widget);
|
||||
m_UpdateHealthBar(character, widget);
|
||||
}
|
||||
void UIngameHUD::OnCharacterDestroyed(ANetworkCharacter* character)
|
||||
{
|
||||
auto it = m_healthBars.Find(character);
|
||||
if (it)
|
||||
{
|
||||
(*it)->RemoveFromViewport();
|
||||
m_healthBars.Remove(character);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When OnCharacterDestroyed is called twice
|
||||
GWWARNING(L"OnCharacterDestroyed called more than once");
|
||||
}
|
||||
}
|
||||
|
||||
ANetworkPlayer* UIngameHUD::GetTeamMate() const
|
||||
{
|
||||
if (Cast<ANetworkPlayer>(m_character))
|
||||
{
|
||||
ANetworkPlayer* teamMate = Cast<ANetworkPlayer>(m_character)->GetTeamMate();
|
||||
return teamMate;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
ANetworkPlayer* UIngameHUD::GetPlayer() const
|
||||
{
|
||||
return Cast<ANetworkPlayer>(m_character);
|
||||
}
|
||||
ANetworkGhost* UIngameHUD::GetGhost() const
|
||||
{
|
||||
return Cast<ANetworkGhost>(m_character);
|
||||
}
|
||||
|
||||
class ADefaultPlayerState* UIngameHUD::GetPlayerState() const
|
||||
{
|
||||
if (!m_character)
|
||||
return nullptr;
|
||||
return Cast<ADefaultPlayerState>(m_character->PlayerState);
|
||||
}
|
||||
|
||||
void UIngameHUD::m_UpdateHealthBar(class ANetworkCharacter* character, class UHealthBar* bar)
|
||||
{
|
||||
APlayerController* pc = GetOwningPlayer();
|
||||
if (!IsValid(pc)) return;
|
||||
|
||||
FVector screenPos;
|
||||
const float height = character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
|
||||
const FVector position = character->GetActorLocation();
|
||||
const FVector worldLocation = FVector(position.X, position.Y, position.Z + height);
|
||||
|
||||
// Check if is approximately in camera view
|
||||
const FVector forward = pc->PlayerCameraManager->GetCameraRotation().RotateVector(FVector(1.0f, 0.0f, 0.0f));
|
||||
const FVector dir = (worldLocation - pc->PlayerCameraManager->GetCameraLocation()).GetSafeNormal();
|
||||
float dot = FVector::DotProduct(forward, dir);
|
||||
if (dot < 0.01f)
|
||||
{
|
||||
bar->SetVisibility(ESlateVisibility::Hidden);
|
||||
return;
|
||||
}
|
||||
bar->SetVisibility(ESlateVisibility::Visible);
|
||||
|
||||
pc->ProjectWorldLocationToScreenWithDistance(FVector(position.X, position.Y, position.Z + height), screenPos);
|
||||
const float viewportScale = UWidgetLayoutLibrary::GetViewportScale(healthBarLayer);
|
||||
|
||||
uint32 otherTeam = character->GetTeam();
|
||||
|
||||
// Check witch bar style to use
|
||||
bool useExtendedBarStyle = character->playerName.Len() > 0;
|
||||
if(useExtendedBarStyle)
|
||||
{
|
||||
bar->SetStyle(0);
|
||||
bar->UpdateMana(character->GetMana(), character->GetMaxMana(), character->GetBlockedMana());
|
||||
bar->SetName(character->playerName);
|
||||
}
|
||||
else
|
||||
{
|
||||
bar->SetStyle(1);
|
||||
bar->SetName("");
|
||||
}
|
||||
bar->UpdateAlignment(otherTeam == m_myTeam);
|
||||
bar->UpdateHealth(character->GetHealth(), character->GetMaxHealth());
|
||||
|
||||
// Set the icon next to the bar
|
||||
EMinimapIcon icon = EMinimapIcon::None;
|
||||
if(character->IsA<ATouhouBoss>())
|
||||
{
|
||||
icon = EMinimapIcon::Crown;
|
||||
}
|
||||
else if(character->IsA<AMiniBossCreature>())
|
||||
{
|
||||
icon = EMinimapIcon::MiniBoss;
|
||||
}
|
||||
bar->SetIcon(icon);
|
||||
|
||||
// Set the on-screen position
|
||||
UCanvasPanelSlot* asSlot = Cast<UCanvasPanelSlot>(bar->Slot);
|
||||
if (IsValid(asSlot))
|
||||
{
|
||||
asSlot->SetAutoSize(true);
|
||||
asSlot->SetAlignment(FVector2D(0.5f, 1));
|
||||
const FVector2D setPos = FVector2D(screenPos.X, screenPos.Y) / viewportScale;
|
||||
asSlot->SetPosition(FVector2D(FPlatformMath::RoundToFloat(setPos.X), FPlatformMath::RoundToFloat(setPos.Y)));
|
||||
}
|
||||
}
|
||||
|
||||
void UIngameHUD::m_UpdateStatDisplay(class ADefaultPlayerState* state, const FStatDisplay& sd)
|
||||
{
|
||||
ANetworkPlayer* player = state ? state->character : nullptr;
|
||||
if(sd.hpBar)
|
||||
{
|
||||
if(!player)
|
||||
sd.hpBar->SetStat(0.0f, true);
|
||||
else
|
||||
{
|
||||
sd.hpBar->SetStat(player->GetHealth(), player->GetMaxHealth(), true);
|
||||
}
|
||||
}
|
||||
if(sd.manaBar)
|
||||
{
|
||||
if(!player)
|
||||
{
|
||||
sd.manaBar->SetBlocked(0.0f);
|
||||
sd.manaBar->SetStat(0, 1, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
sd.manaBar->SetStat(player->GetMana(), player->GetMaxMana(), true);
|
||||
sd.manaBar->SetBlocked((float)player->GetBlockedMana() / (float)player->GetMaxMana());
|
||||
}
|
||||
}
|
||||
|
||||
if(sd.expBar)
|
||||
{
|
||||
if(state)
|
||||
{
|
||||
if(state->GetLevel() == state->GetMaxLevel())
|
||||
sd.expBar->Update(1.0f, state->GetMaxLevel(), true);
|
||||
else
|
||||
sd.expBar->Update((float)state->GetExperience() / (float)state->GetExperienceToLevel(), state->GetLevel(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
sd.expBar->Update(0.0f, 0, false);
|
||||
}
|
||||
}
|
||||
if(sd.name)
|
||||
{
|
||||
if(state)
|
||||
{
|
||||
state->UpdatePersona();
|
||||
sd.name->SetText(FText::FromString(state->nickname));
|
||||
}
|
||||
else
|
||||
{
|
||||
sd.name->SetText(FText());
|
||||
}
|
||||
}
|
||||
if(sd.avatar && state)
|
||||
{
|
||||
sd.avatar->SetBrushFromTexture(state->avatar);
|
||||
}
|
||||
}
|
||||
|
||||
void UIngameHUD::m_InitStatDisplay(const FStatDisplay& sd)
|
||||
{
|
||||
if(sd.hpBar)
|
||||
{
|
||||
sd.hpBar->SetBarColor(EBarColor::GreenGradient);
|
||||
}
|
||||
if(sd.manaBar)
|
||||
{
|
||||
sd.manaBar->SetBarColor(EBarColor::BlueGradient);
|
||||
}
|
||||
}
|
||||
93
Source/UnrealProject/GUI/HUD/IngameHUD.h
Normal file
93
Source/UnrealProject/GUI/HUD/IngameHUD.h
Normal file
@@ -0,0 +1,93 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "IngameSkillTree.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "IngameHUD.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UExperienceBar : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintImplementableEvent)
|
||||
void Update(float rate, int32 level, bool max);
|
||||
};
|
||||
|
||||
struct FStatDisplay
|
||||
{
|
||||
class UStatBar* hpBar;
|
||||
class UStatBar* manaBar;
|
||||
class UExperienceBar* expBar;
|
||||
class UImage* avatar;
|
||||
class UTextBlock* name;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UIngameHUD : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UIngameHUD(const FObjectInitializer& init);
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
|
||||
|
||||
// Sets the player owned character
|
||||
void NativeOnAssignCharacter(class ANetworkPossessable* pawn = nullptr, TArray<FIngameSkillTreeSkill> skillsetPrototype = TArray<FIngameSkillTreeSkill>());
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "HUD")
|
||||
void OnAssignCharacter(ANetworkPossessable* pawn);
|
||||
|
||||
// Called when the character levels up
|
||||
void OnLevelUp(TArray<FIngameSkillTreeSkill> updatedSkills);
|
||||
|
||||
// Updates the displayed cooldowns in the UI with the provided ability states
|
||||
void UpdateCooldowns(TArray<class AAbilityState*> abilityStates, class ANetworkPlayer* player);
|
||||
|
||||
// Called when a character spawns
|
||||
// this makes it display a health-bar in the HUD
|
||||
void OnCharacterCreated(class ANetworkCharacter* character);
|
||||
// Called when a character despawns(destroyed, killed)
|
||||
// this removes all the health-bars, etc. for this character
|
||||
void OnCharacterDestroyed(class ANetworkCharacter* character);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "UI")
|
||||
class ANetworkPlayer* GetTeamMate() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "UI")
|
||||
class ANetworkPlayer* GetPlayer() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "UI")
|
||||
class ANetworkGhost* GetGhost() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "UI")
|
||||
class ADefaultPlayerState* GetPlayerState() const;
|
||||
|
||||
class UMiniMapWidget* miniMap;
|
||||
class UToolTipWidget* toolTips;
|
||||
class UCombatTextWidget* combatText;
|
||||
class UButtonBarSwitcher* buttonBarSwitcher;
|
||||
class UCanvasPanel* healthBarLayer;
|
||||
|
||||
private:
|
||||
// This updates an on screen health bar for a given network character
|
||||
void m_UpdateHealthBar(class ANetworkCharacter* character, class UHealthBar* bar);
|
||||
void m_UpdateStatDisplay(class ADefaultPlayerState* player, const FStatDisplay& statDisplay);
|
||||
void m_InitStatDisplay(const FStatDisplay& statDisplay);
|
||||
|
||||
FStatDisplay m_playerStatDisplay;
|
||||
FStatDisplay m_allyStatDisplay;
|
||||
|
||||
// Maps every character in the game to a health bar widget on-screen
|
||||
UPROPERTY()
|
||||
TMap<class ANetworkCharacter*, class UHealthBar*> m_healthBars;
|
||||
|
||||
UPROPERTY()
|
||||
class ANetworkPossessable* m_character;
|
||||
UPROPERTY()
|
||||
class USpellInfoDisplay* m_spellInfoDisplay;
|
||||
|
||||
uint32 m_myTeam;
|
||||
};
|
||||
13
Source/UnrealProject/GUI/HUD/KOTHHUD.cpp
Normal file
13
Source/UnrealProject/GUI/HUD/KOTHHUD.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "KOTHHUD.h"
|
||||
|
||||
|
||||
void UKOTHHUD::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
void UKOTHHUD::Update(TArray<FKOTHTeamState> teamStates)
|
||||
{
|
||||
}
|
||||
16
Source/UnrealProject/GUI/HUD/KOTHHUD.h
Normal file
16
Source/UnrealProject/GUI/HUD/KOTHHUD.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "KOTHTeamState.h"
|
||||
#include "KOTHHUD.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UKOTHHUD : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
void NativeConstruct() override;
|
||||
void Update(TArray<FKOTHTeamState> teamStates);
|
||||
};
|
||||
14
Source/UnrealProject/GUI/HUD/KeyDisplay.cpp
Normal file
14
Source/UnrealProject/GUI/HUD/KeyDisplay.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "KeyDisplay.h"
|
||||
|
||||
void UKeyDisplay::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
|
||||
void UKeyDisplay::NativeDestruct()
|
||||
{
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
16
Source/UnrealProject/GUI/HUD/KeyDisplay.h
Normal file
16
Source/UnrealProject/GUI/HUD/KeyDisplay.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "KeyDisplay.generated.h"
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UKeyDisplay : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
};
|
||||
192
Source/UnrealProject/GUI/HUD/StatBar.cpp
Normal file
192
Source/UnrealProject/GUI/HUD/StatBar.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "StatBar.h"
|
||||
|
||||
static UTexture2D* barTextures[6];
|
||||
static FLinearColor barColors[4];
|
||||
static UMaterial* textureBarMaterialClass;
|
||||
|
||||
UStatBar::UStatBar(const FObjectInitializer& init) : Super(init)
|
||||
{
|
||||
textureBarMaterialClass = ConstructorHelpers::FObjectFinder<UMaterial>(L"/Game/Assets/Art/UI/M_HealthBar2.M_HealthBar2").Object;
|
||||
|
||||
barColors[0] = FLinearColor::Green * 0.5f;
|
||||
barColors[1] = FLinearColor::Red * 0.6f;
|
||||
barColors[2] = FLinearColor::Blue * 0.8f;
|
||||
barColors[3] = FLinearColor(1.0f, 0.0f, 1.0f, 1.0f);
|
||||
barTextures[0] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/HealthBar/UI_MainStatusBarHealth").Object;
|
||||
barTextures[1] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/HealthBar/UI_MainStatusBarMana").Object;
|
||||
|
||||
m_lastValue = 1.0f;
|
||||
m_currentValue = 1.0f;
|
||||
damageAnimationDuration = 0.3f;
|
||||
m_damageAnimation = 0.0f;
|
||||
showText = false;
|
||||
}
|
||||
|
||||
void UStatBar::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
m_base = Cast<UImage>(WidgetTree->FindWidget("Base"));
|
||||
m_blocked = Cast<UImage>(WidgetTree->FindWidget("Blocked"));
|
||||
m_damage = Cast<UImage>(WidgetTree->FindWidget("Damage"));
|
||||
m_text = Cast<UTextBlock>(WidgetTree->FindWidget("BarText"));
|
||||
if(m_damage)
|
||||
{
|
||||
m_damage->SetVisibility(ESlateVisibility::Hidden);
|
||||
UMaterialInstanceDynamic* damageMat = m_damage->GetDynamicMaterial();
|
||||
damageMat->SetScalarParameterValue("Brightness", 4.0f);
|
||||
}
|
||||
if(m_blocked)
|
||||
m_blocked->SetVisibility(ESlateVisibility::Hidden);
|
||||
}
|
||||
|
||||
#pragma optimize("", off)
|
||||
void UStatBar::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
|
||||
{
|
||||
Super::NativeTick(MyGeometry, InDeltaTime);
|
||||
|
||||
if(m_damageAnimation > 0.0f && m_damage)
|
||||
{
|
||||
float r = m_damageAnimation / damageAnimationDuration;
|
||||
float damageSize = (m_currentValue - m_lastValue);
|
||||
|
||||
UMaterialInstanceDynamic* matDamage = m_damage->GetDynamicMaterial();
|
||||
if(damageSize < 0.0f)
|
||||
{
|
||||
damageSize *= (1.0f - r);
|
||||
matDamage->SetScalarParameterValue("Start", m_currentValue);
|
||||
matDamage->SetScalarParameterValue("End", m_lastValue + damageSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
damageSize *= (1.0f-r);
|
||||
matDamage->SetScalarParameterValue("Start", m_lastValue + damageSize);
|
||||
matDamage->SetScalarParameterValue("End", m_currentValue);
|
||||
}
|
||||
|
||||
m_damageAnimation -= InDeltaTime;
|
||||
if(m_damageAnimation <= 0.0f)
|
||||
{
|
||||
m_damageAnimation = 0.0f;
|
||||
m_damage->SetVisibility(ESlateVisibility::Hidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma optimize("", off)
|
||||
void UStatBar::SetStat(float newStat, bool applyAnimation)
|
||||
{
|
||||
if(!m_base)
|
||||
return;
|
||||
if(newStat == m_currentValue)
|
||||
return;
|
||||
|
||||
float delta = newStat - m_currentValue;
|
||||
if(delta != 0 && m_damageAnimation > 0.0f)
|
||||
{
|
||||
// Append animation
|
||||
float r = m_damageAnimation / damageAnimationDuration;
|
||||
float damageSize = (m_currentValue - m_lastValue);
|
||||
if(damageSize < 0.0f)
|
||||
damageSize *= (1.0f - r);
|
||||
else
|
||||
damageSize *= (1.0f - r);
|
||||
delta += damageSize;
|
||||
m_lastValue = m_lastValue + damageSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lastValue = m_currentValue;
|
||||
}
|
||||
m_currentValue = newStat;
|
||||
|
||||
// Apply animation
|
||||
if(delta != 0.0f && applyAnimation && m_damage)
|
||||
{
|
||||
const FLinearColor deltaColor = FLinearColor(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
m_damageAnimation = damageAnimationDuration;
|
||||
|
||||
UMaterialInstanceDynamic* matDamage = m_base->GetDynamicMaterial();
|
||||
if(delta < 0.0f)
|
||||
{
|
||||
matDamage->SetScalarParameterValue("Start", newStat);
|
||||
matDamage->SetScalarParameterValue("End", m_lastValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
matDamage->SetScalarParameterValue("Start", m_lastValue);
|
||||
matDamage->SetScalarParameterValue("End", newStat);
|
||||
}
|
||||
m_damage->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
|
||||
}
|
||||
|
||||
UMaterialInstanceDynamic* matBase = m_base->GetDynamicMaterial();
|
||||
matBase->SetScalarParameterValue("Start", 0.0f);
|
||||
matBase->SetScalarParameterValue("End", newStat);
|
||||
}
|
||||
|
||||
void UStatBar::SetStat(int32 newStat, int32 max, bool applyAnimation)
|
||||
{
|
||||
if(showText && m_text)
|
||||
{
|
||||
FText t = FText::FromString(FString::Printf(L"%d / %d", newStat, max));
|
||||
m_text->SetText(t);
|
||||
m_text->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_text->SetVisibility(ESlateVisibility::Hidden);
|
||||
}
|
||||
|
||||
float r = (float)newStat / (float)max;
|
||||
SetStat(r, applyAnimation);
|
||||
}
|
||||
|
||||
void UStatBar::SetBlocked(float newBlocked)
|
||||
{
|
||||
if(!m_blocked)
|
||||
return;
|
||||
UMaterialInstanceDynamic* mat = m_blocked->GetDynamicMaterial();
|
||||
if(newBlocked > 0.0f)
|
||||
{
|
||||
mat->SetScalarParameterValue("Start", 1.0f - newBlocked);
|
||||
mat->SetScalarParameterValue("End", 1.0f);
|
||||
m_blocked->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_blocked->SetVisibility(ESlateVisibility::Hidden);
|
||||
}
|
||||
}
|
||||
|
||||
void UStatBar::SetBarColor(EBarColor _colorID)
|
||||
{
|
||||
int32 colorID = (int32)_colorID;
|
||||
if(!m_base)
|
||||
return;
|
||||
|
||||
if((colorID & 0x80) != 0)
|
||||
{
|
||||
colorID = colorID & 0x7F;
|
||||
colorID = FMath::Clamp(colorID, 0, (int32)(sizeof(barTextures) / sizeof(UTexture2D*)));
|
||||
|
||||
m_base->SetBrushFromMaterial(textureBarMaterialClass);
|
||||
m_blocked->SetBrushFromMaterial(textureBarMaterialClass);
|
||||
m_damage->SetBrushFromMaterial(textureBarMaterialClass);
|
||||
UMaterialInstanceDynamic* baseMat = m_base->GetDynamicMaterial();
|
||||
baseMat->SetTextureParameterValue("Texture", barTextures[colorID]);
|
||||
baseMat = m_damage->GetDynamicMaterial();
|
||||
baseMat->SetTextureParameterValue("Texture", barTextures[colorID]);
|
||||
baseMat = m_blocked->GetDynamicMaterial();
|
||||
baseMat->SetTextureParameterValue("Texture", barTextures[colorID]);
|
||||
}
|
||||
else
|
||||
{
|
||||
colorID = FMath::Clamp(colorID, 0, (int32)(sizeof(barColors) / sizeof(FLinearColor)));
|
||||
|
||||
UMaterialInstanceDynamic* baseMat = m_base->GetDynamicMaterial();
|
||||
baseMat->SetVectorParameterValue("Color", barColors[colorID]);
|
||||
}
|
||||
}
|
||||
49
Source/UnrealProject/GUI/HUD/StatBar.h
Normal file
49
Source/UnrealProject/GUI/HUD/StatBar.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "StatBar.generated.h"
|
||||
|
||||
enum class EBarColor
|
||||
{
|
||||
Green = 0,
|
||||
Red,
|
||||
Blue,
|
||||
Purple,
|
||||
GreenGradient = 0x80,
|
||||
BlueGradient
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UStatBar : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UStatBar(const FObjectInitializer& init);
|
||||
void NativeConstruct() override;
|
||||
void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
|
||||
|
||||
void SetStat(int32 newStat, int32 max, bool applyAnimation);
|
||||
void SetStat(float newStat, bool applyAnimation);
|
||||
void SetBlocked(float newBlocked);
|
||||
void SetBarColor(EBarColor colorID);
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Stat Bar")
|
||||
float damageAnimationDuration;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Stat Bar")
|
||||
bool showText;
|
||||
|
||||
private:
|
||||
float m_damageAnimation;
|
||||
float m_lastValue;
|
||||
float m_currentValue;
|
||||
UImage* m_base;
|
||||
UImage* m_blocked;
|
||||
UImage* m_damage;
|
||||
UTextBlock* m_text;
|
||||
};
|
||||
49
Source/UnrealProject/GUI/HUD/Timer.cpp
Normal file
49
Source/UnrealProject/GUI/HUD/Timer.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "Timer.h"
|
||||
#include "Kismet/KismetStringLibrary.h"
|
||||
|
||||
void UTimer::NativeConstruct()
|
||||
{
|
||||
m_duration = 0.0f;
|
||||
m_text = Cast<UTextBlock>(WidgetTree->FindWidget("Text"));
|
||||
m_timerText = Cast<UTextBlock>(WidgetTree->FindWidget("Time"));
|
||||
m_clock = Cast<UImage>(WidgetTree->FindWidget("Klokje"));
|
||||
if(!m_text)
|
||||
GWARNING("No \"Text\" text widget found in " + GetName());
|
||||
if(!m_text)
|
||||
GWARNING("No \"Time\" text widget found in " + GetName());
|
||||
if(!m_clock)
|
||||
GWARNING("No \"Klokje\" image widget found in " + GetName());
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
|
||||
void UTimer::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
|
||||
{
|
||||
m_time = FMath::Clamp(m_time - InDeltaTime, 0.0f, m_duration);
|
||||
if(m_duration == 0.0f)
|
||||
OnSetTimer(0.0f);
|
||||
else
|
||||
OnSetTimer(m_time / m_duration);
|
||||
|
||||
if(m_timerText)
|
||||
{
|
||||
FString msg = UKismetStringLibrary::TimeSecondsToString(m_time);
|
||||
m_timerText->SetText(FText::FromString(msg));
|
||||
}
|
||||
|
||||
Super::NativeTick(MyGeometry, InDeltaTime);
|
||||
}
|
||||
|
||||
void UTimer::SetTimer(float duration, bool reset)
|
||||
{
|
||||
m_duration = duration;
|
||||
if(reset)
|
||||
m_time = duration;
|
||||
}
|
||||
void UTimer::SetText(const FString& text)
|
||||
{
|
||||
if(m_text)
|
||||
m_text->SetText(FText::FromString(text));
|
||||
}
|
||||
44
Source/UnrealProject/GUI/HUD/Timer.h
Normal file
44
Source/UnrealProject/GUI/HUD/Timer.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Timer.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UTimer : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Calbacks handling timer changes
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "HUDTimer")
|
||||
void OnSetTimer(float rate);
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "HUDTimer")
|
||||
void OnHide(); // Play animation
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "HUDTimer")
|
||||
void OnShow(); // Play animation
|
||||
|
||||
void NativeConstruct();
|
||||
void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HUDTimer")
|
||||
void SetTimer(float duration, bool reset = true);
|
||||
UFUNCTION(BlueprintCallable, Category = "HUDTimer")
|
||||
void SetText(const FString& text);
|
||||
UFUNCTION(BlueprintCallable, Category = "HUDTimer")
|
||||
float GetTime() const
|
||||
{
|
||||
return m_time;
|
||||
}
|
||||
|
||||
private:
|
||||
float m_time;
|
||||
float m_duration;
|
||||
UImage* m_clock;
|
||||
UTextBlock* m_text;
|
||||
UTextBlock* m_timerText;
|
||||
};
|
||||
285
Source/UnrealProject/GUI/Lobby/LobbyCharacterSelect.cpp
Normal file
285
Source/UnrealProject/GUI/Lobby/LobbyCharacterSelect.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "DefaultGameInstance.h"
|
||||
#include "MenuController.h"
|
||||
#include "LobbySpawn.h"
|
||||
#include "GameStateBase.h"
|
||||
#include "PlayerStateBase.h"
|
||||
#include "PlayerControllerBase.h"
|
||||
#include "CharacterBase.h"
|
||||
#include "LobbyCharacterSelect.h"
|
||||
#include "MenuGameMode.h"
|
||||
|
||||
void ULobbyCharacterSelect::m_OnPlayerJoined(APlayerController* pc)
|
||||
{
|
||||
for(auto it = m_visualCharacters.CreateIterator(); it; ++it)
|
||||
{
|
||||
(it.Value()).character->SendInitialAppearance(pc);
|
||||
}
|
||||
}
|
||||
|
||||
void ULobbyCharacterSelect::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
|
||||
// Add GameMode callback
|
||||
AMenuGameMode* gm = Cast<AMenuGameMode>(GetWorld()->GetAuthGameMode());
|
||||
if(gm)
|
||||
{
|
||||
m_onPlayerJoinedDH = gm->onPlayerJoined.AddUObject(this, &ULobbyCharacterSelect::m_OnPlayerJoined);
|
||||
}
|
||||
|
||||
UWorld* const world = GetWorld();
|
||||
for (TActorIterator<ALobbySpawn> iter(world); iter; ++iter)
|
||||
{
|
||||
TArray<ALobbySpawn*>* find = m_spawnPoints.Find(iter->assignedTeam);
|
||||
if (!find)
|
||||
{
|
||||
TArray<ALobbySpawn*> arr;
|
||||
arr.Add(*iter);
|
||||
m_spawnPoints.Add(iter->assignedTeam, arr);
|
||||
}
|
||||
else
|
||||
find->Add(*iter);
|
||||
}
|
||||
|
||||
m_requireUpdate = false;
|
||||
m_updateInterval = 0;
|
||||
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
check(gameState);
|
||||
gameState->OnPlayerStateChange.AddDynamic(this, &ULobbyCharacterSelect::UpdateVisualCharacters);
|
||||
}
|
||||
void ULobbyCharacterSelect::NativeDestruct()
|
||||
{
|
||||
Super::NativeDestruct();
|
||||
|
||||
// Remove GameMode Callback
|
||||
AMenuGameMode* gm = Cast<AMenuGameMode>(GetWorld()->GetAuthGameMode());
|
||||
if(gm)
|
||||
{
|
||||
gm->onPlayerJoined.Remove(m_onPlayerJoinedDH);
|
||||
}
|
||||
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
check(gameState);
|
||||
gameState->OnPlayerStateChange.RemoveAll(this);
|
||||
}
|
||||
void ULobbyCharacterSelect::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
|
||||
{
|
||||
// The updating is called once per frame
|
||||
// This is to prevent double calls when two players leave in one cycle
|
||||
|
||||
// Ensure that only the server runs this
|
||||
if (!GetOwningPlayer()->HasAuthority())
|
||||
return;
|
||||
|
||||
// No need to update this frame
|
||||
if (!m_requireUpdate)
|
||||
return;
|
||||
|
||||
// Await the interval
|
||||
m_updateInterval -= InDeltaTime;
|
||||
if (m_updateInterval > 0)
|
||||
return;
|
||||
|
||||
m_requireUpdate = false;
|
||||
m_updateInterval = 0.3f;
|
||||
|
||||
UWorld* world = GetWorld();
|
||||
check(world);
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
check(gameState);
|
||||
TArray<int32> teamSizes = gameState->GetTeamSizes();
|
||||
TArray<TArray<APlayerStateBase*>> playersPerTeam = gameState->GetPlayersByTeam();
|
||||
|
||||
// Check if players have left
|
||||
TMap<APlayerStateBase*, VisualSlot> outdatedCharacters = m_visualCharacters;
|
||||
TArray<APlayerStateBase*> players = gameState->GetPlayers();
|
||||
for (int32 i = 0; i < players.Num(); i++)
|
||||
{
|
||||
VisualSlot* find = outdatedCharacters.Find(players[i]);
|
||||
if (find)
|
||||
outdatedCharacters.Remove(players[i]);
|
||||
}
|
||||
for (auto iter = outdatedCharacters.CreateIterator(); iter; ++iter)
|
||||
{
|
||||
if (IsValid(iter->Value.character))
|
||||
iter->Value.character->Destroy();
|
||||
m_visualCharacters.Remove(iter->Key);
|
||||
}
|
||||
|
||||
// Check if players switched team
|
||||
TMap<APlayerStateBase*, ACharacterBase*> newCharacters;
|
||||
TMap<APlayerStateBase*, VisualSlot> newMap;
|
||||
for (int32 i = 1; i < playersPerTeam.Num(); i++)
|
||||
{
|
||||
auto& set = playersPerTeam[i];
|
||||
for (int32 j = 0; j < set.Num(); j++)
|
||||
{
|
||||
APlayerStateBase* playerState = set[j];
|
||||
|
||||
VisualSlot* find = m_visualCharacters.Find(playerState);
|
||||
const int32 team = i - 1;
|
||||
if (!find || find->team != team || find->slot != j)
|
||||
{
|
||||
// Fetch the spawn coordinates if the spawnpoint exists
|
||||
FVector position;
|
||||
FRotator rotation;
|
||||
if (team < m_spawnPoints.Num() && j < m_spawnPoints[team].Num())
|
||||
{
|
||||
position = m_spawnPoints[team][j]->GetActorLocation();
|
||||
rotation = m_spawnPoints[team][j]->GetActorRotation();
|
||||
}
|
||||
|
||||
// Check if we need to create a new character, else we just repurpose the previous character (like when the player switched slot)
|
||||
ACharacterBase* character;
|
||||
if (!find || !IsValid(find->character))
|
||||
{
|
||||
FActorSpawnParameters params;
|
||||
params.bNoFail = true;
|
||||
params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
character = GetWorld()->SpawnActor<ACharacterBase>(characterBlueprint, FTransform::Identity, params);
|
||||
if (!IsValid(character))
|
||||
{
|
||||
JERROR("Failed to spawn Actor");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
find->character->ClearEquipment();
|
||||
character = find->character;
|
||||
}
|
||||
|
||||
// Set and add
|
||||
character->SetActorLocation(position);
|
||||
character->SetActorRotation(rotation.Quaternion());
|
||||
newMap.Add(playerState, VisualSlot(team, j, character));
|
||||
newCharacters.Add(playerState, character);
|
||||
}
|
||||
else
|
||||
newMap.Add(playerState, *find);
|
||||
}
|
||||
}
|
||||
m_visualCharacters = newMap;
|
||||
|
||||
// Apply the customizations
|
||||
TMap<APlayerControllerBase*, APlayerStateBase*> controllers = gameState->GetPlayersByController();
|
||||
for (auto iter = controllers.CreateIterator(); iter; ++iter)
|
||||
{
|
||||
ACharacterBase** character = newCharacters.Find(iter->Value);
|
||||
if (!character || !IsValid(*character)) continue;
|
||||
(*character)->SetCustomizations(iter->Key->setupState.customizations);
|
||||
(*character)->EquipSkillsFromSkillTreeState(iter->Key->setupState.skills);
|
||||
|
||||
// Set class specific equipment
|
||||
if(characterClassProperties)
|
||||
{
|
||||
FCharacterClassProperty properties = characterClassProperties->GetCharacterClass(iter->Key->setupState.characterClass);
|
||||
(*character)->EquipItems(properties.classItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ULobbyCharacterSelect::Init()
|
||||
{
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
UDefaultGameInstance* const instance = Cast<UDefaultGameInstance>(world->GetGameInstance());
|
||||
check(instance);
|
||||
UCharacterSettings* const settings = instance->GetCharacterSettings();
|
||||
check(settings);
|
||||
|
||||
// Fetch the characters from the save
|
||||
check(settings->characterSaves.Num() > 0);
|
||||
m_playerCharacterSaves = settings->GetValidCharacters();
|
||||
|
||||
m_selection = 0;
|
||||
|
||||
m_ApplyCharacter();
|
||||
}
|
||||
|
||||
void ULobbyCharacterSelect::OnNextCharacter()
|
||||
{
|
||||
if (m_selection == m_playerCharacterSaves.Num() - 1)
|
||||
return;
|
||||
m_selection++;
|
||||
|
||||
m_ApplyCharacter();
|
||||
}
|
||||
void ULobbyCharacterSelect::OnPreviousCharacter()
|
||||
{
|
||||
if (m_selection == 0)
|
||||
return;
|
||||
m_selection--;
|
||||
|
||||
m_ApplyCharacter();
|
||||
}
|
||||
|
||||
void ULobbyCharacterSelect::UpdateCharacter_Implementation(FCharacterSave save)
|
||||
{
|
||||
// Blueprint implementation
|
||||
}
|
||||
|
||||
void ULobbyCharacterSelect::m_ApplyCharacter()
|
||||
{
|
||||
UpdateCharacter(m_playerCharacterSaves[m_selection]);
|
||||
|
||||
// Apply the selected character to the player controller
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
AMenuController* const controller = Cast<AMenuController>(world->GetFirstPlayerController());
|
||||
check(controller);
|
||||
|
||||
FPlayerSetupState state;
|
||||
state.skills = m_playerCharacterSaves[m_selection].skillTreeState;
|
||||
state.customizations = m_playerCharacterSaves[m_selection].characterCustomization;
|
||||
state.characterClass = m_playerCharacterSaves[m_selection].characterClass;
|
||||
controller->SetSetupState(state);
|
||||
}
|
||||
|
||||
void ULobbyCharacterSelect::UpdateVisualCharacters(APlayerControllerBase* playerController)
|
||||
{
|
||||
if (!IsValid(characterBlueprint))
|
||||
{
|
||||
JERROR("Character blueprint not assigned");
|
||||
return;
|
||||
}
|
||||
if (!IsValid(playerController) && playerController != nullptr)
|
||||
return;
|
||||
|
||||
// Ensure that only the server runs this
|
||||
if (!GetOwningPlayer()->HasAuthority())
|
||||
return;
|
||||
|
||||
if (playerController != nullptr)
|
||||
{
|
||||
// Just update the visuals on the specific player
|
||||
APlayerStateBase* playerState = Cast<APlayerStateBase>(playerController->PlayerState);
|
||||
if (IsValid(playerState))
|
||||
{
|
||||
VisualSlot* slot = m_visualCharacters.Find(playerState);
|
||||
if (slot && IsValid(slot->character))
|
||||
{
|
||||
slot->character->SetCustomizations(playerController->setupState.customizations);
|
||||
slot->character->ClearEquipment();
|
||||
slot->character->EquipSkillsFromSkillTreeState(playerController->setupState.skills);
|
||||
|
||||
// Set class specific equipment
|
||||
if(characterClassProperties)
|
||||
{
|
||||
FCharacterClassProperty properties = characterClassProperties->GetCharacterClass(playerController->setupState.characterClass);
|
||||
slot->character->EquipItems(properties.classItems);
|
||||
}
|
||||
}
|
||||
else // Failed to find the character, refresh all
|
||||
m_requireUpdate = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_requireUpdate = true;
|
||||
}
|
||||
65
Source/UnrealProject/GUI/Lobby/LobbyCharacterSelect.h
Normal file
65
Source/UnrealProject/GUI/Lobby/LobbyCharacterSelect.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GUI/Menu/SubMenu.h"
|
||||
#include "CharacterSettings.h"
|
||||
#include "LobbyCharacterSelect.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ULobbyCharacterSelect : public USubMenu
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
void Init();
|
||||
|
||||
// On next/previous player character
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
void OnNextCharacter();
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
void OnPreviousCharacter();
|
||||
|
||||
// Called to set the name of the selected character
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Lobby")
|
||||
void UpdateCharacter(FCharacterSave save);
|
||||
|
||||
// Called to update all visual characters in the lobby (specify a controller if one player only requires a visual update, or nullptr if all characters need to be updated)
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
void UpdateVisualCharacters(class APlayerControllerBase* playerController);
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Lobby")
|
||||
TSubclassOf<class ACharacterBase> characterBlueprint;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Character Sub Menu")
|
||||
class UCharacterClassPropertySet* characterClassProperties;
|
||||
|
||||
private:
|
||||
void m_OnPlayerJoined(APlayerController* pc);
|
||||
FDelegateHandle m_onPlayerJoinedDH;
|
||||
|
||||
TArray<FCharacterSave> m_playerCharacterSaves;
|
||||
int32 m_selection;
|
||||
|
||||
TMap<int32, TArray<ALobbySpawn*>> m_spawnPoints;
|
||||
struct VisualSlot
|
||||
{
|
||||
VisualSlot(int32 team, int32 slot, ACharacterBase* character) : team(team), slot(slot), character(character) {}
|
||||
int32 team;
|
||||
int32 slot;
|
||||
ACharacterBase* character;
|
||||
};
|
||||
TMap<APlayerStateBase*, VisualSlot> m_visualCharacters;
|
||||
|
||||
void m_ApplyCharacter();
|
||||
bool m_requireUpdate;
|
||||
float m_updateInterval;
|
||||
};
|
||||
204
Source/UnrealProject/GUI/Lobby/LobbyMenu.cpp
Normal file
204
Source/UnrealProject/GUI/Lobby/LobbyMenu.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "LobbyMenu.h"
|
||||
#include "LobbySlot.h"
|
||||
#include "DefaultPlayer.h"
|
||||
#include "PlayerStateBase.h"
|
||||
#include "MenuController.h"
|
||||
#include "DefaultGameInstance.h"
|
||||
#include "MenuGameMode.h"
|
||||
#include "GameStateBase.h"
|
||||
#include "SubMenu.h"
|
||||
#include "SessionManager.h"
|
||||
#include "Engine.h"
|
||||
#include "ScreenOverlay.h"
|
||||
#include "CharacterSettings.h"
|
||||
|
||||
ULobbyMenu::ULobbyMenu(const FObjectInitializer& init)
|
||||
: Super(init)
|
||||
{
|
||||
}
|
||||
|
||||
void ULobbyMenu::NativeConstruct()
|
||||
{
|
||||
m_teamContainer = Cast<USubMenu>(WidgetTree->FindWidget("Container"));
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
void ULobbyMenu::NativeDestruct()
|
||||
{
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
void ULobbyMenu::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
|
||||
{
|
||||
Super::NativeTick(MyGeometry, InDeltaTime);
|
||||
|
||||
if(!m_myState)
|
||||
{
|
||||
// Try to aquire my local player state, this is in Tick because player states are not guaranteed during BeginPlay
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
|
||||
check(player);
|
||||
m_myState = Cast<APlayerStateBase>(player->PlayerState);
|
||||
}
|
||||
|
||||
UWorld* world = GetWorld();
|
||||
check(world);
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
check(gameState);
|
||||
TArray<int32> teamSizes = gameState->GetTeamSizes();
|
||||
TArray<TArray<APlayerStateBase*>> playersPerTeam = gameState->GetPlayersByTeam();
|
||||
|
||||
// Update team slots with their respective player arrays
|
||||
for (int32 i = 0; i < (int32)m_numTeams; i++)
|
||||
{
|
||||
if((i + 1) < playersPerTeam.Num())
|
||||
{
|
||||
m_lobbySlots[i]->UpdatePlayers(playersPerTeam[i+1]);
|
||||
}
|
||||
else
|
||||
m_lobbySlots[i]->UpdatePlayers(TArray<APlayerStateBase*>());
|
||||
|
||||
if (i > teamSizes.Num())
|
||||
{
|
||||
m_lobbySlots[i]->SetState(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lobbySlots[i]->SetState(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
void ULobbyMenu::Init()
|
||||
{
|
||||
// Generate the selectable builds
|
||||
UWorld* world = GetWorld();
|
||||
check(world);
|
||||
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(world->GetGameInstance());
|
||||
check(instance);
|
||||
UCharacterSettings* settings = instance->GetCharacterSettings();
|
||||
check(settings);
|
||||
m_characters = settings->characterSaves;
|
||||
|
||||
|
||||
// Remove existing lobby slots
|
||||
for(ULobbySlot* s : m_lobbySlots)
|
||||
{
|
||||
s->RemoveFromParent();
|
||||
}
|
||||
m_lobbySlots.SetNum(0);
|
||||
|
||||
if(!m_teamContainer)
|
||||
{
|
||||
GWERROR("Container not set, can't initialize lobby menu");
|
||||
return;
|
||||
}
|
||||
|
||||
UPanelWidget* panel = Cast<UPanelWidget>(m_teamContainer->WidgetTree->RootWidget);
|
||||
if (!panel)
|
||||
{
|
||||
GWERROR(L"No UPanelWidget found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create team slots in this menu for the amount of selected teams in the game setup screen
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
check(gameState);
|
||||
m_numTeams = gameState->GetMapTeamCount();
|
||||
for (uint32 i = 0; i < m_numTeams; i++)
|
||||
{
|
||||
ULobbySlot* slot = CreateWidget<ULobbySlot>(GetWorld(), lobbySlotClass);
|
||||
check(slot);
|
||||
|
||||
panel->AddChild(slot);
|
||||
slot->SetTeamIndex(i+1);
|
||||
m_lobbySlots.Add(slot);
|
||||
}
|
||||
|
||||
m_teamContainer->RescanItems();
|
||||
}
|
||||
|
||||
void ULobbyMenu::ToggleReadyState()
|
||||
{
|
||||
// Toggle my player state's ready state
|
||||
if(!m_myState)
|
||||
{
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
|
||||
check(player);
|
||||
m_myState = Cast<APlayerStateBase>(player->PlayerState);
|
||||
}
|
||||
|
||||
if(m_myState)
|
||||
m_myState->SetReadyState(!m_myState->GetReadyState());
|
||||
}
|
||||
void ULobbyMenu::StartGame()
|
||||
{
|
||||
UWorld* world = GetWorld();
|
||||
check(world);
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
check(gameState);
|
||||
TArray<APlayerStateBase*> players = gameState->GetPlayers<APlayerStateBase>();
|
||||
|
||||
// Check if we're the host
|
||||
AMenuGameMode* gameMode = world->GetAuthGameMode<AMenuGameMode>();
|
||||
if(gameMode)
|
||||
{
|
||||
// Check if everyone's ready
|
||||
for (size_t i = 0; i < players.Num(); i++)
|
||||
{
|
||||
if (!players[i]->GetReadyState())
|
||||
return;
|
||||
}
|
||||
|
||||
// Tell the game to start
|
||||
GPRINT("Starting game");
|
||||
if(!gameMode->StartGame())
|
||||
{
|
||||
GERROR("Failed to start game");
|
||||
TArray<FString> options;
|
||||
options.Add("OK");
|
||||
Cast<APlayerControllerBase>(GetOwningPlayer())->overlay->ShowMessageBox("Error", "Failed to start game", options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ULobbyMenu::GotoTeamSelector()
|
||||
{
|
||||
if (m_teamContainer)
|
||||
{
|
||||
OpenSubMenu(m_teamContainer);
|
||||
}
|
||||
}
|
||||
void ULobbyMenu::BackToMenu()
|
||||
{
|
||||
// Close active session
|
||||
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetOwningPlayer()->GetGameInstance());
|
||||
inst->sessionManager->DestroySession();
|
||||
inst->sessionManager->CloseNetConnections();
|
||||
|
||||
// Goto menu
|
||||
AMenuController* mc = Cast<AMenuController>(GetOwningPlayer());
|
||||
mc->SwitchToMenu();
|
||||
}
|
||||
bool ULobbyMenu::IsHost() const
|
||||
{
|
||||
if (!GetOwningPlayer())
|
||||
return false;
|
||||
return GetOwningPlayer()->Role == ROLE_Authority;
|
||||
}
|
||||
|
||||
void ULobbyMenu::OnLobbyEnter()
|
||||
{
|
||||
// Register session callbacks
|
||||
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetOwningPlayer()->GetGameInstance());
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
TArray<FCharacterSave> ULobbyMenu::GetCharacterSaves()
|
||||
{
|
||||
return m_characters;
|
||||
}
|
||||
63
Source/UnrealProject/GUI/Lobby/LobbyMenu.h
Normal file
63
Source/UnrealProject/GUI/Lobby/LobbyMenu.h
Normal file
@@ -0,0 +1,63 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
#include "MenuScreenBase.h"
|
||||
#include "CharacterSettings.h"
|
||||
#include "LobbyMenu.generated.h"
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ULobbyMenu : public UMenuScreenBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
|
||||
|
||||
// Must be called from blueprint to setup this element
|
||||
void Init();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Ready")
|
||||
void ToggleReadyState();
|
||||
UFUNCTION(BlueprintCallable, Category = "Ready")
|
||||
void StartGame();
|
||||
UFUNCTION(BlueprintCallable, Category = "Ready")
|
||||
void GotoTeamSelector();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Game")
|
||||
void BackToMenu();
|
||||
UFUNCTION(BlueprintCallable, Category = "Game")
|
||||
bool IsHost() const;
|
||||
|
||||
// Called when the local player enters the lobby menu & state
|
||||
void OnLobbyEnter();
|
||||
|
||||
// When this menu needs to be hidden
|
||||
UFUNCTION(BlueprintImplementableEvent)
|
||||
void OnShow();
|
||||
// When this menu needs to be shown
|
||||
UFUNCTION(BlueprintImplementableEvent)
|
||||
void OnHide();
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Lobby")
|
||||
TSubclassOf<class ULobbySlot> lobbySlotClass;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
TArray<FCharacterSave> GetCharacterSaves();
|
||||
|
||||
private:
|
||||
ULobbyMenu(const FObjectInitializer& init);
|
||||
|
||||
UPROPERTY()
|
||||
class APlayerStateBase* m_myState;
|
||||
UPROPERTY()
|
||||
TArray<class ULobbySlot*> m_lobbySlots;
|
||||
UPROPERTY()
|
||||
class USubMenu* m_teamContainer;
|
||||
|
||||
uint32 m_numTeams;
|
||||
|
||||
TArray<FCharacterSave> m_characters;
|
||||
};
|
||||
4
Source/UnrealProject/GUI/Lobby/LobbyPlayerSlot.cpp
Normal file
4
Source/UnrealProject/GUI/Lobby/LobbyPlayerSlot.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "LobbyPlayerSlot.h"
|
||||
16
Source/UnrealProject/GUI/Lobby/LobbyPlayerSlot.h
Normal file
16
Source/UnrealProject/GUI/Lobby/LobbyPlayerSlot.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GUI/PlayerSlot.h"
|
||||
#include "PlayerSlot.h"
|
||||
#include "LobbyPlayerSlot.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ULobbyPlayerSlot : public UPlayerSlot
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
125
Source/UnrealProject/GUI/Lobby/LobbySlot.cpp
Normal file
125
Source/UnrealProject/GUI/Lobby/LobbySlot.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "LobbySlot.h"
|
||||
#include "LobbyPlayerSlot.h"
|
||||
#include "PlayerStateBase.h"
|
||||
#include "PlayerControllerBase.h"
|
||||
|
||||
#include "SubMenu.h"
|
||||
#include "MenuScreenBase.h"
|
||||
#include "GameStateBase.h"
|
||||
|
||||
ULobbySlot::ULobbySlot(const FObjectInitializer& init)
|
||||
: Super(init)
|
||||
{
|
||||
}
|
||||
|
||||
void ULobbySlot::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
m_open = false;
|
||||
}
|
||||
void ULobbySlot::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
|
||||
{
|
||||
Super::NativeTick(MyGeometry, InDeltaTime);
|
||||
this->bIsEnabled = m_open;
|
||||
}
|
||||
|
||||
void ULobbySlot::SetTeamIndex(uint32 idx)
|
||||
{
|
||||
m_teamIndex = idx;
|
||||
m_UpdateTeamTitle();
|
||||
}
|
||||
void ULobbySlot::m_UpdateTeamTitle()
|
||||
{
|
||||
// Set the title of this team slot
|
||||
if (m_teamName)
|
||||
{
|
||||
std::wstring teamName = std::wstring() + L"Team " + m_teamIndex + L" (" + m_players.Num() + L"/2)";
|
||||
SetButtonText(FString(teamName.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
void ULobbySlot::UpdatePlayers(TArray<APlayerStateBase*> players)
|
||||
{
|
||||
check(m_playerList);
|
||||
check(m_playerSlots.Num() == 2);
|
||||
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
|
||||
check(player);
|
||||
APlayerStateBase* myState = Cast<APlayerStateBase>(player->PlayerState);
|
||||
|
||||
// Set all the player slots in this team to show their respective player / no player placeholder
|
||||
m_players = players;
|
||||
for (int32 i = 0; i < 2; i++)
|
||||
{
|
||||
if (i < m_players.Num())
|
||||
{
|
||||
APlayerStateBase* player = players[i];
|
||||
m_playerSlots[i]->Init(player, player == myState);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_playerSlots[i]->Init(nullptr);
|
||||
}
|
||||
}
|
||||
m_UpdateTeamTitle();
|
||||
}
|
||||
bool ULobbySlot::OnSlotSelected()
|
||||
{
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
|
||||
check(player);
|
||||
APlayerStateBase* myState = Cast<APlayerStateBase>(player->PlayerState);
|
||||
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
|
||||
if (myState->GetReadyState())
|
||||
return false;
|
||||
|
||||
if (gameState)
|
||||
{
|
||||
auto pbt = gameState->GetPlayersByTeam();
|
||||
if (m_teamIndex < (uint32)pbt.Num() && pbt[m_teamIndex].Num() == 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Try for the local player to join this team
|
||||
if (myState) // Check the state cause it can be null
|
||||
myState->RequestTeamEntry(m_teamIndex);
|
||||
else
|
||||
GWERROR(L"Player not set in " + GetName());
|
||||
|
||||
// Make sure this item is now selected
|
||||
GetSubMenu()->SelectNewItem(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ULobbySlot::Init(UTextBlock* teamName, UVerticalBox* playerList)
|
||||
{
|
||||
m_teamName = teamName;
|
||||
m_playerList = playerList;
|
||||
|
||||
// Add fixed 2 player slots to this team
|
||||
m_playerList->ClearChildren();
|
||||
for (int32 i = 0; i < 2; i++)
|
||||
{
|
||||
ULobbyPlayerSlot* playerSlot = CreateWidget<ULobbyPlayerSlot>(GetWorld(), lobbyPlayerSlotClass);
|
||||
check(playerSlot);
|
||||
playerSlot->Init(nullptr);
|
||||
m_playerSlots.Add(playerSlot);
|
||||
m_playerList->AddChild(playerSlot);
|
||||
}
|
||||
}
|
||||
|
||||
void ULobbySlot::SetState(bool open)
|
||||
{
|
||||
m_open = open;
|
||||
}
|
||||
55
Source/UnrealProject/GUI/Lobby/LobbySlot.h
Normal file
55
Source/UnrealProject/GUI/Lobby/LobbySlot.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "MenuButton.h"
|
||||
#include <set>
|
||||
#include "LobbySlot.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ULobbySlot : public UMenuButton
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
|
||||
|
||||
void SetTeamIndex(uint32 idx);
|
||||
|
||||
// Must be called from blueprint to setup this element
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
void Init(UTextBlock* teamName, UVerticalBox* playerList);
|
||||
|
||||
void UpdatePlayers(TArray<class APlayerStateBase*> players);
|
||||
void SetState(bool open);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
bool OnSlotSelected();
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Lobby")
|
||||
UFont* font;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category="Lobby")
|
||||
TSubclassOf<class ULobbyPlayerSlot> lobbyPlayerSlotClass;
|
||||
|
||||
private:
|
||||
ULobbySlot(const FObjectInitializer& init);
|
||||
|
||||
void m_UpdateTeamTitle();
|
||||
|
||||
UPROPERTY()
|
||||
UTextBlock* m_teamName;
|
||||
UPROPERTY()
|
||||
UVerticalBox* m_playerList;
|
||||
UPROPERTY()
|
||||
TArray<class ULobbyPlayerSlot*> m_playerSlots;
|
||||
|
||||
UPROPERTY()
|
||||
UButton* m_joinButton;
|
||||
uint32 m_teamIndex;
|
||||
UPROPERTY()
|
||||
TArray<class APlayerStateBase*> m_players;
|
||||
bool m_open;
|
||||
};
|
||||
4
Source/UnrealProject/GUI/Menu/ButtonHintBar.cpp
Normal file
4
Source/UnrealProject/GUI/Menu/ButtonHintBar.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "ButtonHintBar.h"
|
||||
13
Source/UnrealProject/GUI/Menu/ButtonHintBar.h
Normal file
13
Source/UnrealProject/GUI/Menu/ButtonHintBar.h
Normal 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:
|
||||
};
|
||||
246
Source/UnrealProject/GUI/Menu/GameList.cpp
Normal file
246
Source/UnrealProject/GUI/Menu/GameList.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
75
Source/UnrealProject/GUI/Menu/GameList.h
Normal file
75
Source/UnrealProject/GUI/Menu/GameList.h
Normal 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;
|
||||
};
|
||||
53
Source/UnrealProject/GUI/Menu/GameListItem.cpp
Normal file
53
Source/UnrealProject/GUI/Menu/GameListItem.cpp
Normal 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();
|
||||
}
|
||||
41
Source/UnrealProject/GUI/Menu/GameListItem.h
Normal file
41
Source/UnrealProject/GUI/Menu/GameListItem.h
Normal 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;
|
||||
};
|
||||
103
Source/UnrealProject/GUI/Menu/GraphicsMenu.cpp
Normal file
103
Source/UnrealProject/GUI/Menu/GraphicsMenu.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
32
Source/UnrealProject/GUI/Menu/GraphicsMenu.h
Normal file
32
Source/UnrealProject/GUI/Menu/GraphicsMenu.h
Normal 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;
|
||||
};
|
||||
161
Source/UnrealProject/GUI/Menu/MapSelectionScreen.cpp
Normal file
161
Source/UnrealProject/GUI/Menu/MapSelectionScreen.cpp
Normal 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>";
|
||||
}
|
||||
40
Source/UnrealProject/GUI/Menu/MapSelectionScreen.h
Normal file
40
Source/UnrealProject/GUI/Menu/MapSelectionScreen.h
Normal 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;
|
||||
};
|
||||
14
Source/UnrealProject/GUI/Menu/MapSlot.cpp
Normal file
14
Source/UnrealProject/GUI/Menu/MapSlot.cpp
Normal 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()
|
||||
{
|
||||
}
|
||||
26
Source/UnrealProject/GUI/Menu/MapSlot.h
Normal file
26
Source/UnrealProject/GUI/Menu/MapSlot.h
Normal 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;
|
||||
};
|
||||
32
Source/UnrealProject/GUI/Menu/MenuAction.h
Normal file
32
Source/UnrealProject/GUI/Menu/MenuAction.h
Normal 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
|
||||
};
|
||||
147
Source/UnrealProject/GUI/Menu/MenuButton.cpp
Normal file
147
Source/UnrealProject/GUI/Menu/MenuButton.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Source/UnrealProject/GUI/Menu/MenuButton.h
Normal file
48
Source/UnrealProject/GUI/Menu/MenuButton.h
Normal 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;
|
||||
};
|
||||
9
Source/UnrealProject/GUI/Menu/MenuEnum.h
Normal file
9
Source/UnrealProject/GUI/Menu/MenuEnum.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class MenuButtonID : uint8
|
||||
{
|
||||
OptionsGeneral,
|
||||
OptionsGraphics,
|
||||
Options,
|
||||
};
|
||||
92
Source/UnrealProject/GUI/Menu/MenuItemBase.cpp
Normal file
92
Source/UnrealProject/GUI/Menu/MenuItemBase.cpp
Normal 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();
|
||||
}
|
||||
83
Source/UnrealProject/GUI/Menu/MenuItemBase.h
Normal file
83
Source/UnrealProject/GUI/Menu/MenuItemBase.h
Normal 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;
|
||||
};
|
||||
337
Source/UnrealProject/GUI/Menu/MenuScreen.cpp
Normal file
337
Source/UnrealProject/GUI/Menu/MenuScreen.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
135
Source/UnrealProject/GUI/Menu/MenuScreen.h
Normal file
135
Source/UnrealProject/GUI/Menu/MenuScreen.h
Normal 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:
|
||||
};
|
||||
122
Source/UnrealProject/GUI/Menu/MenuScreenBase.cpp
Normal file
122
Source/UnrealProject/GUI/Menu/MenuScreenBase.cpp
Normal 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);
|
||||
}
|
||||
52
Source/UnrealProject/GUI/Menu/MenuScreenBase.h
Normal file
52
Source/UnrealProject/GUI/Menu/MenuScreenBase.h
Normal 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;
|
||||
};
|
||||
139
Source/UnrealProject/GUI/Menu/MenuSlider.cpp
Normal file
139
Source/UnrealProject/GUI/Menu/MenuSlider.cpp
Normal 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);
|
||||
}
|
||||
61
Source/UnrealProject/GUI/Menu/MenuSlider.h
Normal file
61
Source/UnrealProject/GUI/Menu/MenuSlider.h
Normal 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;
|
||||
|
||||
};
|
||||
78
Source/UnrealProject/GUI/Menu/OverlayMessageBox.cpp
Normal file
78
Source/UnrealProject/GUI/Menu/OverlayMessageBox.cpp
Normal 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);
|
||||
}
|
||||
46
Source/UnrealProject/GUI/Menu/OverlayMessageBox.h
Normal file
46
Source/UnrealProject/GUI/Menu/OverlayMessageBox.h
Normal 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;
|
||||
};
|
||||
24
Source/UnrealProject/GUI/Menu/OverlayProgressBar.cpp
Normal file
24
Source/UnrealProject/GUI/Menu/OverlayProgressBar.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
23
Source/UnrealProject/GUI/Menu/OverlayProgressBar.h
Normal file
23
Source/UnrealProject/GUI/Menu/OverlayProgressBar.h
Normal 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;
|
||||
};
|
||||
78
Source/UnrealProject/GUI/Menu/ScoreBoard.cpp
Normal file
78
Source/UnrealProject/GUI/Menu/ScoreBoard.cpp
Normal 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()));
|
||||
}
|
||||
}
|
||||
34
Source/UnrealProject/GUI/Menu/ScoreBoard.h
Normal file
34
Source/UnrealProject/GUI/Menu/ScoreBoard.h
Normal 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;
|
||||
};
|
||||
4
Source/UnrealProject/GUI/Menu/ScoreBoardSlot.cpp
Normal file
4
Source/UnrealProject/GUI/Menu/ScoreBoardSlot.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "ScoreBoardSlot.h"
|
||||
19
Source/UnrealProject/GUI/Menu/ScoreBoardSlot.h
Normal file
19
Source/UnrealProject/GUI/Menu/ScoreBoardSlot.h
Normal 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);
|
||||
};
|
||||
195
Source/UnrealProject/GUI/Menu/ScreenOverlay.cpp
Normal file
195
Source/UnrealProject/GUI/Menu/ScreenOverlay.cpp
Normal 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";
|
||||
}
|
||||
104
Source/UnrealProject/GUI/Menu/ScreenOverlay.h
Normal file
104
Source/UnrealProject/GUI/Menu/ScreenOverlay.h
Normal 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;
|
||||
|
||||
};
|
||||
10
Source/UnrealProject/GUI/Menu/SelectButton.cpp
Normal file
10
Source/UnrealProject/GUI/Menu/SelectButton.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "SelectButton.h"
|
||||
|
||||
|
||||
USelectButton::USelectButton(const FObjectInitializer& init) : UMenuButton(init)
|
||||
{
|
||||
selected = 0;
|
||||
}
|
||||
22
Source/UnrealProject/GUI/Menu/SelectButton.h
Normal file
22
Source/UnrealProject/GUI/Menu/SelectButton.h
Normal 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;
|
||||
|
||||
};
|
||||
130
Source/UnrealProject/GUI/Menu/SkillSelector.cpp
Normal file
130
Source/UnrealProject/GUI/Menu/SkillSelector.cpp
Normal 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;
|
||||
}
|
||||
54
Source/UnrealProject/GUI/Menu/SkillSelector.h
Normal file
54
Source/UnrealProject/GUI/Menu/SkillSelector.h
Normal 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;
|
||||
};
|
||||
76
Source/UnrealProject/GUI/Menu/SkillSelectorItem.cpp
Normal file
76
Source/UnrealProject/GUI/Menu/SkillSelectorItem.cpp
Normal 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
|
||||
}
|
||||
}
|
||||
50
Source/UnrealProject/GUI/Menu/SkillSelectorItem.h
Normal file
50
Source/UnrealProject/GUI/Menu/SkillSelectorItem.h
Normal 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;
|
||||
};
|
||||
96
Source/UnrealProject/GUI/Menu/SplashScreen.cpp
Normal file
96
Source/UnrealProject/GUI/Menu/SplashScreen.cpp
Normal 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;
|
||||
}
|
||||
45
Source/UnrealProject/GUI/Menu/SplashScreen.h
Normal file
45
Source/UnrealProject/GUI/Menu/SplashScreen.h
Normal 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;
|
||||
};
|
||||
104
Source/UnrealProject/GUI/Menu/SplashScreenItem.cpp
Normal file
104
Source/UnrealProject/GUI/Menu/SplashScreenItem.cpp
Normal 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);
|
||||
}
|
||||
52
Source/UnrealProject/GUI/Menu/SplashScreenItem.h
Normal file
52
Source/UnrealProject/GUI/Menu/SplashScreenItem.h
Normal 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;
|
||||
};
|
||||
10
Source/UnrealProject/GUI/Menu/StartPromptScreen.cpp
Normal file
10
Source/UnrealProject/GUI/Menu/StartPromptScreen.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "StartPromptScreen.h"
|
||||
|
||||
void UStartPromptScreen::Close()
|
||||
{
|
||||
onClosed.Broadcast();
|
||||
OnClose();
|
||||
}
|
||||
26
Source/UnrealProject/GUI/Menu/StartPromptScreen.h
Normal file
26
Source/UnrealProject/GUI/Menu/StartPromptScreen.h
Normal 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;
|
||||
};
|
||||
319
Source/UnrealProject/GUI/Menu/SubMenu.cpp
Normal file
319
Source/UnrealProject/GUI/Menu/SubMenu.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
127
Source/UnrealProject/GUI/Menu/SubMenu.h
Normal file
127
Source/UnrealProject/GUI/Menu/SubMenu.h
Normal 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;
|
||||
};
|
||||
20
Source/UnrealProject/GUI/Minimap/MiniMap.cpp
Normal file
20
Source/UnrealProject/GUI/Minimap/MiniMap.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "UnrealProject.h"
|
||||
#include "MiniMap.h"
|
||||
|
||||
|
||||
namespace MiniMap
|
||||
{
|
||||
void NodeBase::CircleOverlap(const FVector2D& position, float radius_sqr, TArray<MinimapHandle*>& out_objects)
|
||||
{
|
||||
if (objects.empty())
|
||||
return;
|
||||
|
||||
for (auto iter = objects.begin(); iter != objects.end(); iter++)
|
||||
{
|
||||
MinimapHandle& obj = **iter;
|
||||
if (FVector2D::DistSquared(obj.position, position) <= radius_sqr)
|
||||
out_objects.Add(&obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
325
Source/UnrealProject/GUI/Minimap/MiniMap.h
Normal file
325
Source/UnrealProject/GUI/Minimap/MiniMap.h
Normal file
@@ -0,0 +1,325 @@
|
||||
#pragma once
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include <unordered_set>
|
||||
using namespace std;
|
||||
|
||||
|
||||
class ACharacterBase;
|
||||
namespace MiniMap
|
||||
{
|
||||
struct Rect
|
||||
{
|
||||
Rect() = default;
|
||||
Rect(float x, float y, float width, float height) : x(x), y(y), width(width), height(height) {}
|
||||
Rect(const FVector2D& position, const FVector2D& size) : x(position.X), y(position.Y), width(size.X), height(size.Y) {}
|
||||
float x;
|
||||
float y;
|
||||
float width;
|
||||
float height;
|
||||
|
||||
inline FVector2D position() const
|
||||
{
|
||||
return FVector2D(x, y);
|
||||
}
|
||||
inline FVector2D size() const
|
||||
{
|
||||
return FVector2D(width, height);
|
||||
}
|
||||
inline bool contains(const FVector2D& pos) const
|
||||
{
|
||||
return pos.X >= x && pos.X <= (x + width) && pos.Y >= y && pos.Y <= (y + height);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline Rect GetSubArea(const Rect& area, size_t idx)
|
||||
{
|
||||
const FVector2D half_size = area.size() / 2;
|
||||
const FVector2D position = area.position();
|
||||
switch (idx)
|
||||
{
|
||||
case 0: return Rect(position, half_size);
|
||||
case 1: return Rect(position + FVector2D(half_size.X, 0), half_size);
|
||||
case 2: return Rect(position + half_size, half_size);
|
||||
case 3: return Rect(position + FVector2D(0, half_size.Y), half_size);
|
||||
}
|
||||
return area;
|
||||
}
|
||||
inline bool RectCircleOverlapSquared(const Rect& area, const FVector2D& position, float radius_sqr)
|
||||
{
|
||||
const FVector2D pos = area.position();
|
||||
const float left = area.x;
|
||||
const float bottom = area.y;
|
||||
const float right = area.x + area.width;
|
||||
const float top = area.y + area.height;
|
||||
|
||||
const float x = position.X > right ? right : position.X < left ? left : position.X;
|
||||
const float y = position.Y > top ? top : position.Y < bottom ? bottom : position.Y;
|
||||
|
||||
return FVector2D::DistSquared(position, FVector2D(x, y)) <= radius_sqr;
|
||||
}
|
||||
|
||||
class NonCopyable
|
||||
{
|
||||
public:
|
||||
NonCopyable() = default;
|
||||
NonCopyable(const NonCopyable& other) = delete;
|
||||
NonCopyable& operator=(const NonCopyable& other) = delete;
|
||||
};
|
||||
|
||||
class MinimapHandle;
|
||||
class NodeBase : private NonCopyable
|
||||
{
|
||||
protected:
|
||||
NodeBase(const Rect& area, NodeBase* parent) : area(area), parent(parent) {}
|
||||
|
||||
public:
|
||||
virtual void AddObject(MinimapHandle& obj) = 0;
|
||||
virtual void RemoveObject(MinimapHandle& obj) = 0;
|
||||
virtual void ClearRecursive()
|
||||
{
|
||||
objects.clear();
|
||||
}
|
||||
virtual void ResizeRecursive(const Rect& area)
|
||||
{
|
||||
this->area = area;
|
||||
}
|
||||
|
||||
virtual void CircleOverlap(const FVector2D& position, float radius_sqr, TArray<MinimapHandle*>& out_objects);
|
||||
|
||||
unordered_set<MinimapHandle*> objects;
|
||||
Rect area;
|
||||
NodeBase* parent;
|
||||
};
|
||||
|
||||
class MinimapHandle : private NonCopyable
|
||||
{
|
||||
public:
|
||||
MinimapHandle() : character(nullptr), node(nullptr), root(nullptr) {}
|
||||
MinimapHandle(const FVector2D& position) : character(nullptr), position(position), node(nullptr), root(nullptr) {}
|
||||
~MinimapHandle()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FVector2D position;
|
||||
class ::ACharacterBase* character;
|
||||
NodeBase* node;
|
||||
NodeBase* root;
|
||||
};
|
||||
|
||||
template<size_t level> class TreeNode : public NodeBase
|
||||
{
|
||||
public:
|
||||
TreeNode(const Rect& area, NodeBase* parent) : NodeBase(area, parent), child0(GetSubArea(area, 0), this), child1(GetSubArea(area, 1), this), child2(GetSubArea(area, 2), this), child3(GetSubArea(area, 3), this) {}
|
||||
|
||||
typedef TreeNode<level - 1> child_type;
|
||||
child_type child0;
|
||||
child_type child1;
|
||||
child_type child2;
|
||||
child_type child3;
|
||||
unordered_set<MinimapHandle*> child_objects;
|
||||
|
||||
virtual void AddObject(MinimapHandle& obj) override
|
||||
{
|
||||
child_type* children = &child0;
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
{
|
||||
if (children[i].area.contains(obj.position))
|
||||
{
|
||||
// Register to child
|
||||
child_objects.emplace(&obj);
|
||||
children[i].AddObject(obj);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Register to this node
|
||||
objects.emplace(&obj);
|
||||
obj.node = this;
|
||||
}
|
||||
virtual void RemoveObject(MinimapHandle& obj) override
|
||||
{
|
||||
// Check if present in children
|
||||
auto find_child = child_objects.find(&obj);
|
||||
if (find_child != child_objects.end())
|
||||
{
|
||||
child_type* children = &child0;
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
children[i].RemoveObject(obj);
|
||||
|
||||
child_objects.erase(find_child);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove from node
|
||||
auto find = objects.find(&obj);
|
||||
if (find != objects.end())
|
||||
objects.erase(find);
|
||||
}
|
||||
}
|
||||
virtual void ClearRecursive() override
|
||||
{
|
||||
child_type* children = &child0;
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
children[i].ClearRecursive();
|
||||
|
||||
NodeBase::ClearRecursive();
|
||||
child_objects.clear();
|
||||
}
|
||||
virtual void ResizeRecursive(const Rect& area) override
|
||||
{
|
||||
child_type* children = &child0;
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
children[i].ResizeRecursive(GetSubArea(area, i));
|
||||
|
||||
NodeBase::ResizeRecursive(area);
|
||||
}
|
||||
|
||||
virtual void CircleOverlap(const FVector2D& position, float radius_sqr, TArray<MinimapHandle*>& out_objects) override
|
||||
{
|
||||
if (!child_objects.empty())
|
||||
{
|
||||
child_type* children = &child0;
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
{
|
||||
child_type& child = children[i];
|
||||
if (RectCircleOverlapSquared(child.area, position, radius_sqr))
|
||||
child.CircleOverlap(position, radius_sqr, out_objects);
|
||||
}
|
||||
}
|
||||
|
||||
NodeBase::CircleOverlap(position, radius_sqr, out_objects);
|
||||
}
|
||||
};
|
||||
template<size_t level> class RootNode : public TreeNode<level>
|
||||
{
|
||||
public:
|
||||
RootNode(const Rect& area, NodeBase* parent) : TreeNode<level>(area, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual void AddObject(MinimapHandle& obj) override
|
||||
{
|
||||
TreeNode<level>::AddObject(obj);
|
||||
all_objects.emplace(&obj);
|
||||
}
|
||||
virtual void RemoveObject(MinimapHandle& obj) override
|
||||
{
|
||||
TreeNode<level>::RemoveObject(obj);
|
||||
auto find = all_objects.find(&obj);
|
||||
if (find != all_objects.end())
|
||||
all_objects.erase(find);
|
||||
}
|
||||
virtual void ClearRecursive() override
|
||||
{
|
||||
for (auto iter = all_objects.begin(); iter != all_objects.end(); iter++)
|
||||
{
|
||||
(*iter)->node = nullptr;
|
||||
(*iter)->root = nullptr;
|
||||
}
|
||||
all_objects.clear();
|
||||
|
||||
TreeNode<level>::ClearRecursive();
|
||||
}
|
||||
|
||||
void Reparent(MinimapHandle& obj)
|
||||
{
|
||||
TreeNode<level>::RemoveObject(obj);
|
||||
TreeNode<level>::AddObject(obj);
|
||||
}
|
||||
|
||||
unordered_set<MinimapHandle*> all_objects;
|
||||
};
|
||||
template<> class TreeNode<0> : public NodeBase
|
||||
{
|
||||
public:
|
||||
TreeNode(const Rect& area, NodeBase* parent) : NodeBase(area, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AddObject(MinimapHandle& obj) override
|
||||
{
|
||||
objects.emplace(&obj);
|
||||
obj.node = this;
|
||||
}
|
||||
void RemoveObject(MinimapHandle& obj) override
|
||||
{
|
||||
auto find = objects.find(&obj);
|
||||
if (find != objects.end())
|
||||
objects.erase(find);
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t depth> class QuadTree : private NonCopyable
|
||||
{
|
||||
public:
|
||||
QuadTree() : m_root_node(Rect(0, 0, 1, 1), nullptr) {}
|
||||
QuadTree(const Rect& area) : m_root_node(area, nullptr) {}
|
||||
~QuadTree()
|
||||
{
|
||||
m_root_node.ClearRecursive();
|
||||
}
|
||||
|
||||
void AddObject(MinimapHandle& obj)
|
||||
{
|
||||
if (obj.root == &m_root_node)
|
||||
return;
|
||||
if (obj.root != nullptr)
|
||||
obj.root->RemoveObject(obj);
|
||||
|
||||
// Add object to root
|
||||
m_root_node.AddObject(obj);
|
||||
obj.root = &m_root_node;
|
||||
}
|
||||
void RemoveObject(MinimapHandle& obj)
|
||||
{
|
||||
if (obj.root == nullptr)
|
||||
return;
|
||||
if (obj.root != &m_root_node)
|
||||
return;
|
||||
|
||||
// Remove from root
|
||||
m_root_node.RemoveObject(obj);
|
||||
obj.node = nullptr;
|
||||
obj.root = nullptr;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Update positioning
|
||||
for (auto iter = m_root_node.all_objects.begin(); iter != m_root_node.all_objects.end(); iter++)
|
||||
{
|
||||
MinimapHandle& obj = **iter;
|
||||
|
||||
if (!obj.node->area.contains(obj.position) && obj.node->parent)
|
||||
m_root_node.Reparent(obj);
|
||||
}
|
||||
}
|
||||
void Clear()
|
||||
{
|
||||
m_root_node->ClearRecursive();
|
||||
}
|
||||
void Resize(const Rect& area)
|
||||
{
|
||||
m_root_node.ResizeRecursive(area);
|
||||
}
|
||||
|
||||
int32 Num() const
|
||||
{
|
||||
return int32(m_root_node.all_objects.size());
|
||||
}
|
||||
|
||||
bool CircleOverlap(const FVector2D& position, float radius, TArray<MinimapHandle*>& out_objects)
|
||||
{
|
||||
out_objects.SetNum(0);
|
||||
m_root_node.CircleOverlap(position, radius * radius, out_objects);
|
||||
return out_objects.Num() > 0;
|
||||
}
|
||||
|
||||
RootNode<depth> m_root_node;
|
||||
};
|
||||
}
|
||||
10
Source/UnrealProject/GUI/Minimap/MiniMapIconWidget.cpp
Normal file
10
Source/UnrealProject/GUI/Minimap/MiniMapIconWidget.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "MiniMapIconWidget.h"
|
||||
|
||||
|
||||
void UMiniMapIconWidget::SetObjectIcon_Implementation(UTexture2D* texture)
|
||||
{
|
||||
// No implementation
|
||||
}
|
||||
22
Source/UnrealProject/GUI/Minimap/MiniMapIconWidget.h
Normal file
22
Source/UnrealProject/GUI/Minimap/MiniMapIconWidget.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "MiniMapIconWidget.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UMiniMapIconWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Image")
|
||||
UImage* image;
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "MiniMap")
|
||||
void SetObjectIcon(class UTexture2D* texture);
|
||||
};
|
||||
15
Source/UnrealProject/GUI/Minimap/MiniMapVisionCircle.cpp
Normal file
15
Source/UnrealProject/GUI/Minimap/MiniMapVisionCircle.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "MiniMapVisionCircle.h"
|
||||
|
||||
|
||||
UMiniMapVisionCircle::UMiniMapVisionCircle(const FObjectInitializer& init) : Super(init)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UMiniMapVisionCircle::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
26
Source/UnrealProject/GUI/Minimap/MiniMapVisionCircle.h
Normal file
26
Source/UnrealProject/GUI/Minimap/MiniMapVisionCircle.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "MiniMapVisionCircle.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UMiniMapVisionCircle : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UMiniMapVisionCircle(const FObjectInitializer& init);
|
||||
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Vision")
|
||||
void SetTexture(UTexture2D* texture);
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Vision")
|
||||
void SetUV(const FVector2D& uv, const FVector2D& scale);
|
||||
};
|
||||
393
Source/UnrealProject/GUI/Minimap/MiniMapWidget.cpp
Normal file
393
Source/UnrealProject/GUI/Minimap/MiniMapWidget.cpp
Normal file
@@ -0,0 +1,393 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "MiniMapVolume.h"
|
||||
#include "MiniMapIconWidget.h"
|
||||
#include "MiniMapVisionCircle.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "BossBase.h"
|
||||
#include "NPCBase.h"
|
||||
#include "KOTHBossSpawner.h"
|
||||
#include "KOTHMinionSpawner.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "DefaultGameState.h"
|
||||
#include "DefaultPlayerState.h"
|
||||
#include "DefaultPlayerController.h"
|
||||
#include "MiniMapWidget.h"
|
||||
#include "WidgetLayoutLibrary.h"
|
||||
|
||||
#if PLATFORM_SPECIFIC_WIN == 0
|
||||
#include "HeatMapMetrics.h"
|
||||
#endif
|
||||
|
||||
|
||||
float AngleTowards(const FVector2D& from, const FVector2D& to)
|
||||
{
|
||||
const float angle_rad = FMath::Atan2(from.X - to.X, to.Y - from.Y) + PI;
|
||||
return FMath::RadiansToDegrees(angle_rad);
|
||||
}
|
||||
|
||||
UMiniMapWidget::UMiniMapWidget(const FObjectInitializer& init) : Super(init)
|
||||
{
|
||||
viewRadius = 2000;
|
||||
}
|
||||
|
||||
void UMiniMapWidget::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
|
||||
// Find the minimap volume
|
||||
ADefaultGameState* const gameState = Cast<ADefaultGameState>(GetWorld()->GetGameState());
|
||||
if(IsValid(gameState))
|
||||
{
|
||||
for (TActorIterator<AMiniMapVolume> iter(GetWorld()); iter; ++iter)
|
||||
{
|
||||
AMiniMapVolume* const volume = *iter;
|
||||
volume->SetActorRotation(FRotator(0, 0, 0));
|
||||
|
||||
const FVector pos = volume->GetActorLocation();
|
||||
const FVector area = volume->area->GetScaledBoxExtent();
|
||||
m_size = FMath::Max(area.X, area.Y);
|
||||
m_min.X = -m_size + pos.X;
|
||||
m_min.Y = -m_size + pos.Y;
|
||||
m_max.X = m_size + pos.X;
|
||||
m_max.Y = m_size + pos.Y;
|
||||
m_size *= 2;
|
||||
|
||||
m_viewRadius = (viewRadius / (area.X * 2));
|
||||
|
||||
gameState->minimapQuadtree.Resize(MiniMap::Rect(FVector2D(m_min), FVector2D(m_size, m_size)));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JERROR("Failed to locate Mini Map volume");
|
||||
}
|
||||
|
||||
void UMiniMapWidget::NativeTick(const FGeometry& geometry, float deltaTime)
|
||||
{
|
||||
Super::NativeTick(geometry, deltaTime);
|
||||
|
||||
m_arrowAnim = FMath::Fmod(m_arrowAnim + deltaTime, 1.0f);
|
||||
|
||||
// Reset the state of all current widgets
|
||||
{
|
||||
// Set widgets hidden
|
||||
for (int32 i = 0; i < m_miniMapIconWidgetPool.Num(); i++)
|
||||
{
|
||||
m_miniMapIconWidgetPool[i]->SetVisibility(ESlateVisibility::Hidden);
|
||||
// Reset rotations
|
||||
FWidgetTransform trans = m_miniMapIconWidgetPool[i]->image->RenderTransform;
|
||||
trans.Angle = 0;
|
||||
m_miniMapIconWidgetPool[i]->image->SetRenderTransform(trans);
|
||||
}
|
||||
|
||||
// Set existing arrows hidden
|
||||
for (int32 i = 0; i < m_arrowMapIconWidgetPool.Num(); i++)
|
||||
m_arrowMapIconWidgetPool[i]->SetVisibility(ESlateVisibility::Hidden);
|
||||
|
||||
// Allocate the neccesairy vision circles
|
||||
for (int32 i = 0; i < m_viewCircleWidgetPool.Num(); i++)
|
||||
m_viewCircleWidgetPool[i]->SetVisibility(ESlateVisibility::Hidden);
|
||||
}
|
||||
|
||||
// Begin the rendering
|
||||
UWorld* const world = GetWorld();
|
||||
if (!IsValid(world)) return;
|
||||
ADefaultGameState* const gameState = Cast<ADefaultGameState>(world->GetGameState());
|
||||
if (!IsValid(gameState))
|
||||
return;
|
||||
|
||||
// Get the local team
|
||||
m_localTeam = -1;
|
||||
AController* controller = world->GetGameInstance()->GetFirstLocalPlayerController();
|
||||
ACharacterBase* localPlayer = nullptr;
|
||||
if (IsValid(controller))
|
||||
{
|
||||
ADefaultPlayerController* const playerController = Cast<ADefaultPlayerController>(controller);
|
||||
localPlayer = Cast<ACharacterBase>(playerController->GetPawn());
|
||||
if (playerController->PlayerState)
|
||||
m_localTeam = Cast<ADefaultPlayerState>(playerController->PlayerState)->GetTeam();
|
||||
}
|
||||
|
||||
// Failed to fetch local team
|
||||
if (m_localTeam == -1)
|
||||
return;
|
||||
|
||||
gameState->minimapQuadtree.Update();
|
||||
|
||||
map<int32, unordered_set<ACharacterBase*>> drawActors;
|
||||
unordered_set<ACharacterBase*>& friendlyPlayerSet = drawActors.emplace(m_localTeam, unordered_set<ACharacterBase*>()).first->second;
|
||||
|
||||
// Fetch the friendly viewpoints
|
||||
for (TActorIterator<ACharacterBase> iter(world); iter; ++iter)
|
||||
{
|
||||
ACharacterBase* const character = *iter;
|
||||
if (IsValid(character))
|
||||
{
|
||||
if (character->GetTeam() == m_localTeam)
|
||||
friendlyPlayerSet.emplace(character);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all the visible units around the friendly viewpoints
|
||||
for (auto iter = friendlyPlayerSet.begin(); iter != friendlyPlayerSet.end(); iter++)
|
||||
{
|
||||
ACharacterBase* const character = (*iter);
|
||||
FVector2D position = FVector2D(character->GetActorLocation().X, character->GetActorLocation().Y);
|
||||
if (gameState->minimapQuadtree.CircleOverlap(position, character->visionRadius, m_handleBuffer))
|
||||
{
|
||||
for (int32 i = 0; i < m_handleBuffer.Num(); i++)
|
||||
{
|
||||
ACharacterBase* const visibleCharacter = m_handleBuffer[i]->character;
|
||||
if(IsValid(visibleCharacter))
|
||||
drawActors[visibleCharacter->GetTeam()].emplace(visibleCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the minion camps
|
||||
TArray<AKOTHSpawnerBase*> minionCamps;
|
||||
AKOTHBossSpawner* bossCamp = nullptr;
|
||||
FVector2D bossCampPos;
|
||||
for (TActorIterator<AKOTHSpawnerBase> iter(world); iter; ++iter)
|
||||
{
|
||||
AKOTHSpawnerBase* camp = *iter;
|
||||
if (camp->IsA<AKOTHBossSpawner>())
|
||||
{
|
||||
bossCamp = Cast<AKOTHBossSpawner>(camp);
|
||||
const FVector2D charPos = FVector2D(camp->GetActorLocation().X, camp->GetActorLocation().Y);
|
||||
bossCampPos = ConvertWorldToMinimap(charPos);
|
||||
}
|
||||
minionCamps.Add(camp);
|
||||
}
|
||||
|
||||
// Draw the minion camps
|
||||
m_widgetIndex = 0;
|
||||
m_arrowIndex = 0;
|
||||
m_circleIndex = 0;
|
||||
for (int32 i = 0; i < minionCamps.Num(); i++)
|
||||
{
|
||||
UMiniMapIconWidget* const widget = m_AllocateWidget();
|
||||
UCanvasPanelSlot* const slot = Cast<UCanvasPanelSlot>(widget->Slot);
|
||||
widget->SetVisibility(ESlateVisibility::Visible);
|
||||
|
||||
AKOTHSpawnerBase* const camp = minionCamps[i];
|
||||
const FVector2D charPos = FVector2D(camp->GetActorLocation().X, camp->GetActorLocation().Y);
|
||||
FVector2D relativePos = ConvertWorldToMinimap(charPos);
|
||||
|
||||
// Set sprite position
|
||||
FVector2D spriteSize = geometry.GetLocalSize() * 0.07f;
|
||||
|
||||
slot->SetPosition(geometry.GetLocalSize() * relativePos - spriteSize * 0.5f);
|
||||
slot->SetSize(spriteSize);
|
||||
SetObjectIcon(campIcon, widget);
|
||||
|
||||
FLinearColor color = enemyColor;
|
||||
if (camp->team >= NPCTeam::Team1) // NPC Controlled
|
||||
color = playerColor;
|
||||
else if (int32(camp->team) == int32(m_localTeam)) // Friendly
|
||||
color = allyColor;
|
||||
else if (int32(camp->team) == 0) // Contested
|
||||
color = neutralColor;
|
||||
widget->image->Brush.TintColor = color;
|
||||
|
||||
// Draw arrows
|
||||
if (bossCamp && int32(camp->team) > 0 && camp->team < NPCTeam::Team1 && camp != bossCamp)
|
||||
{
|
||||
const int32 arrowCount = int32(FVector2D::Distance(relativePos, bossCampPos) * 25);
|
||||
if (arrowCount > 0)
|
||||
{
|
||||
const float step = 1.0f / float(arrowCount);
|
||||
for (int32 i = 0; i < arrowCount; i++)
|
||||
{
|
||||
UMiniMapIconWidget* arrow = m_AllocateArrow();
|
||||
arrow->SetVisibility(ESlateVisibility::Visible);
|
||||
UCanvasPanelSlot* const slot = Cast<UCanvasPanelSlot>(arrow->Slot);
|
||||
|
||||
// Lerp towards the target
|
||||
color.A = arrowCount > 1 ? (i == 0 ? m_arrowAnim : i == arrowCount - 1 ? (1.f - m_arrowAnim) : 1) : 1;
|
||||
const FVector2D setPos = FMath::Lerp(relativePos, bossCampPos, step * float(i) + step * m_arrowAnim);
|
||||
FWidgetTransform trans = arrow->image->RenderTransform;
|
||||
trans.Angle = AngleTowards(setPos, bossCampPos);
|
||||
arrow->image->SetRenderTransform(trans);
|
||||
arrow->image->Brush.TintColor = color;
|
||||
|
||||
const FVector2D spriteSize = geometry.GetLocalSize() * 0.04f;
|
||||
slot->SetSize(spriteSize);
|
||||
slot->SetPosition(geometry.GetLocalSize() * setPos - spriteSize * 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate draw order (Draw local team last)
|
||||
TArray<int32> teamDrawOrder;
|
||||
for (auto iter = drawActors.rbegin(); iter != drawActors.rend(); iter++)
|
||||
{
|
||||
if (iter->first == m_localTeam)
|
||||
continue;
|
||||
teamDrawOrder.Add(iter->first);
|
||||
}
|
||||
teamDrawOrder.Add(m_localTeam);
|
||||
|
||||
// Draw the actual characters to the minimap
|
||||
int32 circleIndex = 0;
|
||||
for (size_t i = 0; i < teamDrawOrder.Num(); i++)
|
||||
{
|
||||
const int32 team = teamDrawOrder[i];
|
||||
auto& set = drawActors[team];
|
||||
for (auto charIter = set.begin(); charIter != set.end(); charIter++)
|
||||
{
|
||||
ACharacterBase* const character = *charIter;
|
||||
if (character == localPlayer)
|
||||
continue;
|
||||
|
||||
UMiniMapIconWidget* const widget = m_AllocateWidget();
|
||||
m_DrawCreature(geometry, widget, character);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the local player last
|
||||
if (localPlayer)
|
||||
m_DrawCreature(geometry, m_AllocateWidget(), localPlayer);
|
||||
}
|
||||
|
||||
void UMiniMapWidget::m_DrawCreature(const FGeometry& geometry, UMiniMapIconWidget* widget, class ACharacterBase* character)
|
||||
{
|
||||
widget->SetVisibility(ESlateVisibility::Visible);
|
||||
UCanvasPanelSlot* const slot = Cast<UCanvasPanelSlot>(widget->Slot);
|
||||
|
||||
const int32 actorTeam = character->GetTeam();
|
||||
const FVector2D charPos = FVector2D(character->GetActorLocation().X, character->GetActorLocation().Y);
|
||||
FVector2D relativePos = ConvertWorldToMinimap(charPos);
|
||||
|
||||
// Rotation
|
||||
const float drawPlayerAngle = character->GetActorRotation().Yaw;
|
||||
|
||||
// Set sprite position
|
||||
FVector2D spriteSize = geometry.GetLocalSize() * 0.07f;
|
||||
|
||||
slot->SetPosition(geometry.GetLocalSize() * relativePos - spriteSize * 0.5f);
|
||||
slot->SetSize(spriteSize);
|
||||
|
||||
FWidgetTransform trans = widget->image->RenderTransform;
|
||||
trans.Angle = 0;
|
||||
|
||||
// Set icon based on the object type
|
||||
if (character->IsA<ABossBase>())
|
||||
SetObjectIcon(bossIcon, widget);
|
||||
else if (character->IsA<ANPCBase>())
|
||||
SetObjectIcon(creatureIcon, widget);
|
||||
else
|
||||
{
|
||||
trans.Angle = drawPlayerAngle;
|
||||
SetObjectIcon(playerIcon, widget);
|
||||
}
|
||||
widget->image->SetRenderTransform(trans);
|
||||
|
||||
// Set the color
|
||||
if (!(character->IsA<ANetworkPlayer>() && character->IsLocallyControlled()))
|
||||
{
|
||||
if (actorTeam != m_localTeam)
|
||||
{
|
||||
// Neutral or enemy?
|
||||
if (actorTeam == 0)
|
||||
widget->image->Brush.TintColor = neutralColor;
|
||||
else
|
||||
widget->image->Brush.TintColor = enemyColor;
|
||||
}
|
||||
else
|
||||
widget->image->Brush.TintColor = allyColor;
|
||||
}
|
||||
else
|
||||
widget->image->Brush.TintColor = playerColor;
|
||||
|
||||
// Update the view circle
|
||||
if (actorTeam == m_localTeam)
|
||||
{
|
||||
UMiniMapVisionCircle* const circle = m_AllocateVisionCircle();
|
||||
circle->SetVisibility(ESlateVisibility::Visible);
|
||||
|
||||
UCanvasPanelSlot* const circleSlot = Cast<UCanvasPanelSlot>(circle->Slot);
|
||||
if (IsValid(circleSlot))
|
||||
{
|
||||
const float diameter = (character->visionRadius * 2) / m_size;
|
||||
FVector2D size = geometry.GetLocalSize() * diameter;
|
||||
size = FVector2D(FMath::RoundToFloat(size.X), FMath::RoundToFloat(size.Y));
|
||||
FVector2D pos = geometry.GetLocalSize() * relativePos - size * 0.5f;
|
||||
pos = FVector2D(FMath::RoundToFloat(pos.X), FMath::RoundToFloat(pos.Y));
|
||||
|
||||
circleSlot->SetPosition(pos);
|
||||
circleSlot->SetSize(size);
|
||||
circle->SetUV(pos / geometry.GetLocalSize(), size / geometry.GetLocalSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
UMiniMapIconWidget* UMiniMapWidget::m_AllocateWidget()
|
||||
{
|
||||
UMiniMapIconWidget* widget = nullptr;
|
||||
if (m_widgetIndex >= m_miniMapIconWidgetPool.Num())
|
||||
{
|
||||
widget = CreateWidget<UMiniMapIconWidget>(GetWorld(), iconWidget);
|
||||
iconLayer->AddChild(widget);
|
||||
m_miniMapIconWidgetPool.Add(widget);
|
||||
}
|
||||
else
|
||||
widget = m_miniMapIconWidgetPool[m_widgetIndex];
|
||||
|
||||
m_widgetIndex++;
|
||||
return widget;
|
||||
}
|
||||
UMiniMapIconWidget* UMiniMapWidget::m_AllocateArrow()
|
||||
{
|
||||
UMiniMapIconWidget* arrow = nullptr;
|
||||
if (m_arrowIndex >= m_arrowMapIconWidgetPool.Num())
|
||||
{
|
||||
arrow = CreateWidget<UMiniMapIconWidget>(GetWorld(), iconWidget);
|
||||
arrowLayer->AddChild(arrow);
|
||||
m_arrowMapIconWidgetPool.Add(arrow);
|
||||
SetObjectIcon(arrowIcon, arrow);
|
||||
}
|
||||
else
|
||||
arrow = m_arrowMapIconWidgetPool[m_arrowIndex];
|
||||
|
||||
m_arrowIndex++;
|
||||
return arrow;
|
||||
}
|
||||
UMiniMapVisionCircle* UMiniMapWidget::m_AllocateVisionCircle()
|
||||
{
|
||||
UMiniMapVisionCircle* circle = nullptr;
|
||||
if (m_circleIndex >= m_viewCircleWidgetPool.Num())
|
||||
{
|
||||
circle = CreateWidget<UMiniMapVisionCircle>(GetWorld(), visionCircleWidget);
|
||||
visionLayer->AddChild(circle);
|
||||
m_viewCircleWidgetPool.Add(circle);
|
||||
circle->SetTexture(backgroundTexture);
|
||||
}
|
||||
else
|
||||
circle = m_viewCircleWidgetPool[m_circleIndex];
|
||||
|
||||
m_circleIndex++;
|
||||
return circle;
|
||||
}
|
||||
|
||||
FVector2D UMiniMapWidget::ConvertWorldToMinimap(const FVector2D& pos)
|
||||
{
|
||||
const FVector2D scale = FVector2D(m_max.X - m_min.X, m_max.Y - m_min.Y);
|
||||
FVector2D relativePos = (pos - m_min) / scale;
|
||||
FVector2D returnPos = FVector2D(relativePos.Y, relativePos.X);
|
||||
returnPos.Y = 1.0f - returnPos.Y;
|
||||
return returnPos;
|
||||
}
|
||||
|
||||
void UMiniMapWidget::SetObjectIcon_Implementation(UTexture2D* texture, UMiniMapIconWidget* widget)
|
||||
{
|
||||
// No implementation
|
||||
}
|
||||
|
||||
void UMiniMapWidget::SetMinimapInfo_Implementation(FVector2D player0, FVector2D player1, float radius)
|
||||
{
|
||||
// No implementation
|
||||
}
|
||||
89
Source/UnrealProject/GUI/Minimap/MiniMapWidget.h
Normal file
89
Source/UnrealProject/GUI/Minimap/MiniMapWidget.h
Normal file
@@ -0,0 +1,89 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include <map>
|
||||
#include "Minimap.h"
|
||||
#include "MiniMapWidget.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UMiniMapWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UMiniMapWidget(const FObjectInitializer& init);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "MiniMap")
|
||||
void SetObjectIcon(class UTexture2D* texture, class UMiniMapIconWidget* widget);
|
||||
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeTick(const FGeometry& geometry, float deltaTime) override;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
float viewRadius;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Minimap")
|
||||
FLinearColor playerColor;
|
||||
UPROPERTY(EditAnywhere, Category = "Minimap")
|
||||
FLinearColor allyColor;
|
||||
UPROPERTY(EditAnywhere, Category = "Minimap")
|
||||
FLinearColor enemyColor;
|
||||
UPROPERTY(EditAnywhere, Category = "Minimap")
|
||||
FLinearColor neutralColor;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
UTexture2D* playerIcon;
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
UTexture2D* creatureIcon;
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
UTexture2D* bossIcon;
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
UTexture2D* campIcon;
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
UTexture2D* arrowIcon;
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "MiniMap")
|
||||
void SetMinimapInfo(FVector2D player0, FVector2D player1, float radius);
|
||||
|
||||
FVector2D ConvertWorldToMinimap(const FVector2D& pos);
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
TSubclassOf<class UMiniMapIconWidget> iconWidget;
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
TSubclassOf<class UMiniMapVisionCircle> visionCircleWidget;
|
||||
UPROPERTY(BlueprintReadWrite, Category = "MiniMap")
|
||||
UCanvasPanel* iconLayer;
|
||||
UPROPERTY(BlueprintReadWrite, Category = "MiniMap")
|
||||
UCanvasPanel* arrowLayer;
|
||||
UPROPERTY(BlueprintReadWrite, Category = "MiniMap")
|
||||
UTexture2D* backgroundTexture;
|
||||
UPROPERTY(BlueprintReadWrite, Category = "MiniMap")
|
||||
UCanvasPanel* visionLayer;
|
||||
|
||||
private:
|
||||
void m_DrawCreature(const FGeometry& geometry, UMiniMapIconWidget* widget, class ACharacterBase* character);
|
||||
UMiniMapIconWidget* m_AllocateWidget();
|
||||
UMiniMapIconWidget* m_AllocateArrow();
|
||||
UMiniMapVisionCircle* m_AllocateVisionCircle();
|
||||
|
||||
TArray<class UMiniMapIconWidget*> m_miniMapIconWidgetPool;
|
||||
TArray<class UMiniMapIconWidget*> m_arrowMapIconWidgetPool;
|
||||
TArray<class UMiniMapVisionCircle*> m_viewCircleWidgetPool;
|
||||
TArray<MiniMap::MinimapHandle*> m_handleBuffer;
|
||||
|
||||
FVector2D m_min;
|
||||
FVector2D m_max;
|
||||
float m_size;
|
||||
float m_viewRadius;
|
||||
float m_arrowAnim;
|
||||
int32 m_localTeam;
|
||||
|
||||
int32 m_widgetIndex;
|
||||
int32 m_arrowIndex;
|
||||
int32 m_circleIndex;
|
||||
};
|
||||
58
Source/UnrealProject/GUI/PlayerSlot.cpp
Normal file
58
Source/UnrealProject/GUI/PlayerSlot.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "PlayerSlot.h"
|
||||
#include "DefaultGameInstance.h"
|
||||
#include "PlayerStateBase.h"
|
||||
|
||||
UPlayerSlot::UPlayerSlot(const FObjectInitializer& init)
|
||||
: Super(init)
|
||||
{
|
||||
}
|
||||
void UPlayerSlot::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
currentPlayerState = nullptr;
|
||||
isLocal = false;
|
||||
OnChanged();
|
||||
}
|
||||
void UPlayerSlot::Init(APlayerStateBase* playerState, bool isLocal)
|
||||
{
|
||||
if(!playerState && m_playerStateSet)
|
||||
{
|
||||
// Clear this slot
|
||||
m_playerStateSet = false;
|
||||
OnChanged();
|
||||
return;
|
||||
}
|
||||
if (currentPlayerState == playerState)
|
||||
return;
|
||||
|
||||
this->isLocal = isLocal;
|
||||
if (playerState)
|
||||
{
|
||||
// Get the player info to display in this player slot
|
||||
const TSharedPtr<const FUniqueNetId> netID = playerState->UniqueId.GetUniqueNetId();
|
||||
if (netID.IsValid())
|
||||
{
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
UDefaultGameInstance* gameInstance = Cast<UDefaultGameInstance>(world->GetGameInstance());
|
||||
playerState->UpdatePersona();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalid NetID somehow
|
||||
GWWARNING(L"NetID is invalid");
|
||||
return;
|
||||
}
|
||||
m_playerStateSet = true;
|
||||
}
|
||||
currentPlayerState = playerState;
|
||||
|
||||
OnChanged();
|
||||
}
|
||||
|
||||
void UPlayerSlot::OnChanged_Implementation()
|
||||
{
|
||||
}
|
||||
33
Source/UnrealProject/GUI/PlayerSlot.h
Normal file
33
Source/UnrealProject/GUI/PlayerSlot.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "PlayerSlot.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UPlayerSlot : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPlayerSlot(const FObjectInitializer& init);
|
||||
virtual void NativeConstruct() override;
|
||||
void Init(class APlayerStateBase* playerState, bool isLocal = false);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Player")
|
||||
void OnChanged();
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Player")
|
||||
class APlayerStateBase* currentPlayerState;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Player")
|
||||
bool isLocal;
|
||||
|
||||
private:
|
||||
// Keep this boolean to detect currentPlayerState becoming invalid(nullptr)
|
||||
bool m_playerStateSet;
|
||||
|
||||
};
|
||||
16
Source/UnrealProject/GUI/SizeBorder.cpp
Normal file
16
Source/UnrealProject/GUI/SizeBorder.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "SizeBorder.h"
|
||||
#include "SlateBlueprintLibrary.h"
|
||||
|
||||
void USizeBorder::NativeTick(const FGeometry& MyGeometry, float DeltaTime)
|
||||
{
|
||||
USlateBlueprintLibrary::LocalToViewport(this, MyGeometry, MyGeometry.GetLocalSize(),
|
||||
pixelSize, viewportSize);
|
||||
USlateBlueprintLibrary::LocalToViewport(this, MyGeometry, FVector2D(0.0f, 0.0f),
|
||||
pixelPosition, viewportPosition);
|
||||
pixelSize -= pixelPosition;
|
||||
viewportSize -= viewportPosition;
|
||||
Super::NativeTick(MyGeometry, DeltaTime);
|
||||
}
|
||||
28
Source/UnrealProject/GUI/SizeBorder.h
Normal file
28
Source/UnrealProject/GUI/SizeBorder.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "SizeBorder.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API USizeBorder : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Size")
|
||||
FVector2D pixelPosition;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Size")
|
||||
FVector2D pixelSize;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Size")
|
||||
FVector2D viewportPosition;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Size")
|
||||
FVector2D viewportSize;
|
||||
|
||||
void NativeTick(const FGeometry& MyGeometry, float DeltaTime) override;
|
||||
|
||||
};
|
||||
130
Source/UnrealProject/GUI/SkillTree/EffectSelector.cpp
Normal file
130
Source/UnrealProject/GUI/SkillTree/EffectSelector.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "EffectSelector.h"
|
||||
#include "EffectSlot.h"
|
||||
#include "BaseSkillObject.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "SkillWidget.h"
|
||||
|
||||
TSubclassOf<class UEffectSelector> defaultEffectSelectorClass;
|
||||
|
||||
UEffectSelector::UEffectSelector(const FObjectInitializer& init)
|
||||
: Super(init)
|
||||
{
|
||||
defaultEffectSelectorClass = ConstructorHelpers::FClassFinder<UEffectSelector>(L"/Game/Assets/GUI/WEEGEE_SkillEffectSelector").Class;
|
||||
}
|
||||
void UEffectSelector::NativeConstruct()
|
||||
{
|
||||
m_container = Cast<UPanelWidget>(WidgetTree->FindWidget("EffectSlotContainer"));
|
||||
if(!m_container)
|
||||
{
|
||||
GERROR("\"Container\" not found in effect selector widget " + GetName());
|
||||
}
|
||||
|
||||
onItemSelected.AddDynamic(this, &UEffectSelector::m_OnItemSelected);
|
||||
onItemSelectionConfirmed.AddDynamic(this, &UEffectSelector::m_OnItemSelectionConfirmed);
|
||||
|
||||
m_nameField = Cast<UTextBlock>(WidgetTree->FindWidget("NameField"));
|
||||
m_descriptionField = Cast<UMultiLineEditableTextBox>(WidgetTree->FindWidget("DescField"));
|
||||
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
|
||||
void UEffectSelector::Init(class USkillWidget* widget)
|
||||
{
|
||||
this->skill = widget->skillAsset;
|
||||
this->widget = widget;
|
||||
|
||||
// Remove old items
|
||||
for(UEffectSlot* slot : m_effectSlots)
|
||||
{
|
||||
if(slot)
|
||||
slot->RemoveFromParent();
|
||||
}
|
||||
|
||||
if(!skill)
|
||||
{
|
||||
m_effectSlots.SetNum(0);
|
||||
RescanItems();
|
||||
return;
|
||||
}
|
||||
|
||||
// Add effect slot buttons
|
||||
for(UAbilityInfo* abilityEffect : skill->abilityEffects)
|
||||
{
|
||||
if(abilityEffect)
|
||||
{
|
||||
UEffectSlot* slot = CreateWidget<UEffectSlot>(GetWorld(), effectSlotClass);
|
||||
m_container->AddChild(slot);
|
||||
slot->Init(abilityEffect);
|
||||
m_effectSlots.Add(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
GERROR("Didn't set ability on skill " + skill->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
RescanItems();
|
||||
}
|
||||
|
||||
void UEffectSelector::Init2(class TArray<UAbilityInfo*> abilities)
|
||||
{
|
||||
this->skill = nullptr;
|
||||
this->widget = nullptr;
|
||||
// Remove old items
|
||||
for(UEffectSlot* slot : m_effectSlots)
|
||||
{
|
||||
if(slot)
|
||||
slot->RemoveFromParent();
|
||||
}
|
||||
|
||||
// Add effect slot buttons
|
||||
for(UAbilityInfo* abilityEffect : abilities)
|
||||
{
|
||||
if(abilityEffect)
|
||||
{
|
||||
UEffectSlot* slot = CreateWidget<UEffectSlot>(GetWorld(), effectSlotClass);
|
||||
m_container->AddChild(slot);
|
||||
slot->Init(abilityEffect);
|
||||
m_effectSlots.Add(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
GERROR("Didn't set ability on skill " + skill->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
RescanItems();
|
||||
}
|
||||
|
||||
void UEffectSelector::m_OnItemSelected(UMenuItemBase* item)
|
||||
{
|
||||
UEffectSlot* slot = Cast<UEffectSlot>(item);
|
||||
if(slot && slot->ability)
|
||||
{
|
||||
if(m_nameField)
|
||||
m_nameField->SetText(FText::FromString(slot->ability->name));
|
||||
if(m_descriptionField)
|
||||
m_descriptionField->SetText(FText::FromString(slot->ability->description));
|
||||
}
|
||||
}
|
||||
|
||||
void UEffectSelector::m_OnItemSelectionConfirmed(UMenuItemBase* item)
|
||||
{
|
||||
UEffectSlot* slot = Cast<UEffectSlot>(item);
|
||||
if(slot && slot->ability)
|
||||
{
|
||||
if(widget)
|
||||
{
|
||||
widget->SetSelectedEffect(slot->index);
|
||||
}
|
||||
onEffectSelected.Broadcast(slot->index);
|
||||
OnSelectionConfirmed(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
GERROR(L"Invalid ability selected");
|
||||
}
|
||||
}
|
||||
52
Source/UnrealProject/GUI/SkillTree/EffectSelector.h
Normal file
52
Source/UnrealProject/GUI/SkillTree/EffectSelector.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GUI/Menu/SubMenu.h"
|
||||
#include "EffectSelector.generated.h"
|
||||
|
||||
extern TSubclassOf<class UEffectSelector> defaultEffectSelectorClass;
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UEffectSelector : public USubMenu
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UEffectSelector(const FObjectInitializer& init);
|
||||
virtual void NativeConstruct();
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent)
|
||||
void OnSelectionConfirmed(UEffectSlot* effect);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="EffectSelector")
|
||||
void Init(class USkillWidget* widget);
|
||||
UFUNCTION(BlueprintCallable, Category = "EffectSelector")
|
||||
void Init2(TArray<class UAbilityInfo*> abilities);
|
||||
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TSubclassOf<class UEffectSlot> effectSlotClass;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
class UBaseSkillObject* skill;
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
class USkillWidget* widget;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnEffectSelected, int32, selected);
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnEffectSelected onEffectSelected;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void m_OnItemSelected(UMenuItemBase* item);
|
||||
UFUNCTION()
|
||||
void m_OnItemSelectionConfirmed(UMenuItemBase* item);
|
||||
|
||||
UPROPERTY()
|
||||
UTextBlock* m_nameField;
|
||||
UPROPERTY()
|
||||
UMultiLineEditableTextBox* m_descriptionField;
|
||||
UPROPERTY()
|
||||
UPanelWidget* m_container;
|
||||
UPROPERTY()
|
||||
TArray<class UEffectSlot*> m_effectSlots;
|
||||
};
|
||||
31
Source/UnrealProject/GUI/SkillTree/EffectSlot.cpp
Normal file
31
Source/UnrealProject/GUI/SkillTree/EffectSlot.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "EffectSlot.h"
|
||||
#include "AbilityInfo.h"
|
||||
|
||||
void UEffectSlot::NativeConstruct()
|
||||
{
|
||||
m_name = Cast<UTextBlock>(WidgetTree->FindWidget("NameField"));
|
||||
m_image = Cast<UImage>(WidgetTree->FindWidget("Icon"));
|
||||
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
void UEffectSlot::Init(class UAbilityInfo* ability)
|
||||
{
|
||||
this->ability = ability;
|
||||
|
||||
if(!ability)
|
||||
return;
|
||||
|
||||
if(m_name)
|
||||
{
|
||||
m_name->SetText(FText::FromString(ability->name));
|
||||
}
|
||||
if(m_image)
|
||||
{
|
||||
m_image->SetBrushFromTexture(ability->icon);
|
||||
}
|
||||
|
||||
OnInit(ability);
|
||||
}
|
||||
31
Source/UnrealProject/GUI/SkillTree/EffectSlot.h
Normal file
31
Source/UnrealProject/GUI/SkillTree/EffectSlot.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GUI/Menu/MenuButton.h"
|
||||
#include "EffectSlot.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UEffectSlot : public UMenuButton
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "EffectSelector")
|
||||
void Init(class UAbilityInfo* ability);
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "EffectSelector")
|
||||
void OnInit(class UAbilityInfo* ability);
|
||||
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
class UAbilityInfo* ability;
|
||||
|
||||
private:
|
||||
UImage* m_image;
|
||||
UTextBlock* m_name;
|
||||
};
|
||||
22
Source/UnrealProject/GUI/SkillTree/HexagonTile.cpp
Normal file
22
Source/UnrealProject/GUI/SkillTree/HexagonTile.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "HexagonTile.h"
|
||||
|
||||
void UHexagonTile::NativeConstruct()
|
||||
{
|
||||
TArray<UWidget*> widgets;
|
||||
WidgetTree->GetAllWidgets(widgets);
|
||||
|
||||
for (int32 i = 0; i < widgets.Num(); i++)
|
||||
{
|
||||
UImage* tmp = Cast<UImage>(widgets[i]);
|
||||
if (tmp)
|
||||
{
|
||||
material = tmp->GetDynamicMaterial();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
30
Source/UnrealProject/GUI/SkillTree/HexagonTile.h
Normal file
30
Source/UnrealProject/GUI/SkillTree/HexagonTile.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "HexagonTile.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UHexagonTile : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY( EditAnywhere, Category = "UI" )
|
||||
int32 x;
|
||||
|
||||
UPROPERTY( EditAnywhere, Category = "UI" )
|
||||
int32 y;
|
||||
|
||||
FMargin GetMargins()
|
||||
{
|
||||
return GetFullScreenOffset();
|
||||
}
|
||||
|
||||
class UMaterialInstanceDynamic* material;
|
||||
|
||||
virtual void NativeConstruct() override;
|
||||
};
|
||||
155
Source/UnrealProject/GUI/SkillTree/SkillTreeState.h
Normal file
155
Source/UnrealProject/GUI/SkillTree/SkillTreeState.h
Normal file
@@ -0,0 +1,155 @@
|
||||
#pragma once
|
||||
#include "SkillTreeState.Generated.h"
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSkillTreeStateObject
|
||||
{
|
||||
GENERATED_BODY();
|
||||
UPROPERTY()
|
||||
FVector2D gridIndex;
|
||||
UPROPERTY()
|
||||
float rotation;
|
||||
UPROPERTY()
|
||||
TArray<FIntPoint> placedPoints;
|
||||
UPROPERTY()
|
||||
UClass* skillObject;
|
||||
UPROPERTY()
|
||||
int32 selectedEffect;
|
||||
|
||||
bool ContainsRoot(FIntPoint root)
|
||||
{
|
||||
for (int32 i = 0; i < placedPoints.Num(); i++)
|
||||
{
|
||||
if (placedPoints[i] == root) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
TArray<FSkillTreeStateObject*> ConnectedSkills( TMap<FIntPoint, FSkillTreeStateObject*>& skillsMap )
|
||||
{
|
||||
TArray<FSkillTreeStateObject*> result;
|
||||
const FIntPoint offsets[2][6]{
|
||||
{ FIntPoint(0, -1), FIntPoint(1, -1), FIntPoint(1, 0), FIntPoint(0, 1), FIntPoint(-1, 0), FIntPoint(-1, -1) },
|
||||
{ FIntPoint(0, -1), FIntPoint(1, 0), FIntPoint(1, 1), FIntPoint(0, 1), FIntPoint(-1, 1), FIntPoint(-1, 0) } };
|
||||
for (int32 i = 0; i < placedPoints.Num(); i++)
|
||||
{
|
||||
int32 k = placedPoints[i].X & 1;
|
||||
for (int32 j = 0; j < 6; j++)
|
||||
{
|
||||
FIntPoint checkPoint = placedPoints[i] + offsets[k][j];
|
||||
if (placedPoints.Contains(checkPoint)) continue;
|
||||
if (skillsMap.Contains(checkPoint))
|
||||
{
|
||||
FSkillTreeStateObject* skill = *skillsMap.Find(checkPoint);
|
||||
if (result.Contains(skill)) continue;
|
||||
result.Push(skill);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSkillTreeState
|
||||
{
|
||||
GENERATED_BODY();
|
||||
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
TArray<FSkillTreeStateObject> objects;
|
||||
|
||||
private:
|
||||
void WalkTree(TMap<FSkillTreeStateObject*, TArray<FSkillTreeStateObject*>>* a_map, TArray<FSkillTreeStateObject*>* a_checked, TArray<FSkillTreeStateObject*> a_nodes)
|
||||
{
|
||||
for (int32 i = 0; i < a_nodes.Num(); i++)
|
||||
{
|
||||
if (a_checked->Contains(a_nodes[i])) continue;
|
||||
a_checked->Push(a_nodes[i]);
|
||||
TArray<FSkillTreeStateObject*>* connected = a_map->Find(a_nodes[i]);
|
||||
if (!connected)
|
||||
{
|
||||
YERROR("Could not find the connected skills.");
|
||||
continue;
|
||||
}
|
||||
WalkTree(a_map, a_checked, *connected);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
bool ValidateSkillTree()
|
||||
{
|
||||
bool rootResult = false;
|
||||
bool connectResult = true;
|
||||
|
||||
if (objects.Num() == 0) return true;
|
||||
|
||||
|
||||
TMap<FSkillTreeStateObject*, TArray<FSkillTreeStateObject*>> connectedSkillsMap;
|
||||
TMap<FIntPoint, FSkillTreeStateObject*> skillsMap;
|
||||
FSkillTreeStateObject* rootSkill = NULL;
|
||||
for (int32 i = 0; i < objects.Num(); i++)
|
||||
{
|
||||
TArray<FIntPoint> points = objects[i].placedPoints;
|
||||
for (int32 j = 0; j < points.Num(); j++)
|
||||
{
|
||||
skillsMap.Add(points[j], &objects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < objects.Num(); i++)
|
||||
{
|
||||
TArray<FIntPoint> points = objects[i].placedPoints;
|
||||
if (!rootResult)
|
||||
{
|
||||
bool rootTest = objects[i].ContainsRoot(FIntPoint(6,15));
|
||||
if (rootTest)
|
||||
{
|
||||
rootResult = true;
|
||||
rootSkill = &objects[i];
|
||||
}
|
||||
}
|
||||
TArray<FSkillTreeStateObject*> connectedSkills = objects[i].ConnectedSkills(skillsMap);
|
||||
connectedSkillsMap.Add(&objects[i], connectedSkills);
|
||||
if (connectedSkills.Num() == 0)
|
||||
{
|
||||
connectResult = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (connectResult && rootResult)
|
||||
{
|
||||
if (!rootSkill)
|
||||
{
|
||||
YERROR("Root Skill is NULL!");
|
||||
return false;
|
||||
}
|
||||
TArray<FSkillTreeStateObject*> checkedSkills;
|
||||
checkedSkills.Push(rootSkill);
|
||||
TArray<FSkillTreeStateObject*>* connected = connectedSkillsMap.Find(rootSkill);
|
||||
if (!connected)
|
||||
{
|
||||
YPRINT("Root Skill is not attached to any skills!");
|
||||
return false;
|
||||
}
|
||||
WalkTree(&connectedSkillsMap, &checkedSkills, *connected);
|
||||
if (objects.Num() != checkedSkills.Num())
|
||||
connectResult = false;
|
||||
else
|
||||
{
|
||||
for (int32 i = 0; i < objects.Num(); i++)
|
||||
{
|
||||
if (!checkedSkills.Contains(&objects[i]))
|
||||
{
|
||||
connectResult = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isValid = false;
|
||||
if (objects.Num() == 1 && rootResult)
|
||||
isValid = true;
|
||||
else
|
||||
isValid = (rootResult && connectResult);
|
||||
return isValid;
|
||||
}
|
||||
};
|
||||
678
Source/UnrealProject/GUI/SkillTree/SkillTreeWidget.cpp
Normal file
678
Source/UnrealProject/GUI/SkillTree/SkillTreeWidget.cpp
Normal file
@@ -0,0 +1,678 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "SkillTreeWidget.h"
|
||||
#include "SkillTreeObject.h"
|
||||
#include "BaseSkillObject.h"
|
||||
#include "HexagonTile.h"
|
||||
#include "ScrollBox.h"
|
||||
#include "SkillWidget.h"
|
||||
#include "CanvasPanel.h"
|
||||
#include "PlayerControllerBase.h"
|
||||
#include "DefaultGameInstance.h"
|
||||
#include "CharacterSettings.h"
|
||||
#include "SlateBlueprintLibrary.h"
|
||||
#include "ToolTipWidget.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "SizeBorder.h"
|
||||
#include "WidgetLayoutLibrary.h"
|
||||
|
||||
int32 roundToNearestOdd1(float a_in)
|
||||
{
|
||||
return 2 * FMath::FloorToInt((a_in / 2) + 0.5f) - 2;
|
||||
}
|
||||
|
||||
int32 roundToNearestEven1(float a_in)
|
||||
{
|
||||
return 2 * FMath::FloorToInt(a_in / 2);
|
||||
}
|
||||
|
||||
void USkillTreeWidget::NativeOnSkillTreeChanged(ESkillTreeChangeEvent event, UBaseSkillObject* relevantObject)
|
||||
{
|
||||
// Recalculate dominantDamageType
|
||||
int32_t categoryCounters[] = { 0,0,0 };
|
||||
int32_t largest = 0;
|
||||
for(USkillWidget* skill : m_skillWidgets)
|
||||
{
|
||||
if(skill->selectedEffect >= 0 && skill->skillAsset)
|
||||
{
|
||||
if(skill->selectedEffect >= skill->skillAsset->abilityEffects.Num())
|
||||
{
|
||||
GERROR("CORRUPT SKILL TREE");
|
||||
continue;
|
||||
}
|
||||
|
||||
UAbilityInfo* info = skill->skillAsset->abilityEffects[skill->selectedEffect];
|
||||
int32_t& myCounter = categoryCounters[(size_t)info->abilityCategory];
|
||||
myCounter++;
|
||||
|
||||
if(myCounter >= largest)
|
||||
{
|
||||
largest = myCounter;
|
||||
dominantAbilityCategory = info->abilityCategory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onSkillTreeChanged.Broadcast(event, relevantObject);
|
||||
}
|
||||
|
||||
USkillTreeWidget::USkillTreeWidget(const FObjectInitializer& init)
|
||||
: Super( init )
|
||||
{
|
||||
m_interactive = true;
|
||||
m_shown = false;
|
||||
m_lastFocusedWidget = nullptr;
|
||||
isValid = false;
|
||||
placedSkillHexMap = new FHexMap();
|
||||
for (int32 x = 0; x < 13; x++)
|
||||
{
|
||||
placedSkillHexMap->rawdata[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void USkillTreeWidget::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
|
||||
UpdateVisibility();
|
||||
|
||||
// Get the tooltip widget
|
||||
m_toolTipWidget = Cast<UToolTipWidget>(WidgetTree->FindWidget("ToolTipSWidget"));
|
||||
|
||||
m_sizeBorder = Cast<USizeBorder>(WidgetTree->FindWidget("SizeBorder"));
|
||||
|
||||
if ( !skillTreeAsset ) return;
|
||||
TArray<UWidget*> widgets;
|
||||
WidgetTree->GetAllWidgets( widgets );
|
||||
UScrollBox* scrollBox = NULL;
|
||||
m_skillTreeCanvas = WidgetTree->FindWidget<UCanvasPanel>("Main_SkillTree");
|
||||
//m_focusBorder = WidgetTree->FindWidget<UBorder>("FocusBorder");
|
||||
m_inputCapture = WidgetTree->FindWidget<UBorder>("InputCapture");
|
||||
m_inputCapture->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
|
||||
for ( int32 i = 0; i < widgets.Num(); i++ )
|
||||
{
|
||||
UHexagonTile* tmpHex = Cast<UHexagonTile>( widgets[i] );
|
||||
UScrollBox* tmpScrollBox = Cast<UScrollBox>( widgets[i] );
|
||||
//USkillWidget* tmpSkill = Cast<USkillWidget>( widgets[i] );
|
||||
if ( tmpScrollBox ) scrollBox = tmpScrollBox;
|
||||
//if ( tmpSkill ) m_selectedSkill = tmpSkill;
|
||||
if ( !tmpHex ) continue;
|
||||
tiles.Push( tmpHex );
|
||||
bool test = skillTreeAsset->hexMap.Get( tmpHex->x, tmpHex->y );
|
||||
if ( test ) tmpHex->SetVisibility( ESlateVisibility::Hidden );
|
||||
}
|
||||
if ( m_selectedSkill )
|
||||
{
|
||||
m_selectedSkill->parent = this;
|
||||
m_selectedSkill->SetDragable( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
YWARNING( "No Selected Skill found!" );
|
||||
}
|
||||
}
|
||||
void USkillTreeWidget::NativeDestruct()
|
||||
{
|
||||
delete placedSkillHexMap;
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
|
||||
void USkillTreeWidget::NativeTick(const FGeometry& MyGeometry, float DeltaTime)
|
||||
{
|
||||
Super::NativeTick(MyGeometry, DeltaTime);
|
||||
|
||||
//if ((m_focusBorder && m_focusBorder->IsHovered()) || draggingWidget)
|
||||
//{
|
||||
// //m_inputCapture->SetVisibility(ESlateVisibility::Visible);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// //m_inputCapture->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
|
||||
//}
|
||||
|
||||
if (m_toolTipWidget)
|
||||
{
|
||||
m_toolTipWidget->showTooltip = false;
|
||||
|
||||
FVector2D mousePos;
|
||||
UWidgetLayoutLibrary::GetMousePositionScaledByDPI(GetOwningPlayer(), mousePos.X, mousePos.Y);
|
||||
FIntPoint mouseGridPos = GetGridIndex(mousePos);
|
||||
USkillWidget** sw = tileMap.Find(mouseGridPos);
|
||||
USkillWidget* skill = sw ? *sw : nullptr;
|
||||
if (skill)
|
||||
{
|
||||
// Reset hovered state
|
||||
UAbilityInfo* info = skill->GetSelectedEffectAbility();
|
||||
if (info)
|
||||
{
|
||||
m_toolTipWidget->SetTitle(info->name);
|
||||
m_toolTipWidget->SetText(info->description);
|
||||
m_toolTipWidget->position = skill->tooltipAreaPosition;
|
||||
m_toolTipWidget->size = skill->tooltipAreaSize;
|
||||
m_toolTipWidget->showTooltip = true;
|
||||
}
|
||||
skill->hovered = true;
|
||||
}
|
||||
if (m_lastFocusedWidget && m_lastFocusedWidget != skill)
|
||||
{
|
||||
m_lastFocusedWidget->hovered = false;
|
||||
}
|
||||
m_lastFocusedWidget = skill;
|
||||
}
|
||||
}
|
||||
|
||||
FReply USkillTreeWidget::NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
|
||||
{
|
||||
if(!IsInteractive())
|
||||
return FReply::Handled();
|
||||
//GWPRINT(L"Skill tree up");
|
||||
if(draggingWidget && !draggingWidget->IsUsingController())
|
||||
{
|
||||
draggingWidget->StopDragging();
|
||||
return FReply::Handled();
|
||||
}
|
||||
return FReply::Handled();
|
||||
}
|
||||
FReply USkillTreeWidget::NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
|
||||
{
|
||||
if(!IsInteractive())
|
||||
return FReply::Handled();
|
||||
//GWPRINT(L"Skill tree down");
|
||||
FKey button = InMouseEvent.GetEffectingButton();
|
||||
if(button == EKeys::LeftMouseButton)
|
||||
{
|
||||
if(draggingWidget && !draggingWidget->IsUsingController())
|
||||
{
|
||||
draggingWidget->StopDragging();
|
||||
return FReply::Handled();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_lastFocusedWidget)
|
||||
{
|
||||
if(m_lastFocusedWidget->hovered)
|
||||
m_lastFocusedWidget->StartDragging();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(button == EKeys::RightMouseButton)
|
||||
{
|
||||
if(!draggingWidget && m_lastFocusedWidget)
|
||||
{
|
||||
if(m_lastFocusedWidget->hovered)
|
||||
{
|
||||
m_lastFocusedWidget->OnRequireSkillEffect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
FReply USkillTreeWidget::NativeOnMouseWheel(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
|
||||
{
|
||||
//GWPRINT(L"USkillTreeWidget MouseWheel " + InMouseEvent.GetWheelDelta());
|
||||
if (draggingWidget)
|
||||
{
|
||||
float add = 60.0f * FMath::FloorToFloat(InMouseEvent.GetWheelDelta());
|
||||
draggingWidget->SetSkillRotation(add + draggingWidget->GetSkillRotation());
|
||||
}
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
void USkillTreeWidget::SetIsInteractive(bool interactive)
|
||||
{
|
||||
m_interactive = interactive;
|
||||
UpdateVisibility();
|
||||
}
|
||||
void USkillTreeWidget::UpdateVisibility()
|
||||
{
|
||||
if (m_shown)
|
||||
{
|
||||
SetVisibility(ESlateVisibility::SelfHitTestInvisible);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVisibility(ESlateVisibility::Hidden);
|
||||
}
|
||||
UImage* validImage = WidgetTree->FindWidget<UImage>("Validation_Image");
|
||||
if (m_interactive)
|
||||
{
|
||||
validImage->SetVisibility(ESlateVisibility::Visible);
|
||||
}
|
||||
else
|
||||
{
|
||||
validImage->SetVisibility(ESlateVisibility::Hidden);
|
||||
}
|
||||
}
|
||||
|
||||
void WalkTree(TMap<USkillWidget*, TArray<USkillWidget*>>* a_map, TArray<USkillWidget*>* a_checked, TArray<USkillWidget*> a_nodes)
|
||||
{
|
||||
for (int32 i = 0; i < a_nodes.Num(); i++)
|
||||
{
|
||||
if (a_checked->Contains(a_nodes[i])) continue;
|
||||
a_checked->Push(a_nodes[i]);
|
||||
TArray<USkillWidget*>* connected = a_map->Find(a_nodes[i]);
|
||||
if (!connected)
|
||||
{
|
||||
YERROR("Could not find the connected skills.");
|
||||
continue;
|
||||
}
|
||||
WalkTree(a_map, a_checked, *connected);
|
||||
}
|
||||
}
|
||||
|
||||
bool USkillTreeWidget::ValidateSkillTree()
|
||||
{
|
||||
bool rootResult = false;
|
||||
bool connectResult = true;
|
||||
if (m_skillWidgets.Num() == 0) return true;
|
||||
TMap<USkillWidget*, TArray<USkillWidget*>> connectedSkillsMap;
|
||||
USkillWidget* rootSkill = NULL;
|
||||
for (int32 i = 0; i < m_skillWidgets.Num(); i++)
|
||||
{
|
||||
TArray<FIntPoint> points = m_skillWidgets[i]->GetPoints();
|
||||
if (!rootResult)
|
||||
{
|
||||
bool rootTest = m_skillWidgets[i]->ContainsRoot(points);
|
||||
if (rootTest)
|
||||
{
|
||||
rootResult = true;
|
||||
rootSkill = m_skillWidgets[i];
|
||||
}
|
||||
}
|
||||
TArray<USkillWidget*> connectedSkills = m_skillWidgets[i]->ConnectedSkills(points);
|
||||
connectedSkillsMap.Add(m_skillWidgets[i], connectedSkills);
|
||||
if ( connectedSkills.Num() == 0 )
|
||||
{
|
||||
connectResult = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (connectResult && rootResult)
|
||||
{
|
||||
if (!rootSkill)
|
||||
{
|
||||
YERROR("Root Skill is NULL!");
|
||||
return false;
|
||||
}
|
||||
TArray<USkillWidget*> checkedSkills;
|
||||
checkedSkills.Push(rootSkill);
|
||||
TArray<USkillWidget*>* connected = connectedSkillsMap.Find(rootSkill);
|
||||
if (!connected)
|
||||
{
|
||||
YPRINT("Root Skill is not attached to any skills!");
|
||||
return false;
|
||||
}
|
||||
WalkTree(&connectedSkillsMap, &checkedSkills, *connected);
|
||||
if (m_skillWidgets.Num() != checkedSkills.Num())
|
||||
connectResult = false;
|
||||
else
|
||||
{
|
||||
for (int32 i = 0; i < m_skillWidgets.Num(); i++)
|
||||
{
|
||||
if (!checkedSkills.Contains(m_skillWidgets[i]))
|
||||
{
|
||||
connectResult = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_skillWidgets.Num() == 1 && rootResult)
|
||||
isValid = true;
|
||||
else
|
||||
isValid = (rootResult && connectResult);
|
||||
if (isValid)
|
||||
{
|
||||
UpdateLevel(10.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateLevel(0.0f);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
FSkillTreeState USkillTreeWidget::GetState()
|
||||
{
|
||||
FSkillTreeState state;
|
||||
for(int32 i = 0; i < m_skillWidgets.Num(); i++)
|
||||
{
|
||||
FSkillTreeStateObject obj;
|
||||
USkillWidget* skill = m_skillWidgets[i];
|
||||
obj.gridIndex = skill->GetGridIndex() /*- FVector2D(1.5,4) * m_skillTreeCanvas->RenderTransform.Scale.X*/;
|
||||
obj.placedPoints = skill->placedPoints;
|
||||
obj.skillObject = skill->skillAsset->GetClass();
|
||||
obj.rotation = skill->GetSkillRotation();
|
||||
obj.selectedEffect = skill->selectedEffect;
|
||||
state.objects.Add(obj);
|
||||
|
||||
//GWPRINT(L"Saving gridIndex " + obj.gridIndex);
|
||||
//for(int32 i = 0; i < obj.placedPoints.Num(); i++)
|
||||
//{
|
||||
// GWPRINT(L"Saving Placed point " + i + L" " + obj.placedPoints[i].X + L", " + obj.placedPoints[i].Y);
|
||||
//}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
TArray<class UBaseSkillObject*> USkillTreeWidget::GetApearanceState()
|
||||
{
|
||||
return m_skillClasses;
|
||||
}
|
||||
|
||||
void USkillTreeWidget::Clear()
|
||||
{
|
||||
// Remove all placed hexes
|
||||
auto arrayCopy = m_skillWidgets;
|
||||
for(int32 i = 0; i < arrayCopy.Num(); i++)
|
||||
{
|
||||
arrayCopy[i]->RemoveFromParent();
|
||||
RemoveSkill(arrayCopy[i], arrayCopy[i]->placedPoints);
|
||||
}
|
||||
m_skillWidgets.SetNum(0);
|
||||
m_skillClasses.SetNum(0);
|
||||
tileMap.Reset();
|
||||
|
||||
// Clear Hex map
|
||||
for(int32 x = 0; x < 13; x++)
|
||||
{
|
||||
placedSkillHexMap->rawdata[x] = 0;
|
||||
}
|
||||
|
||||
isValid = true;
|
||||
|
||||
// Trigger Callbacks
|
||||
NativeOnSkillTreeChanged(ESkillTreeChangeEvent::Cleared, nullptr);
|
||||
}
|
||||
void USkillTreeWidget::BuildFromState(const FSkillTreeState& state)
|
||||
{
|
||||
Clear();
|
||||
for(int32 i = 0; i < state.objects.Num(); i++)
|
||||
{
|
||||
const FSkillTreeStateObject& obj = state.objects[i];
|
||||
UBaseSkillObject* baseSkill = obj.skillObject->GetDefaultObject<UBaseSkillObject>();
|
||||
|
||||
if(!baseSkill)
|
||||
continue;
|
||||
|
||||
//GWPRINT(L"gridIndex " + obj.gridIndex);
|
||||
//for(int32 i = 0; i < obj.placedPoints.Num(); i++)
|
||||
//{
|
||||
// GWPRINT(L"Placed point " + i + L" " + obj.placedPoints[i].X + L", " + obj.placedPoints[i].Y);
|
||||
//}
|
||||
|
||||
m_selectedSkill = CreateWidget<USkillWidget>(GetWorld(), WidgetTemplate);
|
||||
m_selectedSkill->skillAsset = baseSkill;
|
||||
m_selectedSkill->parent = this;
|
||||
if(m_skillTreeCanvas)
|
||||
m_selectedSkill->SetRenderScale(m_skillTreeCanvas->RenderTransform.Scale);
|
||||
m_selectedSkill->SetPlaced();
|
||||
if(m_skillTreeCanvas)
|
||||
m_skillTreeCanvas->GetParent()->AddChild(m_selectedSkill);
|
||||
UCanvasPanelSlot* slot = Cast<UCanvasPanelSlot>(m_selectedSkill->Slot);
|
||||
if(slot)
|
||||
{
|
||||
slot->SetAutoSize(true);
|
||||
slot->SetAlignment(FVector2D(-0.5f, -0.5f));
|
||||
}
|
||||
m_selectedSkill->SetSelectedEffect(obj.selectedEffect);
|
||||
m_selectedSkill->placedPoints = obj.placedPoints;
|
||||
m_selectedSkill->SetSkillRotation(obj.rotation);
|
||||
m_selectedSkill->PlaceOnGridIndex(obj.gridIndex);
|
||||
AddSkill(m_selectedSkill, obj.placedPoints);
|
||||
}
|
||||
}
|
||||
|
||||
void USkillTreeWidget::RemoveSkill(UBaseSkillObject* skillObject)
|
||||
{
|
||||
int32 skillID = m_skillClasses.Find(skillObject);
|
||||
if (skillID == INDEX_NONE) return;
|
||||
CancelSkill(m_skillWidgets[skillID]);
|
||||
m_skillWidgets[skillID]->Remove();
|
||||
}
|
||||
|
||||
bool USkillTreeWidget::IsUsingSkill(UBaseSkillObject* skillObject) const
|
||||
{
|
||||
return m_skillClasses.Contains(skillObject);
|
||||
}
|
||||
|
||||
USkillWidget* USkillTreeWidget::GetUsedSkill(class UBaseSkillObject* skillObject)
|
||||
{
|
||||
for (int32 i = 0; i < m_skillWidgets.Num(); i++)
|
||||
{
|
||||
if (m_skillWidgets[i]->skillAsset == skillObject)
|
||||
return m_skillWidgets[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void USkillTreeWidget::Save(int32 saveSlot)
|
||||
{
|
||||
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
|
||||
UCharacterSettings* settings = inst->GetCharacterSettings();
|
||||
|
||||
if (saveSlot < 0 || saveSlot >= settings->characterSaves.Num())
|
||||
{
|
||||
JERROR("Invalid save slot");
|
||||
return;
|
||||
}
|
||||
|
||||
settings->characterSaves[saveSlot].skillTreeState = GetState();
|
||||
inst->SaveSettings();
|
||||
}
|
||||
void USkillTreeWidget::Load(int32 saveSlot)
|
||||
{
|
||||
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
|
||||
UCharacterSettings* settings = inst->GetCharacterSettings();
|
||||
|
||||
if (saveSlot < 0 || saveSlot >= settings->characterSaves.Num())
|
||||
{
|
||||
JERROR("Invalid save slot");
|
||||
return;
|
||||
}
|
||||
|
||||
BuildFromState(settings->characterSaves[saveSlot].skillTreeState);
|
||||
ValidateSkillTree();
|
||||
}
|
||||
|
||||
FIntPoint USkillTreeWidget::GetGridIndex(FVector2D in) const
|
||||
{
|
||||
FVector2D result;
|
||||
UCanvasPanelSlot* tmpSlot = UWidgetLayoutLibrary::SlotAsCanvasSlot(m_skillTreeCanvas);
|
||||
float viewportScale = UWidgetLayoutLibrary::GetViewportScale(const_cast<USkillTreeWidget*>(this));
|
||||
FVector2D topLeft;
|
||||
float scale = 32.0f * (m_skillTreeCanvas->RenderTransform.Scale.X / 2);
|
||||
float XScale = (scale * 1.73205f);
|
||||
float YScale = scale;
|
||||
if (tmpSlot)
|
||||
{
|
||||
FVector2D screenTopRight = FVector2D(GEngine->GameViewport->Viewport->GetSizeXY().X * tmpSlot->GetAnchors().Minimum.X, GEngine->GameViewport->Viewport->GetSizeXY().Y * tmpSlot->GetAnchors().Minimum.Y);
|
||||
FVector2D offset = tmpSlot->GetPosition() * viewportScale;
|
||||
FVector2D widgetTopLeft = ((tmpSlot->GetSize() * m_skillTreeCanvas->RenderTransform.Scale.X * viewportScale) / 2);
|
||||
topLeft = screenTopRight + offset - widgetTopLeft;
|
||||
topLeft /= viewportScale;
|
||||
//topLeft.X -= 16.0f;
|
||||
}
|
||||
|
||||
float XPreFloor = (in.X - topLeft.X) / XScale;
|
||||
float YPreFloor = (in.Y - topLeft.Y) / YScale;
|
||||
int32 XIndex = FMath::FloorToInt(XPreFloor);
|
||||
int32 YIndex = FMath::FloorToInt(YPreFloor);
|
||||
/*FVector2D magicOffset = FVector2D(1.5, 4) * m_skillTreeCanvas->RenderTransform.Scale.X;*/
|
||||
bool odd = (XIndex % 2) == 1;
|
||||
if (odd)
|
||||
YIndex = roundToNearestOdd1(YIndex);
|
||||
else
|
||||
YIndex = roundToNearestEven1(YIndex);
|
||||
return FIntPoint(XIndex, (YIndex) / 2.0f);
|
||||
}
|
||||
|
||||
void USkillTreeWidget::StartDragging(USkillWidget* widget)
|
||||
{
|
||||
check(draggingWidget == nullptr);
|
||||
draggingWidget = widget;
|
||||
m_inputCapture->SetVisibility(ESlateVisibility::Visible);
|
||||
}
|
||||
|
||||
void USkillTreeWidget::StopDragging(USkillWidget* widget)
|
||||
{
|
||||
if(draggingWidget != widget)
|
||||
{
|
||||
GERROR("Calling StopDragging twice");
|
||||
}
|
||||
draggingWidget = nullptr;
|
||||
m_inputCapture->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
|
||||
}
|
||||
|
||||
void USkillTreeWidget::UpdateTextInfo_Implementation()
|
||||
{
|
||||
}
|
||||
|
||||
void USkillTreeWidget::AddSkill(USkillWidget* a_skill, const TArray<FIntPoint>& a_points)
|
||||
{
|
||||
for ( int32 i = 0; i < a_points.Num(); i++ )
|
||||
{
|
||||
placedSkillHexMap->Set( a_points[i].X, a_points[i].Y, true );
|
||||
|
||||
tileMap.Add(a_points[i], a_skill);
|
||||
}
|
||||
|
||||
UWorld* const world = GetWorld();
|
||||
if ( !world )
|
||||
{
|
||||
YWARNING( "Couldn't get the World." );
|
||||
return;
|
||||
}
|
||||
APlayerControllerBase* const controller = Cast<APlayerControllerBase>( GetOwningPlayer() );
|
||||
if ( !controller )
|
||||
{
|
||||
YWARNING( "Couldn't get the Player." );
|
||||
return;
|
||||
}
|
||||
controller->OnLearnSkill( a_skill->skillAsset );
|
||||
m_skillWidgets.Add(a_skill);
|
||||
m_skillClasses.Add(a_skill->skillAsset);
|
||||
|
||||
// Trigger Callbacks
|
||||
NativeOnSkillTreeChanged(ESkillTreeChangeEvent::Added, a_skill->skillAsset);
|
||||
|
||||
ValidateSkillTree();
|
||||
}
|
||||
|
||||
void USkillTreeWidget::RemoveSkill( USkillWidget* a_skill, const TArray<FIntPoint>& a_points )
|
||||
{
|
||||
m_skillClasses.Remove(a_skill->skillAsset);
|
||||
m_skillWidgets.Remove(a_skill);
|
||||
for ( int32 i = 0; i < a_points.Num(); i++ )
|
||||
{
|
||||
placedSkillHexMap->Set( a_points[i].X, a_points[i].Y, false );
|
||||
tileMap.Remove(a_points[i]);
|
||||
}
|
||||
|
||||
UWorld* const world = GetWorld();
|
||||
if ( !world )
|
||||
{
|
||||
YWARNING( "Couldn't get the World." );
|
||||
return;
|
||||
}
|
||||
APlayerControllerBase* const controller = Cast<APlayerControllerBase>( GetOwningPlayer() );
|
||||
if ( !controller )
|
||||
{
|
||||
YWARNING( "Couldn't get the Player." );
|
||||
return;
|
||||
}
|
||||
controller->OnUnlearnSkill( a_skill->skillAsset );
|
||||
|
||||
// Trigger Callbacks
|
||||
NativeOnSkillTreeChanged(ESkillTreeChangeEvent::Removed, a_skill->skillAsset);
|
||||
}
|
||||
|
||||
USkillWidget* USkillTreeWidget::NewSkill( USkillWidget* a_skill, bool controller )
|
||||
{
|
||||
return NewSkillFromAsset(a_skill->skillAsset, controller);
|
||||
}
|
||||
|
||||
class USkillWidget* USkillTreeWidget::NewSkillFromAsset(UBaseSkillObject* skillAsset, bool controller /*= false*/)
|
||||
{
|
||||
if(draggingWidget)
|
||||
return NULL;
|
||||
//a_skill->Disable();
|
||||
m_selectedSkill = CreateWidget<USkillWidget>(GetWorld(), WidgetTemplate);
|
||||
m_selectedSkill->skillAsset = skillAsset;
|
||||
m_selectedSkill->parent = this;
|
||||
m_selectedSkill->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
|
||||
m_selectedSkill->SetDragable(true, controller);
|
||||
m_selectedSkill->SetRenderScale(m_skillTreeCanvas->RenderTransform.Scale);
|
||||
m_skillTreeCanvas->GetParent()->AddChild(m_selectedSkill);
|
||||
m_selectedSkill->SetUserFocus(GetOwningPlayer());
|
||||
UCanvasPanelSlot* slot = Cast<UCanvasPanelSlot>(m_selectedSkill->Slot);
|
||||
if(slot)
|
||||
{
|
||||
slot->SetAutoSize(true);
|
||||
slot->SetAlignment(FVector2D(-0.5f, -0.5f));
|
||||
}
|
||||
|
||||
if(controller)
|
||||
{
|
||||
// Set initial position
|
||||
m_selectedSkill->MoveSkillAbsolute(lastGridIndex);
|
||||
}
|
||||
|
||||
return m_selectedSkill;
|
||||
}
|
||||
|
||||
void USkillTreeWidget::CancelSkill(USkillWidget* a_skill)
|
||||
{
|
||||
a_skill->RemoveFromParent();
|
||||
ValidateSkillTree();
|
||||
}
|
||||
|
||||
void USkillTreeWidget::Show()
|
||||
{
|
||||
m_shown = true;
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
void USkillTreeWidget::Hide()
|
||||
{
|
||||
m_shown = false;
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
void USkillTreeWidget::UpdateLevel(float level)
|
||||
{
|
||||
for (int32 i = 0; i < m_skillWidgets.Num(); i++)
|
||||
{
|
||||
m_skillWidgets[i]->UpdateLevel(level);
|
||||
}
|
||||
if (IsInteractable())
|
||||
{
|
||||
UpdateTextInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void USkillTreeWidget::UpdateInfo(float skilltreeHeight, float skilltreeOffset)
|
||||
{
|
||||
for (int32 i = 0; i < m_skillWidgets.Num(); i++)
|
||||
{
|
||||
m_skillWidgets[i]->UpdateInfo(skilltreeHeight, skilltreeOffset);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void USkillTreeWidget::NativeTick(const FGeometry& MyGeometry, float DeltaTime)
|
||||
{
|
||||
FVector2D minPos;
|
||||
FVector2D maxPos;
|
||||
FVector2D viewportPos;
|
||||
USlateBlueprintLibrary::LocalToViewport(this, MyGeometry, MyGeometry.GetLocalSize(), maxPos, viewportPos);
|
||||
USlateBlueprintLibrary::LocalToViewport(this, MyGeometry, FVector2D(0,0), minPos, viewportPos);
|
||||
FVector2D viewportSize = maxPos - minPos;
|
||||
for (int32 i = 0; i < m_skillWidgets.Num(); i++)
|
||||
{
|
||||
m_skillWidgets[i]->UpdateInfo(viewportSize.Y, minPos.Y);
|
||||
}
|
||||
}
|
||||
*/
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user