搬了lyra的hudlayout
This commit is contained in:
42
Plugins/CommonGame/CommonGame.uplugin
Normal file
42
Plugins/CommonGame/CommonGame.uplugin
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "1.0",
|
||||
"FriendlyName": "CommonGame",
|
||||
"Description": "Generic gameplay classes that use the other Common plugins.",
|
||||
"Category": "Gameplay",
|
||||
"CreatedBy": "Epic Games, Inc.",
|
||||
"CreatedByURL": "https://www.epicgames.com",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "",
|
||||
"CanContainContent": false,
|
||||
"IsBetaVersion": false,
|
||||
"Installed": false,
|
||||
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "CommonGame",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "CommonUI",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "CommonUser",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "ModularGameplayActors",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "OnlineFramework",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
43
Plugins/CommonGame/Source/CommonGame.Build.cs
Normal file
43
Plugins/CommonGame/Source/CommonGame.Build.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class CommonGame : ModuleRules
|
||||
{
|
||||
public CommonGame(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"InputCore",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"UMG",
|
||||
"CommonInput",
|
||||
"CommonUI",
|
||||
"CommonUser",
|
||||
"GameplayTags",
|
||||
"ModularGameplayActors",
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "Actions/AsyncAction_CreateWidgetAsync.h"
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Blueprint/WidgetBlueprintLibrary.h"
|
||||
#include "CommonUIExtensions.h"
|
||||
#include "Engine/AssetManager.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Engine/StreamableManager.h"
|
||||
#include "UObject/Stack.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(AsyncAction_CreateWidgetAsync)
|
||||
|
||||
class UUserWidget;
|
||||
|
||||
static const FName InputFilterReason_Template = FName(TEXT("CreatingWidgetAsync"));
|
||||
|
||||
UAsyncAction_CreateWidgetAsync::UAsyncAction_CreateWidgetAsync(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
, bSuspendInputUntilComplete(true)
|
||||
{
|
||||
}
|
||||
|
||||
UAsyncAction_CreateWidgetAsync* UAsyncAction_CreateWidgetAsync::CreateWidgetAsync(UObject* InWorldContextObject, TSoftClassPtr<UUserWidget> InUserWidgetSoftClass, APlayerController* InOwningPlayer, bool bSuspendInputUntilComplete)
|
||||
{
|
||||
if (InUserWidgetSoftClass.IsNull())
|
||||
{
|
||||
FFrame::KismetExecutionMessage(TEXT("CreateWidgetAsync was passed a null UserWidgetSoftClass"), ELogVerbosity::Error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UWorld* World = GEngine->GetWorldFromContextObject(InWorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
|
||||
|
||||
UAsyncAction_CreateWidgetAsync* Action = NewObject<UAsyncAction_CreateWidgetAsync>();
|
||||
Action->UserWidgetSoftClass = InUserWidgetSoftClass;
|
||||
Action->OwningPlayer = InOwningPlayer;
|
||||
Action->World = World;
|
||||
Action->GameInstance = World->GetGameInstance();
|
||||
Action->bSuspendInputUntilComplete = bSuspendInputUntilComplete;
|
||||
Action->RegisterWithGameInstance(World);
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
void UAsyncAction_CreateWidgetAsync::Activate()
|
||||
{
|
||||
SuspendInputToken = bSuspendInputUntilComplete ? UCommonUIExtensions::SuspendInputForPlayer(OwningPlayer.Get(), InputFilterReason_Template) : NAME_None;
|
||||
|
||||
TWeakObjectPtr<UAsyncAction_CreateWidgetAsync> LocalWeakThis(this);
|
||||
StreamingHandle = UAssetManager::Get().GetStreamableManager().RequestAsyncLoad(
|
||||
UserWidgetSoftClass.ToSoftObjectPath(),
|
||||
FStreamableDelegate::CreateUObject(this, &ThisClass::OnWidgetLoaded),
|
||||
FStreamableManager::AsyncLoadHighPriority
|
||||
);
|
||||
|
||||
// Setup a cancel delegate so that we can resume input if this handler is canceled.
|
||||
StreamingHandle->BindCancelDelegate(FStreamableDelegate::CreateWeakLambda(this,
|
||||
[this]()
|
||||
{
|
||||
UCommonUIExtensions::ResumeInputForPlayer(OwningPlayer.Get(), SuspendInputToken);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
void UAsyncAction_CreateWidgetAsync::Cancel()
|
||||
{
|
||||
Super::Cancel();
|
||||
|
||||
if (StreamingHandle.IsValid())
|
||||
{
|
||||
StreamingHandle->CancelHandle();
|
||||
StreamingHandle.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void UAsyncAction_CreateWidgetAsync::OnWidgetLoaded()
|
||||
{
|
||||
if (bSuspendInputUntilComplete)
|
||||
{
|
||||
UCommonUIExtensions::ResumeInputForPlayer(OwningPlayer.Get(), SuspendInputToken);
|
||||
}
|
||||
|
||||
// If the load as successful, create it, otherwise don't complete this.
|
||||
TSubclassOf<UUserWidget> UserWidgetClass = UserWidgetSoftClass.Get();
|
||||
if (UserWidgetClass)
|
||||
{
|
||||
UUserWidget* UserWidget = UWidgetBlueprintLibrary::Create(World.Get(), UserWidgetClass, OwningPlayer.Get());
|
||||
OnComplete.Broadcast(UserWidget);
|
||||
}
|
||||
|
||||
StreamingHandle.Reset();
|
||||
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "Actions/AsyncAction_PushContentToLayerForPlayer.h"
|
||||
|
||||
#include "Engine/Engine.h"
|
||||
#include "PrimaryGameLayout.h"
|
||||
#include "UObject/Stack.h"
|
||||
#include "Widgets/CommonActivatableWidgetContainer.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(AsyncAction_PushContentToLayerForPlayer)
|
||||
|
||||
UAsyncAction_PushContentToLayerForPlayer::UAsyncAction_PushContentToLayerForPlayer(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
UAsyncAction_PushContentToLayerForPlayer* UAsyncAction_PushContentToLayerForPlayer::PushContentToLayerForPlayer(APlayerController* InOwningPlayer, TSoftClassPtr<UCommonActivatableWidget> InWidgetClass, FGameplayTag InLayerName, bool bSuspendInputUntilComplete)
|
||||
{
|
||||
if (InWidgetClass.IsNull())
|
||||
{
|
||||
FFrame::KismetExecutionMessage(TEXT("PushContentToLayerForPlayer was passed a null WidgetClass"), ELogVerbosity::Error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (UWorld* World = GEngine->GetWorldFromContextObject(InOwningPlayer, EGetWorldErrorMode::LogAndReturnNull))
|
||||
{
|
||||
UAsyncAction_PushContentToLayerForPlayer* Action = NewObject<UAsyncAction_PushContentToLayerForPlayer>();
|
||||
Action->WidgetClass = InWidgetClass;
|
||||
Action->OwningPlayerPtr = InOwningPlayer;
|
||||
Action->LayerName = InLayerName;
|
||||
Action->bSuspendInputUntilComplete = bSuspendInputUntilComplete;
|
||||
Action->RegisterWithGameInstance(World);
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UAsyncAction_PushContentToLayerForPlayer::Cancel()
|
||||
{
|
||||
Super::Cancel();
|
||||
|
||||
if (StreamingHandle.IsValid())
|
||||
{
|
||||
StreamingHandle->CancelHandle();
|
||||
StreamingHandle.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void UAsyncAction_PushContentToLayerForPlayer::Activate()
|
||||
{
|
||||
if (UPrimaryGameLayout* RootLayout = UPrimaryGameLayout::GetPrimaryGameLayout(OwningPlayerPtr.Get()))
|
||||
{
|
||||
TWeakObjectPtr<UAsyncAction_PushContentToLayerForPlayer> WeakThis = this;
|
||||
StreamingHandle = RootLayout->PushWidgetToLayerStackAsync<UCommonActivatableWidget>(LayerName, bSuspendInputUntilComplete, WidgetClass, [this, WeakThis](EAsyncWidgetLayerState State, UCommonActivatableWidget* Widget) {
|
||||
if (WeakThis.IsValid())
|
||||
{
|
||||
switch (State)
|
||||
{
|
||||
case EAsyncWidgetLayerState::Initialize:
|
||||
BeforePush.Broadcast(Widget);
|
||||
break;
|
||||
case EAsyncWidgetLayerState::AfterPush:
|
||||
AfterPush.Broadcast(Widget);
|
||||
SetReadyToDestroy();
|
||||
break;
|
||||
case EAsyncWidgetLayerState::Canceled:
|
||||
SetReadyToDestroy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
SetReadyToDestroy();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "Actions/AsyncAction_ShowConfirmation.h"
|
||||
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Messaging/CommonGameDialog.h"
|
||||
#include "Messaging/CommonMessagingSubsystem.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(AsyncAction_ShowConfirmation)
|
||||
|
||||
UAsyncAction_ShowConfirmation::UAsyncAction_ShowConfirmation(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
UAsyncAction_ShowConfirmation* UAsyncAction_ShowConfirmation::ShowConfirmationYesNo(UObject* InWorldContextObject, FText Title, FText Message)
|
||||
{
|
||||
UAsyncAction_ShowConfirmation* Action = NewObject<UAsyncAction_ShowConfirmation>();
|
||||
Action->WorldContextObject = InWorldContextObject;
|
||||
Action->Descriptor = UCommonGameDialogDescriptor::CreateConfirmationYesNo(Title, Message);
|
||||
Action->RegisterWithGameInstance(InWorldContextObject);
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
UAsyncAction_ShowConfirmation* UAsyncAction_ShowConfirmation::ShowConfirmationOkCancel(UObject* InWorldContextObject, FText Title, FText Message)
|
||||
{
|
||||
UAsyncAction_ShowConfirmation* Action = NewObject<UAsyncAction_ShowConfirmation>();
|
||||
Action->WorldContextObject = InWorldContextObject;
|
||||
Action->Descriptor = UCommonGameDialogDescriptor::CreateConfirmationOkCancel(Title, Message);
|
||||
Action->RegisterWithGameInstance(InWorldContextObject);
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
UAsyncAction_ShowConfirmation* UAsyncAction_ShowConfirmation::ShowConfirmationCustom(UObject* InWorldContextObject, UCommonGameDialogDescriptor* Descriptor)
|
||||
{
|
||||
UAsyncAction_ShowConfirmation* Action = NewObject<UAsyncAction_ShowConfirmation>();
|
||||
Action->WorldContextObject = InWorldContextObject;
|
||||
Action->Descriptor = Descriptor;
|
||||
Action->RegisterWithGameInstance(InWorldContextObject);
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
void UAsyncAction_ShowConfirmation::Activate()
|
||||
{
|
||||
if (WorldContextObject && !TargetLocalPlayer)
|
||||
{
|
||||
if (UUserWidget* UserWidget = Cast<UUserWidget>(WorldContextObject))
|
||||
{
|
||||
TargetLocalPlayer = UserWidget->GetOwningLocalPlayer<ULocalPlayer>();
|
||||
}
|
||||
else if (APlayerController* PC = Cast<APlayerController>(WorldContextObject))
|
||||
{
|
||||
TargetLocalPlayer = PC->GetLocalPlayer();
|
||||
}
|
||||
else if (UWorld* World = WorldContextObject->GetWorld())
|
||||
{
|
||||
if (UGameInstance* GameInstance = World->GetGameInstance<UGameInstance>())
|
||||
{
|
||||
TargetLocalPlayer = GameInstance->GetPrimaryPlayerController(false)->GetLocalPlayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TargetLocalPlayer)
|
||||
{
|
||||
if (UCommonMessagingSubsystem* Messaging = TargetLocalPlayer->GetSubsystem<UCommonMessagingSubsystem>())
|
||||
{
|
||||
FCommonMessagingResultDelegate ResultCallback = FCommonMessagingResultDelegate::CreateUObject(this, &UAsyncAction_ShowConfirmation::HandleConfirmationResult);
|
||||
Messaging->ShowConfirmation(Descriptor, ResultCallback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we couldn't make the confirmation, just handle an unknown result and broadcast nothing
|
||||
HandleConfirmationResult(ECommonMessagingResult::Unknown);
|
||||
}
|
||||
|
||||
void UAsyncAction_ShowConfirmation::HandleConfirmationResult(ECommonMessagingResult ConfirmationResult)
|
||||
{
|
||||
OnResult.Broadcast(ConfirmationResult);
|
||||
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
|
||||
194
Plugins/CommonGame/Source/Private/CommonGameInstance.cpp
Normal file
194
Plugins/CommonGame/Source/Private/CommonGameInstance.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "CommonGameInstance.h"
|
||||
|
||||
#include "CommonLocalPlayer.h"
|
||||
#include "CommonSessionSubsystem.h"
|
||||
#include "CommonUISettings.h"
|
||||
#include "CommonUserSubsystem.h"
|
||||
#include "GameUIManagerSubsystem.h"
|
||||
#include "ICommonUIModule.h"
|
||||
#include "LogCommonGame.h"
|
||||
#include "Messaging/CommonGameDialog.h"
|
||||
#include "Messaging/CommonMessagingSubsystem.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonGameInstance)
|
||||
|
||||
UCommonGameInstance::UCommonGameInstance(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UCommonGameInstance::HandleSystemMessage(FGameplayTag MessageType, FText Title, FText Message)
|
||||
{
|
||||
ULocalPlayer* FirstPlayer = GetFirstGamePlayer();
|
||||
// Forward severe ones to the error dialog for the first player
|
||||
if (FirstPlayer && MessageType.MatchesTag(FCommonUserTags::SystemMessage_Error))
|
||||
{
|
||||
if (UCommonMessagingSubsystem* Messaging = FirstPlayer->GetSubsystem<UCommonMessagingSubsystem>())
|
||||
{
|
||||
Messaging->ShowError(UCommonGameDialogDescriptor::CreateConfirmationOk(Title, Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonGameInstance::HandlePrivilegeChanged(const UCommonUserInfo* UserInfo, ECommonUserPrivilege Privilege, ECommonUserAvailability OldAvailability, ECommonUserAvailability NewAvailability)
|
||||
{
|
||||
// By default show errors and disconnect if play privilege for first player is lost
|
||||
if (Privilege == ECommonUserPrivilege::CanPlay && OldAvailability == ECommonUserAvailability::NowAvailable && NewAvailability != ECommonUserAvailability::NowAvailable)
|
||||
{
|
||||
UE_LOG(LogCommonGame, Error, TEXT("HandlePrivilegeChanged: Player %d no longer has permission to play the game!"), UserInfo->LocalPlayerIndex);
|
||||
// TODO: Games can do something specific in subclass
|
||||
// ReturnToMainMenu();
|
||||
}
|
||||
}
|
||||
|
||||
int32 UCommonGameInstance::AddLocalPlayer(ULocalPlayer* NewPlayer, FPlatformUserId UserId)
|
||||
{
|
||||
int32 ReturnVal = Super::AddLocalPlayer(NewPlayer, UserId);
|
||||
if (ReturnVal != INDEX_NONE)
|
||||
{
|
||||
if (!PrimaryPlayer.IsValid())
|
||||
{
|
||||
UE_LOG(LogCommonGame, Log, TEXT("AddLocalPlayer: Set %s to Primary Player"), *NewPlayer->GetName());
|
||||
PrimaryPlayer = NewPlayer;
|
||||
}
|
||||
|
||||
GetSubsystem<UGameUIManagerSubsystem>()->NotifyPlayerAdded(Cast<UCommonLocalPlayer>(NewPlayer));
|
||||
}
|
||||
|
||||
return ReturnVal;
|
||||
}
|
||||
|
||||
bool UCommonGameInstance::RemoveLocalPlayer(ULocalPlayer* ExistingPlayer)
|
||||
{
|
||||
if (PrimaryPlayer == ExistingPlayer)
|
||||
{
|
||||
//TODO: do we want to fall back to another player?
|
||||
PrimaryPlayer.Reset();
|
||||
UE_LOG(LogCommonGame, Log, TEXT("RemoveLocalPlayer: Unsetting Primary Player from %s"), *ExistingPlayer->GetName());
|
||||
}
|
||||
GetSubsystem<UGameUIManagerSubsystem>()->NotifyPlayerDestroyed(Cast<UCommonLocalPlayer>(ExistingPlayer));
|
||||
|
||||
return Super::RemoveLocalPlayer(ExistingPlayer);
|
||||
}
|
||||
|
||||
void UCommonGameInstance::Init()
|
||||
{
|
||||
Super::Init();
|
||||
|
||||
// After subsystems are initialized, hook them together
|
||||
FGameplayTagContainer PlatformTraits = ICommonUIModule::GetSettings().GetPlatformTraits();
|
||||
|
||||
UCommonUserSubsystem* UserSubsystem = GetSubsystem<UCommonUserSubsystem>();
|
||||
if (ensure(UserSubsystem))
|
||||
{
|
||||
UserSubsystem->SetTraitTags(PlatformTraits);
|
||||
UserSubsystem->OnHandleSystemMessage.AddDynamic(this, &UCommonGameInstance::HandleSystemMessage);
|
||||
UserSubsystem->OnUserPrivilegeChanged.AddDynamic(this, &UCommonGameInstance::HandlePrivilegeChanged);
|
||||
}
|
||||
|
||||
UCommonSessionSubsystem* SessionSubsystem = GetSubsystem<UCommonSessionSubsystem>();
|
||||
if (ensure(SessionSubsystem))
|
||||
{
|
||||
SessionSubsystem->OnUserRequestedSessionEvent.AddUObject(this, &UCommonGameInstance::OnUserRequestedSession);
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonGameInstance::ResetUserAndSessionState()
|
||||
{
|
||||
UCommonUserSubsystem* UserSubsystem = GetSubsystem<UCommonUserSubsystem>();
|
||||
if (ensure(UserSubsystem))
|
||||
{
|
||||
UserSubsystem->ResetUserState();
|
||||
}
|
||||
|
||||
UCommonSessionSubsystem* SessionSubsystem = GetSubsystem<UCommonSessionSubsystem>();
|
||||
if (ensure(SessionSubsystem))
|
||||
{
|
||||
SessionSubsystem->CleanUpSessions();
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonGameInstance::ReturnToMainMenu()
|
||||
{
|
||||
// By default when returning to main menu we should reset everything
|
||||
ResetUserAndSessionState();
|
||||
|
||||
Super::ReturnToMainMenu();
|
||||
}
|
||||
|
||||
void UCommonGameInstance::OnUserRequestedSession(const FPlatformUserId& PlatformUserId, UCommonSession_SearchResult* InRequestedSession, const FOnlineResultInformation& RequestedSessionResult)
|
||||
{
|
||||
if (InRequestedSession)
|
||||
{
|
||||
SetRequestedSession(InRequestedSession);
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleSystemMessage(FCommonUserTags::SystemMessage_Error, NSLOCTEXT("CommonGame", "Warning_RequestedSessionFailed", "Requested Session Failed"), RequestedSessionResult.ErrorText);
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonGameInstance::SetRequestedSession(UCommonSession_SearchResult* InRequestedSession)
|
||||
{
|
||||
RequestedSession = InRequestedSession;
|
||||
if (RequestedSession)
|
||||
{
|
||||
if (CanJoinRequestedSession())
|
||||
{
|
||||
JoinRequestedSession();
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetGameAndJoinRequestedSession();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UCommonGameInstance::CanJoinRequestedSession() const
|
||||
{
|
||||
// Default behavior is always allow joining the requested session
|
||||
return true;
|
||||
}
|
||||
|
||||
void UCommonGameInstance::JoinRequestedSession()
|
||||
{
|
||||
if (RequestedSession)
|
||||
{
|
||||
if (ULocalPlayer* const FirstPlayer = GetFirstGamePlayer())
|
||||
{
|
||||
UCommonSessionSubsystem* SessionSubsystem = GetSubsystem<UCommonSessionSubsystem>();
|
||||
if (ensure(SessionSubsystem))
|
||||
{
|
||||
// Clear our current requested session since we are now acting on it.
|
||||
UCommonSession_SearchResult* LocalRequestedSession = RequestedSession;
|
||||
RequestedSession = nullptr;
|
||||
SessionSubsystem->JoinSession(FirstPlayer->PlayerController, LocalRequestedSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonGameInstance::ResetGameAndJoinRequestedSession()
|
||||
{
|
||||
// Default behavior is to return to the main menu. The game must call JoinRequestedSession when the game is in a ready state.
|
||||
ReturnToMainMenu();
|
||||
}
|
||||
|
||||
|
||||
//void UCommonGameInstance::OnPreLoadMap(const FString& MapName)
|
||||
//{
|
||||
// if (!IsDedicatedServerInstance())
|
||||
// {
|
||||
// if (!bWasInLoadMap)
|
||||
// {
|
||||
// UGameUIManagerSubsystem* UIManager = GetSubsystem<UGameUIManagerSubsystem>();
|
||||
// for (ULocalPlayer* LocalPlayer : LocalPlayers)
|
||||
// {
|
||||
// UIManager->NotifyPlayerAdded(Cast<UCommonLocalPlayer>(LocalPlayer));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
32
Plugins/CommonGame/Source/Private/CommonGameModule.cpp
Normal file
32
Plugins/CommonGame/Source/Private/CommonGameModule.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
/**
|
||||
* Implements the FCommonGameModule module.
|
||||
*/
|
||||
class FCommonGameModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
FCommonGameModule();
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
|
||||
FCommonGameModule::FCommonGameModule()
|
||||
{
|
||||
}
|
||||
|
||||
void FCommonGameModule::StartupModule()
|
||||
{
|
||||
}
|
||||
|
||||
void FCommonGameModule::ShutdownModule()
|
||||
{
|
||||
}
|
||||
|
||||
IMPLEMENT_MODULE(FCommonGameModule, CommonGame);
|
||||
81
Plugins/CommonGame/Source/Private/CommonLocalPlayer.cpp
Normal file
81
Plugins/CommonGame/Source/Private/CommonLocalPlayer.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "CommonLocalPlayer.h"
|
||||
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GameUIManagerSubsystem.h"
|
||||
#include "GameUIPolicy.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonLocalPlayer)
|
||||
|
||||
class APawn;
|
||||
class APlayerState;
|
||||
class FViewport;
|
||||
struct FSceneViewProjectionData;
|
||||
|
||||
UCommonLocalPlayer::UCommonLocalPlayer()
|
||||
: Super(FObjectInitializer::Get())
|
||||
{
|
||||
}
|
||||
|
||||
FDelegateHandle UCommonLocalPlayer::CallAndRegister_OnPlayerControllerSet(FPlayerControllerSetDelegate::FDelegate Delegate)
|
||||
{
|
||||
APlayerController* PC = GetPlayerController(GetWorld());
|
||||
|
||||
if (PC)
|
||||
{
|
||||
Delegate.Execute(this, PC);
|
||||
}
|
||||
|
||||
return OnPlayerControllerSet.Add(Delegate);
|
||||
}
|
||||
|
||||
FDelegateHandle UCommonLocalPlayer::CallAndRegister_OnPlayerStateSet(FPlayerStateSetDelegate::FDelegate Delegate)
|
||||
{
|
||||
APlayerController* PC = GetPlayerController(GetWorld());
|
||||
APlayerState* PlayerState = PC ? PC->PlayerState : nullptr;
|
||||
|
||||
if (PlayerState)
|
||||
{
|
||||
Delegate.Execute(this, PlayerState);
|
||||
}
|
||||
|
||||
return OnPlayerStateSet.Add(Delegate);
|
||||
}
|
||||
|
||||
FDelegateHandle UCommonLocalPlayer::CallAndRegister_OnPlayerPawnSet(FPlayerPawnSetDelegate::FDelegate Delegate)
|
||||
{
|
||||
APlayerController* PC = GetPlayerController(GetWorld());
|
||||
APawn* Pawn = PC ? PC->GetPawn() : nullptr;
|
||||
|
||||
if (Pawn)
|
||||
{
|
||||
Delegate.Execute(this, Pawn);
|
||||
}
|
||||
|
||||
return OnPlayerPawnSet.Add(Delegate);
|
||||
}
|
||||
|
||||
bool UCommonLocalPlayer::GetProjectionData(FViewport* Viewport, FSceneViewProjectionData& ProjectionData, int32 StereoViewIndex) const
|
||||
{
|
||||
if (!bIsPlayerViewEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Super::GetProjectionData(Viewport, ProjectionData, StereoViewIndex);
|
||||
}
|
||||
|
||||
UPrimaryGameLayout* UCommonLocalPlayer::GetRootUILayout() const
|
||||
{
|
||||
if (UGameUIManagerSubsystem* UIManager = GetGameInstance()->GetSubsystem<UGameUIManagerSubsystem>())
|
||||
{
|
||||
if (UGameUIPolicy* Policy = UIManager->GetCurrentUIPolicy())
|
||||
{
|
||||
return Policy->GetRootLayout(this);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
72
Plugins/CommonGame/Source/Private/CommonPlayerController.cpp
Normal file
72
Plugins/CommonGame/Source/Private/CommonPlayerController.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "CommonPlayerController.h"
|
||||
|
||||
#include "CommonLocalPlayer.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonPlayerController)
|
||||
|
||||
class APawn;
|
||||
|
||||
ACommonPlayerController::ACommonPlayerController(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
void ACommonPlayerController::ReceivedPlayer()
|
||||
{
|
||||
Super::ReceivedPlayer();
|
||||
|
||||
if (UCommonLocalPlayer* LocalPlayer = Cast<UCommonLocalPlayer>(Player))
|
||||
{
|
||||
LocalPlayer->OnPlayerControllerSet.Broadcast(LocalPlayer, this);
|
||||
|
||||
if (PlayerState)
|
||||
{
|
||||
LocalPlayer->OnPlayerStateSet.Broadcast(LocalPlayer, PlayerState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ACommonPlayerController::SetPawn(APawn* InPawn)
|
||||
{
|
||||
Super::SetPawn(InPawn);
|
||||
|
||||
if (UCommonLocalPlayer* LocalPlayer = Cast<UCommonLocalPlayer>(Player))
|
||||
{
|
||||
LocalPlayer->OnPlayerPawnSet.Broadcast(LocalPlayer, InPawn);
|
||||
}
|
||||
}
|
||||
|
||||
void ACommonPlayerController::OnPossess(APawn* APawn)
|
||||
{
|
||||
Super::OnPossess(APawn);
|
||||
|
||||
if (UCommonLocalPlayer* LocalPlayer = Cast<UCommonLocalPlayer>(Player))
|
||||
{
|
||||
LocalPlayer->OnPlayerPawnSet.Broadcast(LocalPlayer, APawn);
|
||||
}
|
||||
}
|
||||
|
||||
void ACommonPlayerController::OnUnPossess()
|
||||
{
|
||||
Super::OnUnPossess();
|
||||
|
||||
if (UCommonLocalPlayer* LocalPlayer = Cast<UCommonLocalPlayer>(Player))
|
||||
{
|
||||
LocalPlayer->OnPlayerPawnSet.Broadcast(LocalPlayer, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void ACommonPlayerController::OnRep_PlayerState()
|
||||
{
|
||||
Super::OnRep_PlayerState();
|
||||
|
||||
if (PlayerState)
|
||||
{
|
||||
if (UCommonLocalPlayer* LocalPlayer = Cast<UCommonLocalPlayer>(Player))
|
||||
{
|
||||
LocalPlayer->OnPlayerStateSet.Broadcast(LocalPlayer, PlayerState);
|
||||
}
|
||||
}
|
||||
}
|
||||
543
Plugins/CommonGame/Source/Private/CommonPlayerInputKey.cpp
Normal file
543
Plugins/CommonGame/Source/Private/CommonPlayerInputKey.cpp
Normal file
@@ -0,0 +1,543 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "CommonPlayerInputKey.h"
|
||||
|
||||
#include "CommonInputSubsystem.h"
|
||||
#include "CommonInputTypeEnum.h"
|
||||
#include "CommonLocalPlayer.h"
|
||||
#include "CommonPlayerController.h"
|
||||
#include "Fonts/FontMeasure.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialInstanceDynamic.h"
|
||||
#include "Rendering/SlateRenderer.h"
|
||||
#include "TimerManager.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonPlayerInputKey)
|
||||
|
||||
class FPaintArgs;
|
||||
class FSlateRect;
|
||||
|
||||
#define LOCTEXT_NAMESPACE "CommonKeybindWidget"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogCommonPlayerInput, Log, All);
|
||||
DEFINE_LOG_CATEGORY(LogCommonPlayerInput);
|
||||
|
||||
struct FSlateDrawUtil
|
||||
{
|
||||
static void DrawBrushCenterFit(
|
||||
FSlateWindowElementList& ElementList,
|
||||
uint32 InLayer,
|
||||
const FGeometry& InAllottedGeometry,
|
||||
const FSlateBrush* InBrush,
|
||||
const FLinearColor& InTint = FLinearColor::White)
|
||||
{
|
||||
DrawBrushCenterFitWithOffset
|
||||
(
|
||||
ElementList,
|
||||
InLayer,
|
||||
InAllottedGeometry,
|
||||
InBrush,
|
||||
InTint,
|
||||
FVector2D(0, 0)
|
||||
);
|
||||
}
|
||||
|
||||
static void DrawBrushCenterFitWithOffset(
|
||||
FSlateWindowElementList& ElementList,
|
||||
uint32 InLayer,
|
||||
const FGeometry& InAllottedGeometry,
|
||||
const FSlateBrush* InBrush,
|
||||
const FLinearColor& InTint,
|
||||
const FVector2D InOffset)
|
||||
{
|
||||
if (!InBrush)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const FVector2D AreaSize = InAllottedGeometry.GetLocalSize();
|
||||
const FVector2D ProgressSize = InBrush->GetImageSize();
|
||||
const float FitScale = FMath::Min(FMath::Min(AreaSize.X / ProgressSize.X, AreaSize.Y / ProgressSize.Y), 1.0f);
|
||||
const FVector2D FinalSize = FitScale * ProgressSize;
|
||||
|
||||
const FVector2D Offset = (InAllottedGeometry.GetLocalSize() * 0.5f) - (FinalSize * 0.5f) + InOffset;
|
||||
|
||||
FSlateDrawElement::MakeBox
|
||||
(
|
||||
ElementList,
|
||||
InLayer,
|
||||
InAllottedGeometry.ToPaintGeometry(FinalSize, FSlateLayoutTransform(Offset)),
|
||||
InBrush,
|
||||
ESlateDrawEffect::None,
|
||||
InTint
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void FMeasuredText::SetText(const FText& InText)
|
||||
{
|
||||
CachedText = InText;
|
||||
bTextDirty = true;
|
||||
}
|
||||
|
||||
FVector2D FMeasuredText::UpdateTextSize(const FSlateFontInfo &InFontInfo, float FontScale) const
|
||||
{
|
||||
if (bTextDirty)
|
||||
{
|
||||
bTextDirty = false;
|
||||
CachedTextSize = FSlateApplication::Get().GetRenderer()->GetFontMeasureService()->Measure(CachedText, InFontInfo, FontScale);
|
||||
}
|
||||
|
||||
return CachedTextSize;
|
||||
}
|
||||
|
||||
UCommonPlayerInputKey::UCommonPlayerInputKey(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
, BoundKeyFallback(EKeys::Invalid)
|
||||
, InputTypeOverride(ECommonInputType::Count)
|
||||
{
|
||||
FrameSize = FVector2D(0, 0);
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::NativePreConstruct()
|
||||
{
|
||||
Super::NativePreConstruct();
|
||||
|
||||
UpdateKeybindWidget();
|
||||
|
||||
if (IsDesignTime())
|
||||
{
|
||||
ShowHoldBackPlate();
|
||||
RecalculateDesiredSize();
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::NativeDestruct()
|
||||
{
|
||||
if (ProgressPercentageMID)
|
||||
{
|
||||
// Need to restore the material on the brush before we kill off the MID.
|
||||
HoldProgressBrush.SetResourceObject(ProgressPercentageMID->GetMaterial());
|
||||
|
||||
ProgressPercentageMID->MarkAsGarbage();
|
||||
ProgressPercentageMID = nullptr;
|
||||
}
|
||||
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
|
||||
int32 UCommonPlayerInputKey::NativePaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
|
||||
{
|
||||
int32 MaxLayer = Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
|
||||
|
||||
if (bDrawProgress)
|
||||
{
|
||||
FSlateDrawUtil::DrawBrushCenterFit
|
||||
(
|
||||
OutDrawElements,
|
||||
++MaxLayer,
|
||||
AllottedGeometry,
|
||||
&HoldProgressBrush,
|
||||
FLinearColor(InWidgetStyle.GetColorAndOpacityTint() * HoldProgressBrush.GetTint(InWidgetStyle))
|
||||
);
|
||||
}
|
||||
|
||||
if (bDrawCountdownText)
|
||||
{
|
||||
const FVector2D CountdownTextOffset = (AllottedGeometry.GetLocalSize() - CountdownText.GetTextSize()) * 0.5f;
|
||||
|
||||
FSlateDrawElement::MakeText
|
||||
(
|
||||
OutDrawElements,
|
||||
++MaxLayer,
|
||||
AllottedGeometry.ToOffsetPaintGeometry(CountdownTextOffset),
|
||||
CountdownText.GetText(),
|
||||
CountdownTextFont,
|
||||
ESlateDrawEffect::None,
|
||||
FLinearColor(InWidgetStyle.GetColorAndOpacityTint())
|
||||
);
|
||||
}
|
||||
else if (bDrawBrushForKey)
|
||||
{
|
||||
// Draw Shadow
|
||||
FSlateDrawUtil::DrawBrushCenterFitWithOffset
|
||||
(
|
||||
OutDrawElements,
|
||||
++MaxLayer,
|
||||
AllottedGeometry,
|
||||
&CachedKeyBrush,
|
||||
FLinearColor(InWidgetStyle.GetColorAndOpacityTint() * FLinearColor::Black),
|
||||
FVector2D(1, 1)
|
||||
);
|
||||
|
||||
FSlateDrawUtil::DrawBrushCenterFit
|
||||
(
|
||||
OutDrawElements,
|
||||
++MaxLayer,
|
||||
AllottedGeometry,
|
||||
&CachedKeyBrush,
|
||||
FLinearColor(InWidgetStyle.GetColorAndOpacityTint() * CachedKeyBrush.GetTint(InWidgetStyle))
|
||||
);
|
||||
}
|
||||
else if (KeybindText.GetTextSize().X > 0)
|
||||
{
|
||||
const FVector2D FrameOffset = (AllottedGeometry.GetLocalSize() - FrameSize) * 0.5f;
|
||||
|
||||
FSlateDrawElement::MakeBox
|
||||
(
|
||||
OutDrawElements,
|
||||
++MaxLayer,
|
||||
AllottedGeometry.ToPaintGeometry(FrameSize, FSlateLayoutTransform(FrameOffset)),
|
||||
&KeyBindTextBorder,
|
||||
ESlateDrawEffect::None,
|
||||
FLinearColor(InWidgetStyle.GetColorAndOpacityTint() * KeyBindTextBorder.GetTint(InWidgetStyle))
|
||||
);
|
||||
|
||||
const FVector2D ActionTextOffset = (AllottedGeometry.GetLocalSize() - KeybindText.GetTextSize()) * 0.5f;
|
||||
|
||||
FSlateDrawElement::MakeText
|
||||
(
|
||||
OutDrawElements,
|
||||
++MaxLayer,
|
||||
AllottedGeometry.ToOffsetPaintGeometry(ActionTextOffset),
|
||||
KeybindText.GetText(),
|
||||
KeyBindTextFont,
|
||||
ESlateDrawEffect::None,
|
||||
FLinearColor(InWidgetStyle.GetColorAndOpacityTint())
|
||||
);
|
||||
}
|
||||
|
||||
return MaxLayer;
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::StartHoldProgress(FName HoldActionName, float HoldDuration)
|
||||
{
|
||||
if (HoldActionName == BoundAction && ensureMsgf(HoldDuration > 0.0f, TEXT("Trying to perform hold action \"%s\" with no HoldDuration"), *BoundAction.ToString()))
|
||||
{
|
||||
HoldKeybindDuration = HoldDuration;
|
||||
HoldKeybindStartTime = GetWorld()->GetRealTimeSeconds();
|
||||
|
||||
UpdateHoldProgress();
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::StopHoldProgress(FName HoldActionName, bool bCompletedSuccessfully)
|
||||
{
|
||||
if (HoldActionName == BoundAction)
|
||||
{
|
||||
HoldKeybindStartTime = 0.f;
|
||||
HoldKeybindDuration = 0.f;
|
||||
|
||||
if (ensure(ProgressPercentageMID))
|
||||
{
|
||||
ProgressPercentageMID->SetScalarParameterValue(PercentageMaterialParameterName, 0.f);
|
||||
}
|
||||
|
||||
if (bDrawCountdownText)
|
||||
{
|
||||
bDrawCountdownText = false;
|
||||
Invalidate(EInvalidateWidget::Paint);
|
||||
RecalculateDesiredSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::SyncHoldProgress()
|
||||
{
|
||||
// If we had an active hold action, stop it
|
||||
if (HoldKeybindStartTime > 0.f)
|
||||
{
|
||||
StopHoldProgress(BoundAction, false);
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::UpdateHoldProgress()
|
||||
{
|
||||
if (HoldKeybindStartTime != 0.f && HoldKeybindDuration > 0.f)
|
||||
{
|
||||
const float CurrentTime = GetWorld()->GetRealTimeSeconds();
|
||||
const float ElapsedTime = FMath::Min(CurrentTime - HoldKeybindStartTime, HoldKeybindDuration);
|
||||
const float RemainingTime = FMath::Max(0.0f, HoldKeybindDuration - ElapsedTime);
|
||||
|
||||
if (ElapsedTime < HoldKeybindDuration && ensure(ProgressPercentageMID))
|
||||
{
|
||||
const float HoldKeybindPercentage = ElapsedTime / HoldKeybindDuration;
|
||||
ProgressPercentageMID->SetScalarParameterValue(PercentageMaterialParameterName, HoldKeybindPercentage);
|
||||
|
||||
// Schedule a callback for next tick to update the hold progress again.
|
||||
GetWorld()->GetTimerManager().SetTimerForNextTick(this, &ThisClass::UpdateHoldProgress);
|
||||
}
|
||||
|
||||
if (bShowTimeCountDown)
|
||||
{
|
||||
FNumberFormattingOptions Options;
|
||||
Options.MinimumFractionalDigits = 1;
|
||||
Options.MaximumFractionalDigits = 1;
|
||||
CountdownText.SetText(FText::AsNumber(RemainingTime, &Options));
|
||||
|
||||
bDrawCountdownText = true;
|
||||
Invalidate(EInvalidateWidget::Paint);
|
||||
RecalculateDesiredSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::UpdateKeybindWidget()
|
||||
{
|
||||
if (!GetOwningPlayer<ACommonPlayerController>())
|
||||
{
|
||||
bWaitingForPlayerController = true;
|
||||
return;
|
||||
}
|
||||
|
||||
UCommonInputSubsystem* CommonInputSubsystem = GetInputSubsystem();
|
||||
|
||||
if (CommonInputSubsystem && !CommonInputSubsystem->ShouldShowInputKeys())
|
||||
{
|
||||
SetVisibility(ESlateVisibility::Collapsed);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool bIsUsingGamepad = (InputTypeOverride == ECommonInputType::Gamepad) || ((CommonInputSubsystem != nullptr) && (CommonInputSubsystem->GetCurrentInputType() == ECommonInputType::Gamepad)) ;
|
||||
|
||||
if (!BoundKey.IsValid())
|
||||
{
|
||||
BoundKey = BoundKeyFallback;
|
||||
}
|
||||
UE_LOG(LogCommonPlayerInput, Verbose, TEXT("UCommonKeybindWidget::UpdateKeybindWidget: Action: %s Key: %s"), *(BoundAction.ToString()), *(BoundKey.ToString()));
|
||||
|
||||
// Must be called before Update, due to the creation of ProgressPercentageMID which will be used in Update
|
||||
SetupHoldKeybind();
|
||||
|
||||
bool NewDrawBrushForKey = false;
|
||||
bool NeedToRecalcSize = false;
|
||||
|
||||
if (BoundKey.IsValid())
|
||||
{
|
||||
SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
|
||||
ShowHoldBackPlate();
|
||||
|
||||
NeedToRecalcSize = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bShowUnboundStatus)
|
||||
{
|
||||
SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
NewDrawBrushForKey = false;
|
||||
|
||||
KeybindText.SetText(LOCTEXT("Unbound", "Unbound"));
|
||||
|
||||
NeedToRecalcSize = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
if (bDrawBrushForKey != NewDrawBrushForKey)
|
||||
{
|
||||
bDrawBrushForKey = NewDrawBrushForKey;
|
||||
Invalidate(EInvalidateWidget::Paint);
|
||||
}
|
||||
|
||||
// As RecalculateDesiredSize relies on the bDrawBrushForKey
|
||||
// we shouldn't call it until that value has been finalized
|
||||
// for the update
|
||||
if (NeedToRecalcSize)
|
||||
{
|
||||
RecalculateDesiredSize();
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::SetBoundKey(FKey NewKey)
|
||||
{
|
||||
if (NewKey != BoundKey)
|
||||
{
|
||||
BoundKeyFallback = NewKey;
|
||||
BoundAction = NAME_None;
|
||||
UpdateKeybindWidget();
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::SetBoundAction(FName NewBoundAction)
|
||||
{
|
||||
bool bUpdateWidget = true;
|
||||
|
||||
if (BoundAction != NewBoundAction)
|
||||
{
|
||||
BoundAction = NewBoundAction;
|
||||
}
|
||||
|
||||
if (bUpdateWidget)
|
||||
{
|
||||
UpdateKeybindWidget();
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::NativeOnInitialized()
|
||||
{
|
||||
Super::NativeOnInitialized();
|
||||
|
||||
if (UCommonLocalPlayer* CommonLocalPlayer = GetOwningLocalPlayer<UCommonLocalPlayer>())
|
||||
{
|
||||
CommonLocalPlayer->OnPlayerControllerSet.AddUObject(this, &ThisClass::HandlePlayerControllerSet);
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::SetForcedHoldKeybind(bool InForcedHoldKeybind)
|
||||
{
|
||||
if (InForcedHoldKeybind)
|
||||
{
|
||||
SetForcedHoldKeybindStatus(ECommonKeybindForcedHoldStatus::ForcedHold);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetForcedHoldKeybindStatus(ECommonKeybindForcedHoldStatus::NoForcedHold);
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::SetForcedHoldKeybindStatus(ECommonKeybindForcedHoldStatus InForcedHoldKeybindStatus)
|
||||
{
|
||||
ForcedHoldKeybindStatus = InForcedHoldKeybindStatus;
|
||||
|
||||
UpdateKeybindWidget();
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::SetShowProgressCountDown(bool bShow)
|
||||
{
|
||||
bShowTimeCountDown = bShow;
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::SetupHoldKeybind()
|
||||
{
|
||||
ACommonPlayerController* OwningCommonPlayer = Cast<ACommonPlayerController>(GetOwningPlayer());
|
||||
|
||||
// Setup the hold
|
||||
if (ForcedHoldKeybindStatus == ECommonKeybindForcedHoldStatus::ForcedHold)
|
||||
{
|
||||
bIsHoldKeybind = true;
|
||||
}
|
||||
else if (ForcedHoldKeybindStatus == ECommonKeybindForcedHoldStatus::NeverShowHold)
|
||||
{
|
||||
bIsHoldKeybind = false;
|
||||
}
|
||||
|
||||
if (ensure(OwningCommonPlayer))
|
||||
{
|
||||
if (bIsHoldKeybind)
|
||||
{
|
||||
// Setup the ProgressPercentageMID
|
||||
if (ProgressPercentageMID == nullptr)
|
||||
{
|
||||
if (UMaterialInterface* Material = Cast<UMaterialInterface>(HoldProgressBrush.GetResourceObject()))
|
||||
{
|
||||
ProgressPercentageMID = UMaterialInstanceDynamic::Create(Material, this);
|
||||
HoldProgressBrush.SetResourceObject(ProgressPercentageMID);
|
||||
}
|
||||
}
|
||||
SyncHoldProgress();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::ShowHoldBackPlate()
|
||||
{
|
||||
bool bDirty = false;
|
||||
|
||||
if (IsHoldKeybind())
|
||||
{
|
||||
float BrushSizeAsValue = 32.0f;
|
||||
|
||||
float DesiredBoxSize = BrushSizeAsValue + 10.0f;
|
||||
if (!bDrawBrushForKey)
|
||||
{
|
||||
DesiredBoxSize += 14.0f;
|
||||
}
|
||||
|
||||
const FVector2D NewDesiredBrushSize(DesiredBoxSize, DesiredBoxSize);
|
||||
if (HoldProgressBrush.GetImageSize() != NewDesiredBrushSize)
|
||||
{
|
||||
HoldProgressBrush.SetImageSize(NewDesiredBrushSize);
|
||||
bDirty = true;
|
||||
}
|
||||
|
||||
if (!bDrawProgress)
|
||||
{
|
||||
bDrawProgress = true;
|
||||
bDirty = true;
|
||||
}
|
||||
|
||||
static const FName BackAlphaName = TEXT("BackAlpha");
|
||||
static const FName OutlineAlphaName = TEXT("OutlineAlpha");
|
||||
|
||||
if (ProgressPercentageMID)
|
||||
{
|
||||
ProgressPercentageMID->SetScalarParameterValue(BackAlphaName, 0.2f);
|
||||
ProgressPercentageMID->SetScalarParameterValue(OutlineAlphaName, 0.4f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bDrawProgress)
|
||||
{
|
||||
bDrawProgress = false;
|
||||
bDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bDirty)
|
||||
{
|
||||
Invalidate(EInvalidateWidget::Paint);
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::HandlePlayerControllerSet(UCommonLocalPlayer* LocalPlayer, APlayerController* PlayerController)
|
||||
{
|
||||
if (bWaitingForPlayerController && GetOwningPlayer<ACommonPlayerController>())
|
||||
{
|
||||
UpdateKeybindWidget();
|
||||
bWaitingForPlayerController = false;
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonPlayerInputKey::RecalculateDesiredSize()
|
||||
{
|
||||
FVector2D MaximumDesiredSize(0, 0);
|
||||
float LayoutScale = 1;
|
||||
|
||||
if (bDrawProgress)
|
||||
{
|
||||
MaximumDesiredSize = FVector2D::Max(MaximumDesiredSize, HoldProgressBrush.GetImageSize());
|
||||
}
|
||||
|
||||
if (bDrawCountdownText)
|
||||
{
|
||||
MaximumDesiredSize = FVector2D::Max(MaximumDesiredSize, CountdownText.UpdateTextSize(CountdownTextFont, LayoutScale));
|
||||
}
|
||||
else if (bDrawBrushForKey)
|
||||
{
|
||||
MaximumDesiredSize = FVector2D::Max(MaximumDesiredSize, CachedKeyBrush.GetImageSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
const FVector2D KeybindTextSize = KeybindText.UpdateTextSize(KeyBindTextFont, LayoutScale);
|
||||
FrameSize = FVector2D::Max(KeybindTextSize, KeybindFrameMinimumSize) + KeybindTextPadding.GetDesiredSize();
|
||||
MaximumDesiredSize = FVector2D::Max(MaximumDesiredSize, FrameSize);
|
||||
}
|
||||
|
||||
SetMinimumDesiredSize(MaximumDesiredSize);
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
171
Plugins/CommonGame/Source/Private/CommonUIExtensions.cpp
Normal file
171
Plugins/CommonGame/Source/Private/CommonUIExtensions.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "CommonUIExtensions.h"
|
||||
|
||||
#include "CommonInputSubsystem.h"
|
||||
#include "CommonInputTypeEnum.h"
|
||||
#include "CommonLocalPlayer.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "GameUIManagerSubsystem.h"
|
||||
#include "GameUIPolicy.h"
|
||||
#include "PrimaryGameLayout.h"
|
||||
#include "Widgets/CommonActivatableWidgetContainer.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonUIExtensions)
|
||||
|
||||
int32 UCommonUIExtensions::InputSuspensions = 0;
|
||||
|
||||
ECommonInputType UCommonUIExtensions::GetOwningPlayerInputType(const UUserWidget* WidgetContextObject)
|
||||
{
|
||||
if (WidgetContextObject)
|
||||
{
|
||||
if (const UCommonInputSubsystem* InputSubsystem = UCommonInputSubsystem::Get(WidgetContextObject->GetOwningLocalPlayer()))
|
||||
{
|
||||
return InputSubsystem->GetCurrentInputType();
|
||||
}
|
||||
}
|
||||
|
||||
return ECommonInputType::Count;
|
||||
}
|
||||
|
||||
bool UCommonUIExtensions::IsOwningPlayerUsingTouch(const UUserWidget* WidgetContextObject)
|
||||
{
|
||||
if (WidgetContextObject)
|
||||
{
|
||||
if (const UCommonInputSubsystem* InputSubsystem = UCommonInputSubsystem::Get(WidgetContextObject->GetOwningLocalPlayer()))
|
||||
{
|
||||
return InputSubsystem->GetCurrentInputType() == ECommonInputType::Touch;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UCommonUIExtensions::IsOwningPlayerUsingGamepad(const UUserWidget* WidgetContextObject)
|
||||
{
|
||||
if (WidgetContextObject)
|
||||
{
|
||||
if (const UCommonInputSubsystem* InputSubsystem = UCommonInputSubsystem::Get(WidgetContextObject->GetOwningLocalPlayer()))
|
||||
{
|
||||
return InputSubsystem->GetCurrentInputType() == ECommonInputType::Gamepad;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UCommonActivatableWidget* UCommonUIExtensions::PushContentToLayer_ForPlayer(const ULocalPlayer* LocalPlayer, FGameplayTag LayerName, TSubclassOf<UCommonActivatableWidget> WidgetClass)
|
||||
{
|
||||
if (!ensure(LocalPlayer) || !ensure(WidgetClass != nullptr))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (UGameUIManagerSubsystem* UIManager = LocalPlayer->GetGameInstance()->GetSubsystem<UGameUIManagerSubsystem>())
|
||||
{
|
||||
if (UGameUIPolicy* Policy = UIManager->GetCurrentUIPolicy())
|
||||
{
|
||||
if (UPrimaryGameLayout* RootLayout = Policy->GetRootLayout(CastChecked<UCommonLocalPlayer>(LocalPlayer)))
|
||||
{
|
||||
return RootLayout->PushWidgetToLayerStack(LayerName, WidgetClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UCommonUIExtensions::PushStreamedContentToLayer_ForPlayer(const ULocalPlayer* LocalPlayer, FGameplayTag LayerName, TSoftClassPtr<UCommonActivatableWidget> WidgetClass)
|
||||
{
|
||||
if (!ensure(LocalPlayer) || !ensure(!WidgetClass.IsNull()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (UGameUIManagerSubsystem* UIManager = LocalPlayer->GetGameInstance()->GetSubsystem<UGameUIManagerSubsystem>())
|
||||
{
|
||||
if (UGameUIPolicy* Policy = UIManager->GetCurrentUIPolicy())
|
||||
{
|
||||
if (UPrimaryGameLayout* RootLayout = Policy->GetRootLayout(CastChecked<UCommonLocalPlayer>(LocalPlayer)))
|
||||
{
|
||||
const bool bSuspendInputUntilComplete = true;
|
||||
RootLayout->PushWidgetToLayerStackAsync(LayerName, bSuspendInputUntilComplete, WidgetClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonUIExtensions::PopContentFromLayer(UCommonActivatableWidget* ActivatableWidget)
|
||||
{
|
||||
if (!ActivatableWidget)
|
||||
{
|
||||
// Ignore request to pop an already deleted widget
|
||||
return;
|
||||
}
|
||||
|
||||
if (const ULocalPlayer* LocalPlayer = ActivatableWidget->GetOwningLocalPlayer())
|
||||
{
|
||||
if (const UGameUIManagerSubsystem* UIManager = LocalPlayer->GetGameInstance()->GetSubsystem<UGameUIManagerSubsystem>())
|
||||
{
|
||||
if (const UGameUIPolicy* Policy = UIManager->GetCurrentUIPolicy())
|
||||
{
|
||||
if (UPrimaryGameLayout* RootLayout = Policy->GetRootLayout(CastChecked<UCommonLocalPlayer>(LocalPlayer)))
|
||||
{
|
||||
RootLayout->FindAndRemoveWidgetFromLayer(ActivatableWidget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ULocalPlayer* UCommonUIExtensions::GetLocalPlayerFromController(APlayerController* PlayerController)
|
||||
{
|
||||
if (PlayerController)
|
||||
{
|
||||
return Cast<ULocalPlayer>(PlayerController->Player);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FName UCommonUIExtensions::SuspendInputForPlayer(APlayerController* PlayerController, FName SuspendReason)
|
||||
{
|
||||
return SuspendInputForPlayer(PlayerController ? PlayerController->GetLocalPlayer() : nullptr, SuspendReason);
|
||||
}
|
||||
|
||||
FName UCommonUIExtensions::SuspendInputForPlayer(ULocalPlayer* LocalPlayer, FName SuspendReason)
|
||||
{
|
||||
if (UCommonInputSubsystem* CommonInputSubsystem = UCommonInputSubsystem::Get(LocalPlayer))
|
||||
{
|
||||
InputSuspensions++;
|
||||
FName SuspendToken = SuspendReason;
|
||||
SuspendToken.SetNumber(InputSuspensions);
|
||||
|
||||
CommonInputSubsystem->SetInputTypeFilter(ECommonInputType::MouseAndKeyboard, SuspendToken, true);
|
||||
CommonInputSubsystem->SetInputTypeFilter(ECommonInputType::Gamepad, SuspendToken, true);
|
||||
CommonInputSubsystem->SetInputTypeFilter(ECommonInputType::Touch, SuspendToken, true);
|
||||
|
||||
return SuspendToken;
|
||||
}
|
||||
|
||||
return NAME_None;
|
||||
}
|
||||
|
||||
void UCommonUIExtensions::ResumeInputForPlayer(APlayerController* PlayerController, FName SuspendToken)
|
||||
{
|
||||
ResumeInputForPlayer(PlayerController ? PlayerController->GetLocalPlayer() : nullptr, SuspendToken);
|
||||
}
|
||||
|
||||
void UCommonUIExtensions::ResumeInputForPlayer(ULocalPlayer* LocalPlayer, FName SuspendToken)
|
||||
{
|
||||
if (SuspendToken == NAME_None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (UCommonInputSubsystem* CommonInputSubsystem = UCommonInputSubsystem::Get(LocalPlayer))
|
||||
{
|
||||
CommonInputSubsystem->SetInputTypeFilter(ECommonInputType::MouseAndKeyboard, SuspendToken, false);
|
||||
CommonInputSubsystem->SetInputTypeFilter(ECommonInputType::Gamepad, SuspendToken, false);
|
||||
CommonInputSubsystem->SetInputTypeFilter(ECommonInputType::Touch, SuspendToken, false);
|
||||
}
|
||||
}
|
||||
|
||||
76
Plugins/CommonGame/Source/Private/GameUIManagerSubsystem.cpp
Normal file
76
Plugins/CommonGame/Source/Private/GameUIManagerSubsystem.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "GameUIManagerSubsystem.h"
|
||||
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "GameUIPolicy.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GameUIManagerSubsystem)
|
||||
|
||||
class FSubsystemCollectionBase;
|
||||
class UClass;
|
||||
|
||||
void UGameUIManagerSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
|
||||
if (!CurrentPolicy && !DefaultUIPolicyClass.IsNull())
|
||||
{
|
||||
TSubclassOf<UGameUIPolicy> PolicyClass = DefaultUIPolicyClass.LoadSynchronous();
|
||||
SwitchToPolicy(NewObject<UGameUIPolicy>(this, PolicyClass));
|
||||
}
|
||||
}
|
||||
|
||||
void UGameUIManagerSubsystem::Deinitialize()
|
||||
{
|
||||
Super::Deinitialize();
|
||||
|
||||
SwitchToPolicy(nullptr);
|
||||
}
|
||||
|
||||
bool UGameUIManagerSubsystem::ShouldCreateSubsystem(UObject* Outer) const
|
||||
{
|
||||
if (!CastChecked<UGameInstance>(Outer)->IsDedicatedServerInstance())
|
||||
{
|
||||
TArray<UClass*> ChildClasses;
|
||||
GetDerivedClasses(GetClass(), ChildClasses, false);
|
||||
|
||||
// Only create an instance if there is no override implementation defined elsewhere
|
||||
return ChildClasses.Num() == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UGameUIManagerSubsystem::NotifyPlayerAdded(UCommonLocalPlayer* LocalPlayer)
|
||||
{
|
||||
if (ensure(LocalPlayer) && CurrentPolicy)
|
||||
{
|
||||
CurrentPolicy->NotifyPlayerAdded(LocalPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
void UGameUIManagerSubsystem::NotifyPlayerRemoved(UCommonLocalPlayer* LocalPlayer)
|
||||
{
|
||||
if (LocalPlayer && CurrentPolicy)
|
||||
{
|
||||
CurrentPolicy->NotifyPlayerRemoved(LocalPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
void UGameUIManagerSubsystem::NotifyPlayerDestroyed(UCommonLocalPlayer* LocalPlayer)
|
||||
{
|
||||
if (LocalPlayer && CurrentPolicy)
|
||||
{
|
||||
CurrentPolicy->NotifyPlayerDestroyed(LocalPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
void UGameUIManagerSubsystem::SwitchToPolicy(UGameUIPolicy* InPolicy)
|
||||
{
|
||||
if (CurrentPolicy != InPolicy)
|
||||
{
|
||||
CurrentPolicy = InPolicy;
|
||||
}
|
||||
}
|
||||
|
||||
203
Plugins/CommonGame/Source/Private/GameUIPolicy.cpp
Normal file
203
Plugins/CommonGame/Source/Private/GameUIPolicy.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "GameUIPolicy.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "GameUIManagerSubsystem.h"
|
||||
#include "CommonLocalPlayer.h"
|
||||
#include "PrimaryGameLayout.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "LogCommonGame.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GameUIPolicy)
|
||||
|
||||
// Static
|
||||
UGameUIPolicy* UGameUIPolicy::GetGameUIPolicy(const UObject* WorldContextObject)
|
||||
{
|
||||
if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
|
||||
{
|
||||
if (UGameInstance* GameInstance = World->GetGameInstance())
|
||||
{
|
||||
if (UGameUIManagerSubsystem* UIManager = UGameInstance::GetSubsystem<UGameUIManagerSubsystem>(GameInstance))
|
||||
{
|
||||
return UIManager->GetCurrentUIPolicy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UGameUIManagerSubsystem* UGameUIPolicy::GetOwningUIManager() const
|
||||
{
|
||||
return CastChecked<UGameUIManagerSubsystem>(GetOuter());
|
||||
}
|
||||
|
||||
UWorld* UGameUIPolicy::GetWorld() const
|
||||
{
|
||||
return GetOwningUIManager()->GetGameInstance()->GetWorld();
|
||||
}
|
||||
|
||||
UPrimaryGameLayout* UGameUIPolicy::GetRootLayout(const UCommonLocalPlayer* LocalPlayer) const
|
||||
{
|
||||
const FRootViewportLayoutInfo* LayoutInfo = RootViewportLayouts.FindByKey(LocalPlayer);
|
||||
return LayoutInfo ? LayoutInfo->RootLayout : nullptr;
|
||||
}
|
||||
|
||||
void UGameUIPolicy::NotifyPlayerAdded(UCommonLocalPlayer* LocalPlayer)
|
||||
{
|
||||
LocalPlayer->OnPlayerControllerSet.AddWeakLambda(this, [this](UCommonLocalPlayer* LocalPlayer, APlayerController* PlayerController)
|
||||
{
|
||||
NotifyPlayerRemoved(LocalPlayer);
|
||||
|
||||
if (FRootViewportLayoutInfo* LayoutInfo = RootViewportLayouts.FindByKey(LocalPlayer))
|
||||
{
|
||||
AddLayoutToViewport(LocalPlayer, LayoutInfo->RootLayout);
|
||||
LayoutInfo->bAddedToViewport = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateLayoutWidget(LocalPlayer);
|
||||
}
|
||||
});
|
||||
|
||||
if (FRootViewportLayoutInfo* LayoutInfo = RootViewportLayouts.FindByKey(LocalPlayer))
|
||||
{
|
||||
AddLayoutToViewport(LocalPlayer, LayoutInfo->RootLayout);
|
||||
LayoutInfo->bAddedToViewport = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateLayoutWidget(LocalPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
void UGameUIPolicy::NotifyPlayerRemoved(UCommonLocalPlayer* LocalPlayer)
|
||||
{
|
||||
if (FRootViewportLayoutInfo* LayoutInfo = RootViewportLayouts.FindByKey(LocalPlayer))
|
||||
{
|
||||
RemoveLayoutFromViewport(LocalPlayer, LayoutInfo->RootLayout);
|
||||
LayoutInfo->bAddedToViewport = false;
|
||||
|
||||
if (LocalMultiplayerInteractionMode == ELocalMultiplayerInteractionMode::SingleToggle && !LocalPlayer->IsPrimaryPlayer())
|
||||
{
|
||||
UPrimaryGameLayout* RootLayout = LayoutInfo->RootLayout;
|
||||
if (RootLayout && !RootLayout->IsDormant())
|
||||
{
|
||||
// We're removing a secondary player's root while it's in control - transfer control back to the primary player's root
|
||||
RootLayout->SetIsDormant(true);
|
||||
for (const FRootViewportLayoutInfo& RootLayoutInfo : RootViewportLayouts)
|
||||
{
|
||||
if (RootLayoutInfo.LocalPlayer->IsPrimaryPlayer())
|
||||
{
|
||||
if (UPrimaryGameLayout* PrimaryRootLayout = RootLayoutInfo.RootLayout)
|
||||
{
|
||||
PrimaryRootLayout->SetIsDormant(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGameUIPolicy::NotifyPlayerDestroyed(UCommonLocalPlayer* LocalPlayer)
|
||||
{
|
||||
NotifyPlayerRemoved(LocalPlayer);
|
||||
LocalPlayer->OnPlayerControllerSet.RemoveAll(this);
|
||||
const int32 LayoutInfoIdx = RootViewportLayouts.IndexOfByKey(LocalPlayer);
|
||||
if (LayoutInfoIdx != INDEX_NONE)
|
||||
{
|
||||
UPrimaryGameLayout* Layout = RootViewportLayouts[LayoutInfoIdx].RootLayout;
|
||||
RootViewportLayouts.RemoveAt(LayoutInfoIdx);
|
||||
|
||||
RemoveLayoutFromViewport(LocalPlayer, Layout);
|
||||
|
||||
OnRootLayoutReleased(LocalPlayer, Layout);
|
||||
}
|
||||
}
|
||||
|
||||
void UGameUIPolicy::AddLayoutToViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout)
|
||||
{
|
||||
UE_LOG(LogCommonGame, Log, TEXT("[%s] is adding player [%s]'s root layout [%s] to the viewport"), *GetName(), *GetNameSafe(LocalPlayer), *GetNameSafe(Layout));
|
||||
|
||||
Layout->SetPlayerContext(FLocalPlayerContext(LocalPlayer));
|
||||
Layout->AddToPlayerScreen(1000);
|
||||
|
||||
OnRootLayoutAddedToViewport(LocalPlayer, Layout);
|
||||
}
|
||||
|
||||
void UGameUIPolicy::RemoveLayoutFromViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout)
|
||||
{
|
||||
TWeakPtr<SWidget> LayoutSlateWidget = Layout->GetCachedWidget();
|
||||
if (LayoutSlateWidget.IsValid())
|
||||
{
|
||||
UE_LOG(LogCommonGame, Log, TEXT("[%s] is removing player [%s]'s root layout [%s] from the viewport"), *GetName(), *GetNameSafe(LocalPlayer), *GetNameSafe(Layout));
|
||||
|
||||
Layout->RemoveFromParent();
|
||||
if (LayoutSlateWidget.IsValid())
|
||||
{
|
||||
UE_LOG(LogCommonGame, Log, TEXT("Player [%s]'s root layout [%s] has been removed from the viewport, but other references to its underlying Slate widget still exist. Noting in case we leak it."), *GetNameSafe(LocalPlayer), *GetNameSafe(Layout));
|
||||
}
|
||||
|
||||
OnRootLayoutRemovedFromViewport(LocalPlayer, Layout);
|
||||
}
|
||||
}
|
||||
|
||||
void UGameUIPolicy::OnRootLayoutAddedToViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout)
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
if (GIsEditor && LocalPlayer->IsPrimaryPlayer())
|
||||
{
|
||||
// So our controller will work in PIE without needing to click in the viewport
|
||||
FSlateApplication::Get().SetUserFocusToGameViewport(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void UGameUIPolicy::OnRootLayoutRemovedFromViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UGameUIPolicy::OnRootLayoutReleased(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UGameUIPolicy::RequestPrimaryControl(UPrimaryGameLayout* Layout)
|
||||
{
|
||||
if (LocalMultiplayerInteractionMode == ELocalMultiplayerInteractionMode::SingleToggle && Layout->IsDormant())
|
||||
{
|
||||
for (const FRootViewportLayoutInfo& LayoutInfo : RootViewportLayouts)
|
||||
{
|
||||
UPrimaryGameLayout* RootLayout = LayoutInfo.RootLayout;
|
||||
if (RootLayout && !RootLayout->IsDormant())
|
||||
{
|
||||
RootLayout->SetIsDormant(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Layout->SetIsDormant(false);
|
||||
}
|
||||
}
|
||||
|
||||
void UGameUIPolicy::CreateLayoutWidget(UCommonLocalPlayer* LocalPlayer)
|
||||
{
|
||||
if (APlayerController* PlayerController = LocalPlayer->GetPlayerController(GetWorld()))
|
||||
{
|
||||
TSubclassOf<UPrimaryGameLayout> LayoutWidgetClass = GetLayoutWidgetClass(LocalPlayer);
|
||||
if (ensure(LayoutWidgetClass && !LayoutWidgetClass->HasAnyClassFlags(CLASS_Abstract)))
|
||||
{
|
||||
UPrimaryGameLayout* NewLayoutObject = CreateWidget<UPrimaryGameLayout>(PlayerController, LayoutWidgetClass);
|
||||
RootViewportLayouts.Emplace(LocalPlayer, NewLayoutObject, true);
|
||||
|
||||
AddLayoutToViewport(LocalPlayer, NewLayoutObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TSubclassOf<UPrimaryGameLayout> UGameUIPolicy::GetLayoutWidgetClass(UCommonLocalPlayer* LocalPlayer)
|
||||
{
|
||||
return LayoutClass.LoadSynchronous();
|
||||
}
|
||||
5
Plugins/CommonGame/Source/Private/LogCommonGame.cpp
Normal file
5
Plugins/CommonGame/Source/Private/LogCommonGame.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "LogCommonGame.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogCommonGame);
|
||||
7
Plugins/CommonGame/Source/Private/LogCommonGame.h
Normal file
7
Plugins/CommonGame/Source/Private/LogCommonGame.h
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogCommonGame, Log, All);
|
||||
106
Plugins/CommonGame/Source/Private/Messaging/CommonGameDialog.cpp
Normal file
106
Plugins/CommonGame/Source/Private/Messaging/CommonGameDialog.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "Messaging/CommonGameDialog.h"
|
||||
|
||||
#include "Messaging/CommonMessagingSubsystem.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonGameDialog)
|
||||
|
||||
#define LOCTEXT_NAMESPACE "Messaging"
|
||||
|
||||
UCommonGameDialogDescriptor* UCommonGameDialogDescriptor::CreateConfirmationOk(const FText& Header, const FText& Body)
|
||||
{
|
||||
UCommonGameDialogDescriptor* Descriptor = NewObject<UCommonGameDialogDescriptor>();
|
||||
Descriptor->Header = Header;
|
||||
Descriptor->Body = Body;
|
||||
|
||||
FConfirmationDialogAction ConfirmAction;
|
||||
ConfirmAction.Result = ECommonMessagingResult::Confirmed;
|
||||
ConfirmAction.OptionalDisplayText = LOCTEXT("Ok", "Ok");
|
||||
|
||||
Descriptor->ButtonActions.Add(ConfirmAction);
|
||||
|
||||
return Descriptor;
|
||||
}
|
||||
|
||||
UCommonGameDialogDescriptor* UCommonGameDialogDescriptor::CreateConfirmationOkCancel(const FText& Header, const FText& Body)
|
||||
{
|
||||
UCommonGameDialogDescriptor* Descriptor = NewObject<UCommonGameDialogDescriptor>();
|
||||
Descriptor->Header = Header;
|
||||
Descriptor->Body = Body;
|
||||
|
||||
FConfirmationDialogAction ConfirmAction;
|
||||
ConfirmAction.Result = ECommonMessagingResult::Confirmed;
|
||||
ConfirmAction.OptionalDisplayText = LOCTEXT("Ok", "Ok");
|
||||
|
||||
FConfirmationDialogAction CancelAction;
|
||||
CancelAction.Result = ECommonMessagingResult::Cancelled;
|
||||
CancelAction.OptionalDisplayText = LOCTEXT("Cancel", "Cancel");
|
||||
|
||||
Descriptor->ButtonActions.Add(ConfirmAction);
|
||||
Descriptor->ButtonActions.Add(CancelAction);
|
||||
|
||||
return Descriptor;
|
||||
}
|
||||
|
||||
UCommonGameDialogDescriptor* UCommonGameDialogDescriptor::CreateConfirmationYesNo(const FText& Header, const FText& Body)
|
||||
{
|
||||
UCommonGameDialogDescriptor* Descriptor = NewObject<UCommonGameDialogDescriptor>();
|
||||
Descriptor->Header = Header;
|
||||
Descriptor->Body = Body;
|
||||
|
||||
FConfirmationDialogAction ConfirmAction;
|
||||
ConfirmAction.Result = ECommonMessagingResult::Confirmed;
|
||||
ConfirmAction.OptionalDisplayText = LOCTEXT("Yes", "Yes");
|
||||
|
||||
FConfirmationDialogAction DeclineAction;
|
||||
DeclineAction.Result = ECommonMessagingResult::Declined;
|
||||
DeclineAction.OptionalDisplayText = LOCTEXT("No", "No");
|
||||
|
||||
Descriptor->ButtonActions.Add(ConfirmAction);
|
||||
Descriptor->ButtonActions.Add(DeclineAction);
|
||||
|
||||
return Descriptor;
|
||||
}
|
||||
|
||||
UCommonGameDialogDescriptor* UCommonGameDialogDescriptor::CreateConfirmationYesNoCancel(const FText& Header, const FText& Body)
|
||||
{
|
||||
UCommonGameDialogDescriptor* Descriptor = NewObject<UCommonGameDialogDescriptor>();
|
||||
Descriptor->Header = Header;
|
||||
Descriptor->Body = Body;
|
||||
|
||||
FConfirmationDialogAction ConfirmAction;
|
||||
ConfirmAction.Result = ECommonMessagingResult::Confirmed;
|
||||
ConfirmAction.OptionalDisplayText = LOCTEXT("Yes", "Yes");
|
||||
|
||||
FConfirmationDialogAction DeclineAction;
|
||||
DeclineAction.Result = ECommonMessagingResult::Declined;
|
||||
DeclineAction.OptionalDisplayText = LOCTEXT("No", "No");
|
||||
|
||||
FConfirmationDialogAction CancelAction;
|
||||
CancelAction.Result = ECommonMessagingResult::Cancelled;
|
||||
CancelAction.OptionalDisplayText = LOCTEXT("Cancel", "Cancel");
|
||||
|
||||
Descriptor->ButtonActions.Add(ConfirmAction);
|
||||
Descriptor->ButtonActions.Add(DeclineAction);
|
||||
Descriptor->ButtonActions.Add(CancelAction);
|
||||
|
||||
return Descriptor;
|
||||
}
|
||||
|
||||
UCommonGameDialog::UCommonGameDialog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UCommonGameDialog::SetupDialog(UCommonGameDialogDescriptor* Descriptor, FCommonMessagingResultDelegate ResultCallback)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UCommonGameDialog::KillDialog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "Messaging/CommonMessagingSubsystem.h"
|
||||
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
#include "UObject/UObjectHash.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonMessagingSubsystem)
|
||||
|
||||
class FSubsystemCollectionBase;
|
||||
class UClass;
|
||||
|
||||
void UCommonMessagingSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
}
|
||||
|
||||
void UCommonMessagingSubsystem::Deinitialize()
|
||||
{
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
bool UCommonMessagingSubsystem::ShouldCreateSubsystem(UObject* Outer) const
|
||||
{
|
||||
if (!CastChecked<ULocalPlayer>(Outer)->GetGameInstance()->IsDedicatedServerInstance())
|
||||
{
|
||||
TArray<UClass*> ChildClasses;
|
||||
GetDerivedClasses(GetClass(), ChildClasses, false);
|
||||
|
||||
// Only create an instance if there is no override implementation defined elsewhere
|
||||
return ChildClasses.Num() == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UCommonMessagingSubsystem::ShowConfirmation(UCommonGameDialogDescriptor* DialogDescriptor, FCommonMessagingResultDelegate ResultCallback)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UCommonMessagingSubsystem::ShowError(UCommonGameDialogDescriptor* DialogDescriptor, FCommonMessagingResultDelegate ResultCallback)
|
||||
{
|
||||
|
||||
}
|
||||
132
Plugins/CommonGame/Source/Private/PrimaryGameLayout.cpp
Normal file
132
Plugins/CommonGame/Source/Private/PrimaryGameLayout.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "PrimaryGameLayout.h"
|
||||
|
||||
#include "CommonLocalPlayer.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "GameUIManagerSubsystem.h"
|
||||
#include "GameUIPolicy.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "LogCommonGame.h"
|
||||
#include "Widgets/CommonActivatableWidgetContainer.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PrimaryGameLayout)
|
||||
|
||||
class UObject;
|
||||
|
||||
/*static*/ UPrimaryGameLayout* UPrimaryGameLayout::GetPrimaryGameLayoutForPrimaryPlayer(const UObject* WorldContextObject)
|
||||
{
|
||||
UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(WorldContextObject);
|
||||
APlayerController* PlayerController = GameInstance->GetPrimaryPlayerController(false);
|
||||
return GetPrimaryGameLayout(PlayerController);
|
||||
}
|
||||
|
||||
/*static*/ UPrimaryGameLayout* UPrimaryGameLayout::GetPrimaryGameLayout(APlayerController* PlayerController)
|
||||
{
|
||||
return PlayerController ? GetPrimaryGameLayout(Cast<UCommonLocalPlayer>(PlayerController->Player)) : nullptr;
|
||||
}
|
||||
|
||||
/*static*/ UPrimaryGameLayout* UPrimaryGameLayout::GetPrimaryGameLayout(ULocalPlayer* LocalPlayer)
|
||||
{
|
||||
if (LocalPlayer)
|
||||
{
|
||||
const UCommonLocalPlayer* CommonLocalPlayer = CastChecked<UCommonLocalPlayer>(LocalPlayer);
|
||||
if (const UGameInstance* GameInstance = CommonLocalPlayer->GetGameInstance())
|
||||
{
|
||||
if (UGameUIManagerSubsystem* UIManager = GameInstance->GetSubsystem<UGameUIManagerSubsystem>())
|
||||
{
|
||||
if (const UGameUIPolicy* Policy = UIManager->GetCurrentUIPolicy())
|
||||
{
|
||||
if (UPrimaryGameLayout* RootLayout = Policy->GetRootLayout(CommonLocalPlayer))
|
||||
{
|
||||
return RootLayout;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UPrimaryGameLayout::UPrimaryGameLayout(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
void UPrimaryGameLayout::SetIsDormant(bool InDormant)
|
||||
{
|
||||
if (bIsDormant != InDormant)
|
||||
{
|
||||
const ULocalPlayer* LP = GetOwningLocalPlayer();
|
||||
const int32 PlayerId = LP ? LP->GetControllerId() : -1;
|
||||
const TCHAR* OldDormancyStr = bIsDormant ? TEXT("Dormant") : TEXT("Not-Dormant");
|
||||
const TCHAR* NewDormancyStr = InDormant ? TEXT("Dormant") : TEXT("Not-Dormant");
|
||||
const TCHAR* PrimaryPlayerStr = LP && LP->IsPrimaryPlayer() ? TEXT("[Primary]") : TEXT("[Non-Primary]");
|
||||
UE_LOG(LogCommonGame, Display, TEXT("%s PrimaryGameLayout Dormancy changed for [%d] from [%s] to [%s]"), PrimaryPlayerStr, PlayerId, OldDormancyStr, NewDormancyStr);
|
||||
|
||||
bIsDormant = InDormant;
|
||||
OnIsDormantChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void UPrimaryGameLayout::OnIsDormantChanged()
|
||||
{
|
||||
//@TODO NDarnell Determine what to do with dormancy, in the past we treated dormancy as a way to shutoff rendering
|
||||
//and the view for the other local players when we force multiple players to use the player view of a single player.
|
||||
|
||||
//if (UCommonLocalPlayer* LocalPlayer = GetOwningLocalPlayer<UCommonLocalPlayer>())
|
||||
//{
|
||||
// // When the root layout is dormant, we don't want to render anything from the owner's view either
|
||||
// LocalPlayer->SetIsPlayerViewEnabled(!bIsDormant);
|
||||
//}
|
||||
|
||||
//SetVisibility(bIsDormant ? ESlateVisibility::Collapsed : ESlateVisibility::SelfHitTestInvisible);
|
||||
|
||||
//OnLayoutDormancyChanged().Broadcast(bIsDormant);
|
||||
}
|
||||
|
||||
void UPrimaryGameLayout::RegisterLayer(FGameplayTag LayerTag, UCommonActivatableWidgetContainerBase* LayerWidget)
|
||||
{
|
||||
if (!IsDesignTime())
|
||||
{
|
||||
LayerWidget->OnTransitioningChanged.AddUObject(this, &UPrimaryGameLayout::OnWidgetStackTransitioning);
|
||||
// TODO: Consider allowing a transition duration, we currently set it to 0, because if it's not 0, the
|
||||
// transition effect will cause focus to not transition properly to the new widgets when using
|
||||
// gamepad always.
|
||||
LayerWidget->SetTransitionDuration(0.0);
|
||||
|
||||
Layers.Add(LayerTag, LayerWidget);
|
||||
}
|
||||
}
|
||||
|
||||
void UPrimaryGameLayout::OnWidgetStackTransitioning(UCommonActivatableWidgetContainerBase* Widget, bool bIsTransitioning)
|
||||
{
|
||||
if (bIsTransitioning)
|
||||
{
|
||||
const FName SuspendToken = UCommonUIExtensions::SuspendInputForPlayer(GetOwningLocalPlayer(), TEXT("GlobalStackTransion"));
|
||||
SuspendInputTokens.Add(SuspendToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ensure(SuspendInputTokens.Num() > 0))
|
||||
{
|
||||
const FName SuspendToken = SuspendInputTokens.Pop();
|
||||
UCommonUIExtensions::ResumeInputForPlayer(GetOwningLocalPlayer(), SuspendToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UPrimaryGameLayout::FindAndRemoveWidgetFromLayer(UCommonActivatableWidget* ActivatableWidget)
|
||||
{
|
||||
// We're not sure what layer the widget is on so go searching.
|
||||
for (const auto& LayerKVP : Layers)
|
||||
{
|
||||
LayerKVP.Value->RemoveWidget(*ActivatableWidget);
|
||||
}
|
||||
}
|
||||
|
||||
UCommonActivatableWidgetContainerBase* UPrimaryGameLayout::GetLayerWidget(FGameplayTag LayerName)
|
||||
{
|
||||
return Layers.FindRef(LayerName);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/CancellableAsyncAction.h"
|
||||
#include "UObject/SoftObjectPtr.h"
|
||||
|
||||
#include "AsyncAction_CreateWidgetAsync.generated.h"
|
||||
|
||||
class APlayerController;
|
||||
class UGameInstance;
|
||||
class UUserWidget;
|
||||
class UWorld;
|
||||
struct FFrame;
|
||||
struct FStreamableHandle;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FCreateWidgetAsyncDelegate, UUserWidget*, UserWidget);
|
||||
|
||||
/**
|
||||
* Load the widget class asynchronously, the instance the widget after the loading completes, and return it on OnComplete.
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class COMMONGAME_API UAsyncAction_CreateWidgetAsync : public UCancellableAsyncAction
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
virtual void Cancel() override;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintCosmetic, meta=(WorldContext = "WorldContextObject", BlueprintInternalUseOnly="true"))
|
||||
static UAsyncAction_CreateWidgetAsync* CreateWidgetAsync(UObject* WorldContextObject, TSoftClassPtr<UUserWidget> UserWidgetSoftClass, APlayerController* OwningPlayer, bool bSuspendInputUntilComplete = true);
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FCreateWidgetAsyncDelegate OnComplete;
|
||||
|
||||
private:
|
||||
|
||||
void OnWidgetLoaded();
|
||||
|
||||
FName SuspendInputToken;
|
||||
TWeakObjectPtr<APlayerController> OwningPlayer;
|
||||
TWeakObjectPtr<UWorld> World;
|
||||
TWeakObjectPtr<UGameInstance> GameInstance;
|
||||
bool bSuspendInputUntilComplete;
|
||||
TSoftClassPtr<UUserWidget> UserWidgetSoftClass;
|
||||
TSharedPtr<FStreamableHandle> StreamingHandle;
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/CancellableAsyncAction.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "UObject/SoftObjectPtr.h"
|
||||
|
||||
#include "AsyncAction_PushContentToLayerForPlayer.generated.h"
|
||||
|
||||
class APlayerController;
|
||||
class UCommonActivatableWidget;
|
||||
class UObject;
|
||||
struct FFrame;
|
||||
struct FStreamableHandle;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPushContentToLayerForPlayerAsyncDelegate, UCommonActivatableWidget*, UserWidget);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class COMMONGAME_API UAsyncAction_PushContentToLayerForPlayer : public UCancellableAsyncAction
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
virtual void Cancel() override;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintCosmetic, meta=(WorldContext = "WorldContextObject", BlueprintInternalUseOnly="true"))
|
||||
static UAsyncAction_PushContentToLayerForPlayer* PushContentToLayerForPlayer(APlayerController* OwningPlayer, UPARAM(meta = (AllowAbstract=false)) TSoftClassPtr<UCommonActivatableWidget> WidgetClass, UPARAM(meta = (Categories = "UI.Layer")) FGameplayTag LayerName, bool bSuspendInputUntilComplete = true);
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FPushContentToLayerForPlayerAsyncDelegate BeforePush;
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FPushContentToLayerForPlayerAsyncDelegate AfterPush;
|
||||
|
||||
private:
|
||||
|
||||
FGameplayTag LayerName;
|
||||
bool bSuspendInputUntilComplete = false;
|
||||
TWeakObjectPtr<APlayerController> OwningPlayerPtr;
|
||||
TSoftClassPtr<UCommonActivatableWidget> WidgetClass;
|
||||
|
||||
TSharedPtr<FStreamableHandle> StreamingHandle;
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Kismet/BlueprintAsyncActionBase.h"
|
||||
|
||||
#include "UObject/ObjectPtr.h"
|
||||
#include "AsyncAction_ShowConfirmation.generated.h"
|
||||
|
||||
enum class ECommonMessagingResult : uint8;
|
||||
|
||||
class FText;
|
||||
class UCommonGameDialogDescriptor;
|
||||
class ULocalPlayer;
|
||||
struct FFrame;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FCommonMessagingResultMCDelegate, ECommonMessagingResult, Result);
|
||||
|
||||
/**
|
||||
* Allows easily triggering an async confirmation dialog in blueprints that you can then wait on the result.
|
||||
*/
|
||||
UCLASS()
|
||||
class UAsyncAction_ShowConfirmation : public UBlueprintAsyncActionBase
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, BlueprintCosmetic, meta = (BlueprintInternalUseOnly = "true", WorldContext = "InWorldContextObject"))
|
||||
static UAsyncAction_ShowConfirmation* ShowConfirmationYesNo(
|
||||
UObject* InWorldContextObject, FText Title, FText Message
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintCosmetic, meta = (BlueprintInternalUseOnly = "true", WorldContext = "InWorldContextObject"))
|
||||
static UAsyncAction_ShowConfirmation* ShowConfirmationOkCancel(
|
||||
UObject* InWorldContextObject, FText Title, FText Message
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintCosmetic, meta = (BlueprintInternalUseOnly = "true", WorldContext = "InWorldContextObject"))
|
||||
static UAsyncAction_ShowConfirmation* ShowConfirmationCustom(
|
||||
UObject* InWorldContextObject, UCommonGameDialogDescriptor* Descriptor
|
||||
);
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FCommonMessagingResultMCDelegate OnResult;
|
||||
|
||||
private:
|
||||
void HandleConfirmationResult(ECommonMessagingResult ConfirmationResult);
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UObject> WorldContextObject;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<ULocalPlayer> TargetLocalPlayer;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UCommonGameDialogDescriptor> Descriptor;
|
||||
};
|
||||
72
Plugins/CommonGame/Source/Public/CommonGameInstance.h
Normal file
72
Plugins/CommonGame/Source/Public/CommonGameInstance.h
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/GameInstance.h"
|
||||
|
||||
#include "CommonGameInstance.generated.h"
|
||||
|
||||
enum class ECommonUserAvailability : uint8;
|
||||
enum class ECommonUserPrivilege : uint8;
|
||||
|
||||
class FText;
|
||||
class UCommonUserInfo;
|
||||
class UCommonSession_SearchResult;
|
||||
struct FOnlineResultInformation;
|
||||
class ULocalPlayer;
|
||||
class USocialManager;
|
||||
class UObject;
|
||||
struct FFrame;
|
||||
struct FGameplayTag;
|
||||
|
||||
UCLASS(Abstract, Config = Game)
|
||||
class COMMONGAME_API UCommonGameInstance : public UGameInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UCommonGameInstance(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
/** Handles errors/warnings from CommonUser, can be overridden per game */
|
||||
UFUNCTION()
|
||||
virtual void HandleSystemMessage(FGameplayTag MessageType, FText Title, FText Message);
|
||||
|
||||
UFUNCTION()
|
||||
virtual void HandlePrivilegeChanged(const UCommonUserInfo* UserInfo, ECommonUserPrivilege Privilege, ECommonUserAvailability OldAvailability, ECommonUserAvailability NewAvailability);
|
||||
|
||||
/** Call to reset user and session state, usually because a player has been disconnected */
|
||||
virtual void ResetUserAndSessionState();
|
||||
|
||||
/**
|
||||
* Requested Session Flow
|
||||
* Something requests the user to join a specific session (for example, a platform overlay via OnUserRequestedSession).
|
||||
* This request is handled in SetRequestedSession.
|
||||
* Check if we can join the requested session immediately (CanJoinRequestedSession). If we can, join the requested session (JoinRequestedSession)
|
||||
* If not, cache the requested session and instruct the game to get into a state where the session can be joined (ResetGameAndJoinRequestedSession)
|
||||
*/
|
||||
/** Handles user accepting a session invite from an external source (for example, a platform overlay). Intended to be overridden per game. */
|
||||
virtual void OnUserRequestedSession(const FPlatformUserId& PlatformUserId, UCommonSession_SearchResult* InRequestedSession, const FOnlineResultInformation& RequestedSessionResult);
|
||||
|
||||
/** Get the requested session */
|
||||
UCommonSession_SearchResult* GetRequestedSession() const { return RequestedSession; }
|
||||
/** Set (or clear) the requested session. When this is set, the requested session flow begins. */
|
||||
virtual void SetRequestedSession(UCommonSession_SearchResult* InRequestedSession);
|
||||
/** Checks if the requested session can be joined. Can be overridden per game. */
|
||||
virtual bool CanJoinRequestedSession() const;
|
||||
/** Join the requested session */
|
||||
virtual void JoinRequestedSession();
|
||||
/** Get the game into a state to join the requested session */
|
||||
virtual void ResetGameAndJoinRequestedSession();
|
||||
|
||||
virtual int32 AddLocalPlayer(ULocalPlayer* NewPlayer, FPlatformUserId UserId) override;
|
||||
virtual bool RemoveLocalPlayer(ULocalPlayer* ExistingPlayer) override;
|
||||
virtual void Init() override;
|
||||
virtual void ReturnToMainMenu() override;
|
||||
|
||||
private:
|
||||
/** This is the primary player*/
|
||||
TWeakObjectPtr<ULocalPlayer> PrimaryPlayer;
|
||||
/** Session the player has requested to join */
|
||||
UPROPERTY()
|
||||
TObjectPtr<UCommonSession_SearchResult> RequestedSession;
|
||||
};
|
||||
51
Plugins/CommonGame/Source/Public/CommonLocalPlayer.h
Normal file
51
Plugins/CommonGame/Source/Public/CommonLocalPlayer.h
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/LocalPlayer.h"
|
||||
|
||||
#include "CommonLocalPlayer.generated.h"
|
||||
|
||||
class APawn;
|
||||
class APlayerController;
|
||||
class APlayerState;
|
||||
class FViewport;
|
||||
class UObject;
|
||||
class UPrimaryGameLayout;
|
||||
struct FSceneViewProjectionData;
|
||||
|
||||
UCLASS(config=Engine, transient)
|
||||
class COMMONGAME_API UCommonLocalPlayer : public ULocalPlayer
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UCommonLocalPlayer();
|
||||
|
||||
/** Called when the local player is assigned a player controller */
|
||||
DECLARE_MULTICAST_DELEGATE_TwoParams(FPlayerControllerSetDelegate, UCommonLocalPlayer* LocalPlayer, APlayerController* PlayerController);
|
||||
FPlayerControllerSetDelegate OnPlayerControllerSet;
|
||||
|
||||
/** Called when the local player is assigned a player state */
|
||||
DECLARE_MULTICAST_DELEGATE_TwoParams(FPlayerStateSetDelegate, UCommonLocalPlayer* LocalPlayer, APlayerState* PlayerState);
|
||||
FPlayerStateSetDelegate OnPlayerStateSet;
|
||||
|
||||
/** Called when the local player is assigned a player pawn */
|
||||
DECLARE_MULTICAST_DELEGATE_TwoParams(FPlayerPawnSetDelegate, UCommonLocalPlayer* LocalPlayer, APawn* Pawn);
|
||||
FPlayerPawnSetDelegate OnPlayerPawnSet;
|
||||
|
||||
FDelegateHandle CallAndRegister_OnPlayerControllerSet(FPlayerControllerSetDelegate::FDelegate Delegate);
|
||||
FDelegateHandle CallAndRegister_OnPlayerStateSet(FPlayerStateSetDelegate::FDelegate Delegate);
|
||||
FDelegateHandle CallAndRegister_OnPlayerPawnSet(FPlayerPawnSetDelegate::FDelegate Delegate);
|
||||
|
||||
public:
|
||||
virtual bool GetProjectionData(FViewport* Viewport, FSceneViewProjectionData& ProjectionData, int32 StereoViewIndex) const override;
|
||||
|
||||
bool IsPlayerViewEnabled() const { return bIsPlayerViewEnabled; }
|
||||
void SetIsPlayerViewEnabled(bool bInIsPlayerViewEnabled) { bIsPlayerViewEnabled = bInIsPlayerViewEnabled; }
|
||||
|
||||
UPrimaryGameLayout* GetRootUILayout() const;
|
||||
|
||||
private:
|
||||
bool bIsPlayerViewEnabled = true;
|
||||
};
|
||||
27
Plugins/CommonGame/Source/Public/CommonPlayerController.h
Normal file
27
Plugins/CommonGame/Source/Public/CommonPlayerController.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ModularPlayerController.h"
|
||||
|
||||
#include "CommonPlayerController.generated.h"
|
||||
|
||||
class APawn;
|
||||
class UObject;
|
||||
|
||||
UCLASS(config=Game)
|
||||
class COMMONGAME_API ACommonPlayerController : public AModularPlayerController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ACommonPlayerController(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
||||
|
||||
virtual void ReceivedPlayer() override;
|
||||
virtual void SetPawn(APawn* InPawn) override;
|
||||
virtual void OnPossess(class APawn* APawn) override;
|
||||
virtual void OnUnPossess() override;
|
||||
|
||||
protected:
|
||||
virtual void OnRep_PlayerState() override;
|
||||
};
|
||||
228
Plugins/CommonGame/Source/Public/CommonPlayerInputKey.h
Normal file
228
Plugins/CommonGame/Source/Public/CommonPlayerInputKey.h
Normal file
@@ -0,0 +1,228 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CommonUserWidget.h"
|
||||
#include "Fonts/SlateFontInfo.h"
|
||||
|
||||
#include "CommonPlayerInputKey.generated.h"
|
||||
|
||||
enum class ECommonInputType : uint8;
|
||||
|
||||
class APlayerController;
|
||||
class FPaintArgs;
|
||||
class FSlateRect;
|
||||
class FSlateWindowElementList;
|
||||
class FWidgetStyle;
|
||||
class UCommonLocalPlayer;
|
||||
class UMaterialInstanceDynamic;
|
||||
class UObject;
|
||||
struct FFrame;
|
||||
struct FGeometry;
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class ECommonKeybindForcedHoldStatus : uint8
|
||||
{
|
||||
NoForcedHold,
|
||||
ForcedHold,
|
||||
NeverShowHold
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct FMeasuredText
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
FText GetText() const { return CachedText; }
|
||||
void SetText(const FText& InText);
|
||||
|
||||
FVector2D GetTextSize() const { return CachedTextSize; }
|
||||
FVector2D UpdateTextSize(const FSlateFontInfo &InFontInfo, float FontScale = 1.0f) const;
|
||||
|
||||
private:
|
||||
|
||||
FText CachedText;
|
||||
mutable FVector2D CachedTextSize;
|
||||
mutable bool bTextDirty = true;
|
||||
};
|
||||
|
||||
UCLASS(Abstract, BlueprintType, Blueprintable, meta = (DisableNativeTick))
|
||||
class COMMONGAME_API UCommonPlayerInputKey : public UCommonUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UCommonPlayerInputKey(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
/** Update the key and associated display based on our current Boundaction */
|
||||
UFUNCTION(BlueprintCallable, Category = "Keybind Widget")
|
||||
void UpdateKeybindWidget();
|
||||
|
||||
/** Set the bound key for our keybind */
|
||||
UFUNCTION(BlueprintCallable, Category = "Keybind Widget")
|
||||
void SetBoundKey(FKey NewBoundAction);
|
||||
|
||||
/** Set the bound action for our keybind */
|
||||
UFUNCTION(BlueprintCallable, Category = "Keybind Widget")
|
||||
void SetBoundAction(FName NewBoundAction);
|
||||
|
||||
/** Force this keybind to be a hold keybind */
|
||||
UFUNCTION(BlueprintCallable, Category = "Keybind Widget", meta=(DeprecatedFunction, DeprecationMessage = "Use SetForcedHoldKeybindStatus instead"))
|
||||
void SetForcedHoldKeybind(bool InForcedHoldKeybind);
|
||||
|
||||
/** Force this keybind to be a hold keybind */
|
||||
UFUNCTION(BlueprintCallable, Category = "Keybind Widget")
|
||||
void SetForcedHoldKeybindStatus(ECommonKeybindForcedHoldStatus InForcedHoldKeybindStatus);
|
||||
|
||||
/** Force this keybind to be a hold keybind */
|
||||
UFUNCTION(BlueprintCallable, Category = "Keybind Widget")
|
||||
void SetShowProgressCountDown(bool bShow);
|
||||
|
||||
/** Set the axis scale value for this keybind */
|
||||
UFUNCTION(BlueprintCallable, Category = "Keybind Widget")
|
||||
void SetAxisScale(const float NewValue) { AxisScale = NewValue; }
|
||||
|
||||
/** Set the preset name override value for this keybind. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Keybind Widget")
|
||||
void SetPresetNameOverride(const FName NewValue) { PresetNameOverride = NewValue; }
|
||||
|
||||
/** Our current BoundAction */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Keybind Widget")
|
||||
FName BoundAction;
|
||||
|
||||
/** Scale to read when using an axis Mapping */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Keybind Widget")
|
||||
float AxisScale;
|
||||
|
||||
/** Key this widget is bound to set directly in blueprint. Used when we want to reference a specific key instead of an action. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Keybind Widget")
|
||||
FKey BoundKeyFallback;
|
||||
|
||||
/** Allows us to set the input type explicitly for the keybind widget. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Keybind Widget")
|
||||
ECommonInputType InputTypeOverride;
|
||||
|
||||
/** Allows us to set the preset name explicitly for the keybind widget. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Keybind Widget")
|
||||
FName PresetNameOverride;
|
||||
|
||||
/** Setting that can show this keybind as a hold or never show it as a hold (even if it is) */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Keybind Widget")
|
||||
ECommonKeybindForcedHoldStatus ForcedHoldKeybindStatus;
|
||||
|
||||
/** Called through a delegate when we start hold progress */
|
||||
UFUNCTION()
|
||||
void StartHoldProgress(FName HoldActionName, float HoldDuration);
|
||||
|
||||
/** Called through a delegate when we stop hold progress */
|
||||
UFUNCTION()
|
||||
void StopHoldProgress(FName HoldActionName, bool bCompletedSuccessfully);
|
||||
|
||||
/** Get whether this keybind is a hold action. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Keybind Widget")
|
||||
bool IsHoldKeybind() const { return bIsHoldKeybind; }
|
||||
|
||||
UFUNCTION()
|
||||
bool IsBoundKeyValid() const { return BoundKey.IsValid(); }
|
||||
|
||||
protected:
|
||||
virtual void NativePreConstruct() override;
|
||||
virtual void NativeConstruct() override;
|
||||
virtual int32 NativePaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
|
||||
void RecalculateDesiredSize();
|
||||
|
||||
/** Overridden to destroy our MID */
|
||||
virtual void NativeDestruct() override;
|
||||
|
||||
/** Whether or not this keybind widget is currently set to be a hold keybind */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Keybind Widget", meta=(ScriptName = "IsHoldKeybindValue"))
|
||||
bool bIsHoldKeybind;
|
||||
|
||||
/** */
|
||||
UPROPERTY(Transient)
|
||||
bool bShowKeybindBorder;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FVector2D FrameSize;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Keybind Widget")
|
||||
bool bShowTimeCountDown;
|
||||
|
||||
/** Derived Key this widget is bound to */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Keybind Widget")
|
||||
FKey BoundKey;
|
||||
|
||||
/** Material for showing Progress */
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Keybind Widget")
|
||||
FSlateBrush HoldProgressBrush;
|
||||
|
||||
/** The key bind text border. */
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Keybind Widget")
|
||||
FSlateBrush KeyBindTextBorder;
|
||||
|
||||
/** Should this keybinding widget display information that it is currently unbound? */
|
||||
UPROPERTY(EditAnywhere, Category = "Keybind Widget")
|
||||
bool bShowUnboundStatus = false;
|
||||
|
||||
/** The font to apply at each size */
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Font")
|
||||
FSlateFontInfo KeyBindTextFont;
|
||||
|
||||
/** The font to apply at each size */
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Font")
|
||||
FSlateFontInfo CountdownTextFont;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FMeasuredText CountdownText;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FMeasuredText KeybindText;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FMargin KeybindTextPadding;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FVector2D KeybindFrameMinimumSize;
|
||||
|
||||
/** The material parameter name for hold percentage in the HoldKeybindImage */
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Keybind Widget")
|
||||
FName PercentageMaterialParameterName;
|
||||
|
||||
/** MID for the progress percentage */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UMaterialInstanceDynamic> ProgressPercentageMID;
|
||||
|
||||
virtual void NativeOnInitialized() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Synchronizes the hold progress to whatever is currently set in the
|
||||
* owning player controller.
|
||||
*/
|
||||
void SyncHoldProgress();
|
||||
|
||||
/** Called for updating the HoldKeybindImage during a hold keybind */
|
||||
void UpdateHoldProgress();
|
||||
|
||||
/** Called when we want to set up this keybind widget as a hold keybind */
|
||||
void SetupHoldKeybind();
|
||||
|
||||
void ShowHoldBackPlate();
|
||||
|
||||
void HandlePlayerControllerSet(UCommonLocalPlayer* LocalPlayer, APlayerController* PlayerController);
|
||||
|
||||
/** Time when we started using a hold keybind */
|
||||
float HoldKeybindStartTime = 0;
|
||||
|
||||
/** How long, in seconds, we will be doing a hold keybind */
|
||||
float HoldKeybindDuration = 0;
|
||||
|
||||
bool bDrawProgress = false;
|
||||
bool bDrawBrushForKey = false;
|
||||
bool bDrawCountdownText = false;
|
||||
bool bWaitingForPlayerController = false;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FSlateBrush CachedKeyBrush;
|
||||
};
|
||||
62
Plugins/CommonGame/Source/Public/CommonUIExtensions.h
Normal file
62
Plugins/CommonGame/Source/Public/CommonUIExtensions.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "UObject/SoftObjectPtr.h"
|
||||
|
||||
#include "CommonUIExtensions.generated.h"
|
||||
|
||||
enum class ECommonInputType : uint8;
|
||||
template <typename T> class TSubclassOf;
|
||||
|
||||
class APlayerController;
|
||||
class UCommonActivatableWidget;
|
||||
class ULocalPlayer;
|
||||
class UObject;
|
||||
class UUserWidget;
|
||||
struct FFrame;
|
||||
struct FGameplayTag;
|
||||
|
||||
UCLASS()
|
||||
class COMMONGAME_API UCommonUIExtensions : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UCommonUIExtensions() { }
|
||||
|
||||
UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "Global UI Extensions", meta = (WorldContext = "WidgetContextObject"))
|
||||
static ECommonInputType GetOwningPlayerInputType(const UUserWidget* WidgetContextObject);
|
||||
|
||||
UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "Global UI Extensions", meta = (WorldContext = "WidgetContextObject"))
|
||||
static bool IsOwningPlayerUsingTouch(const UUserWidget* WidgetContextObject);
|
||||
|
||||
UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "Global UI Extensions", meta = (WorldContext = "WidgetContextObject"))
|
||||
static bool IsOwningPlayerUsingGamepad(const UUserWidget* WidgetContextObject);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "Global UI Extensions")
|
||||
static UCommonActivatableWidget* PushContentToLayer_ForPlayer(const ULocalPlayer* LocalPlayer, UPARAM(meta = (Categories = "UI.Layer")) FGameplayTag LayerName, UPARAM(meta = (AllowAbstract = false)) TSubclassOf<UCommonActivatableWidget> WidgetClass);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "Global UI Extensions")
|
||||
static void PushStreamedContentToLayer_ForPlayer(const ULocalPlayer* LocalPlayer, UPARAM(meta = (Categories = "UI.Layer")) FGameplayTag LayerName, UPARAM(meta = (AllowAbstract = false)) TSoftClassPtr<UCommonActivatableWidget> WidgetClass);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "Global UI Extensions")
|
||||
static void PopContentFromLayer(UCommonActivatableWidget* ActivatableWidget);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "Global UI Extensions")
|
||||
static ULocalPlayer* GetLocalPlayerFromController(APlayerController* PlayerController);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "Global UI Extensions")
|
||||
static FName SuspendInputForPlayer(APlayerController* PlayerController, FName SuspendReason);
|
||||
|
||||
static FName SuspendInputForPlayer(ULocalPlayer* LocalPlayer, FName SuspendReason);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "Global UI Extensions")
|
||||
static void ResumeInputForPlayer(APlayerController* PlayerController, FName SuspendToken);
|
||||
|
||||
static void ResumeInputForPlayer(ULocalPlayer* LocalPlayer, FName SuspendToken);
|
||||
|
||||
private:
|
||||
static int32 InputSuspensions;
|
||||
};
|
||||
50
Plugins/CommonGame/Source/Public/GameUIManagerSubsystem.h
Normal file
50
Plugins/CommonGame/Source/Public/GameUIManagerSubsystem.h
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Subsystems/GameInstanceSubsystem.h"
|
||||
#include "UObject/SoftObjectPtr.h"
|
||||
|
||||
#include "GameUIManagerSubsystem.generated.h"
|
||||
|
||||
class FSubsystemCollectionBase;
|
||||
class UCommonLocalPlayer;
|
||||
class UGameUIPolicy;
|
||||
class UObject;
|
||||
|
||||
/**
|
||||
* This manager is intended to be replaced by whatever your game needs to
|
||||
* actually create, so this class is abstract to prevent it from being created.
|
||||
*
|
||||
* If you just need the basic functionality you will start by sublcassing this
|
||||
* subsystem in your own game.
|
||||
*/
|
||||
UCLASS(Abstract, config = Game)
|
||||
class COMMONGAME_API UGameUIManagerSubsystem : public UGameInstanceSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGameUIManagerSubsystem() { }
|
||||
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
|
||||
|
||||
const UGameUIPolicy* GetCurrentUIPolicy() const { return CurrentPolicy; }
|
||||
UGameUIPolicy* GetCurrentUIPolicy() { return CurrentPolicy; }
|
||||
|
||||
virtual void NotifyPlayerAdded(UCommonLocalPlayer* LocalPlayer);
|
||||
virtual void NotifyPlayerRemoved(UCommonLocalPlayer* LocalPlayer);
|
||||
virtual void NotifyPlayerDestroyed(UCommonLocalPlayer* LocalPlayer);
|
||||
|
||||
protected:
|
||||
void SwitchToPolicy(UGameUIPolicy* InPolicy);
|
||||
|
||||
private:
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UGameUIPolicy> CurrentPolicy = nullptr;
|
||||
|
||||
UPROPERTY(config, EditAnywhere)
|
||||
TSoftClassPtr<UGameUIPolicy> DefaultUIPolicyClass;
|
||||
};
|
||||
103
Plugins/CommonGame/Source/Public/GameUIPolicy.h
Normal file
103
Plugins/CommonGame/Source/Public/GameUIPolicy.h
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/World.h"
|
||||
|
||||
#include "GameUIPolicy.generated.h"
|
||||
|
||||
class UCommonLocalPlayer;
|
||||
class UGameUIManagerSubsystem;
|
||||
class ULocalPlayer;
|
||||
class UPrimaryGameLayout;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UENUM()
|
||||
enum class ELocalMultiplayerInteractionMode : uint8
|
||||
{
|
||||
// Fullscreen viewport for the primary player only, regardless of the other player's existence
|
||||
PrimaryOnly,
|
||||
|
||||
// Fullscreen viewport for one player, but players can swap control over who's is displayed and who's is dormant
|
||||
SingleToggle,
|
||||
|
||||
// Viewports displayed simultaneously for both players
|
||||
Simultaneous
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct FRootViewportLayoutInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<ULocalPlayer> LocalPlayer = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UPrimaryGameLayout> RootLayout = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
bool bAddedToViewport = false;
|
||||
|
||||
FRootViewportLayoutInfo() {}
|
||||
FRootViewportLayoutInfo(ULocalPlayer* InLocalPlayer, UPrimaryGameLayout* InRootLayout, bool bIsInViewport)
|
||||
: LocalPlayer(InLocalPlayer)
|
||||
, RootLayout(InRootLayout)
|
||||
, bAddedToViewport(bIsInViewport)
|
||||
{}
|
||||
|
||||
bool operator==(const ULocalPlayer* OtherLocalPlayer) const { return LocalPlayer == OtherLocalPlayer; }
|
||||
};
|
||||
|
||||
UCLASS(Abstract, Blueprintable, Within = GameUIManagerSubsystem)
|
||||
class COMMONGAME_API UGameUIPolicy : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
template <typename GameUIPolicyClass = UGameUIPolicy>
|
||||
static GameUIPolicyClass* GetGameUIPolicyAs(const UObject* WorldContextObject)
|
||||
{
|
||||
return Cast<GameUIPolicyClass>(GetGameUIPolicy(WorldContextObject));
|
||||
}
|
||||
|
||||
static UGameUIPolicy* GetGameUIPolicy(const UObject* WorldContextObject);
|
||||
|
||||
public:
|
||||
virtual UWorld* GetWorld() const override;
|
||||
UGameUIManagerSubsystem* GetOwningUIManager() const;
|
||||
UPrimaryGameLayout* GetRootLayout(const UCommonLocalPlayer* LocalPlayer) const;
|
||||
|
||||
ELocalMultiplayerInteractionMode GetLocalMultiplayerInteractionMode() const { return LocalMultiplayerInteractionMode; }
|
||||
|
||||
void RequestPrimaryControl(UPrimaryGameLayout* Layout);
|
||||
|
||||
protected:
|
||||
void AddLayoutToViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout);
|
||||
void RemoveLayoutFromViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout);
|
||||
|
||||
virtual void OnRootLayoutAddedToViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout);
|
||||
virtual void OnRootLayoutRemovedFromViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout);
|
||||
virtual void OnRootLayoutReleased(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout);
|
||||
|
||||
void CreateLayoutWidget(UCommonLocalPlayer* LocalPlayer);
|
||||
TSubclassOf<UPrimaryGameLayout> GetLayoutWidgetClass(UCommonLocalPlayer* LocalPlayer);
|
||||
|
||||
private:
|
||||
ELocalMultiplayerInteractionMode LocalMultiplayerInteractionMode = ELocalMultiplayerInteractionMode::PrimaryOnly;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
TSoftClassPtr<UPrimaryGameLayout> LayoutClass;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TArray<FRootViewportLayoutInfo> RootViewportLayouts;
|
||||
|
||||
private:
|
||||
void NotifyPlayerAdded(UCommonLocalPlayer* LocalPlayer);
|
||||
void NotifyPlayerRemoved(UCommonLocalPlayer* LocalPlayer);
|
||||
void NotifyPlayerDestroyed(UCommonLocalPlayer* LocalPlayer);
|
||||
|
||||
friend class UGameUIManagerSubsystem;
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CommonActivatableWidget.h"
|
||||
#include "CommonMessagingSubsystem.h"
|
||||
|
||||
#include "CommonGameDialog.generated.h"
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FConfirmationDialogAction
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Required: The dialog option to provide. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
ECommonMessagingResult Result = ECommonMessagingResult::Unknown;
|
||||
|
||||
/** Optional: Display Text to use instead of the action name associated with the result. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FText OptionalDisplayText;
|
||||
|
||||
bool operator==(const FConfirmationDialogAction& Other) const
|
||||
{
|
||||
return Result == Other.Result &&
|
||||
OptionalDisplayText.EqualTo(Other.OptionalDisplayText);
|
||||
}
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class COMMONGAME_API UCommonGameDialogDescriptor : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
static UCommonGameDialogDescriptor* CreateConfirmationOk(const FText& Header, const FText& Body);
|
||||
static UCommonGameDialogDescriptor* CreateConfirmationOkCancel(const FText& Header, const FText& Body);
|
||||
static UCommonGameDialogDescriptor* CreateConfirmationYesNo(const FText& Header, const FText& Body);
|
||||
static UCommonGameDialogDescriptor* CreateConfirmationYesNoCancel(const FText& Header, const FText& Body);
|
||||
|
||||
public:
|
||||
/** The header of the message to display */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FText Header;
|
||||
|
||||
/** The body of the message to display */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FText Body;
|
||||
|
||||
/** The confirm button's input action to use. */
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
TArray<FConfirmationDialogAction> ButtonActions;
|
||||
};
|
||||
|
||||
|
||||
UCLASS(Abstract)
|
||||
class COMMONGAME_API UCommonGameDialog : public UCommonActivatableWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UCommonGameDialog();
|
||||
|
||||
virtual void SetupDialog(UCommonGameDialogDescriptor* Descriptor, FCommonMessagingResultDelegate ResultCallback);
|
||||
|
||||
virtual void KillDialog();
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Subsystems/LocalPlayerSubsystem.h"
|
||||
|
||||
#include "CommonMessagingSubsystem.generated.h"
|
||||
|
||||
class FSubsystemCollectionBase;
|
||||
class UCommonGameDialogDescriptor;
|
||||
class UObject;
|
||||
|
||||
/** Possible results from a dialog */
|
||||
UENUM(BlueprintType)
|
||||
enum class ECommonMessagingResult : uint8
|
||||
{
|
||||
/** The "yes" button was pressed */
|
||||
Confirmed,
|
||||
/** The "no" button was pressed */
|
||||
Declined,
|
||||
/** The "ignore/cancel" button was pressed */
|
||||
Cancelled,
|
||||
/** The dialog was explicitly killed (no user input) */
|
||||
Killed,
|
||||
Unknown UMETA(Hidden)
|
||||
};
|
||||
|
||||
DECLARE_DELEGATE_OneParam(FCommonMessagingResultDelegate, ECommonMessagingResult /* Result */);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(config = Game)
|
||||
class COMMONGAME_API UCommonMessagingSubsystem : public ULocalPlayerSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UCommonMessagingSubsystem() { }
|
||||
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
|
||||
|
||||
virtual void ShowConfirmation(UCommonGameDialogDescriptor* DialogDescriptor, FCommonMessagingResultDelegate ResultCallback = FCommonMessagingResultDelegate());
|
||||
virtual void ShowError(UCommonGameDialogDescriptor* DialogDescriptor, FCommonMessagingResultDelegate ResultCallback = FCommonMessagingResultDelegate());
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
137
Plugins/CommonGame/Source/Public/PrimaryGameLayout.h
Normal file
137
Plugins/CommonGame/Source/Public/PrimaryGameLayout.h
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CommonActivatableWidget.h"
|
||||
#include "CommonUIExtensions.h"
|
||||
#include "Engine/AssetManager.h"
|
||||
#include "Engine/StreamableManager.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Widgets/CommonActivatableWidgetContainer.h" // IWYU pragma: keep
|
||||
|
||||
#include "PrimaryGameLayout.generated.h"
|
||||
|
||||
class APlayerController;
|
||||
class UClass;
|
||||
class UCommonActivatableWidgetContainerBase;
|
||||
class ULocalPlayer;
|
||||
class UObject;
|
||||
struct FFrame;
|
||||
|
||||
/**
|
||||
* The state of an async load operation for the UI.
|
||||
*/
|
||||
enum class EAsyncWidgetLayerState : uint8
|
||||
{
|
||||
Canceled,
|
||||
Initialize,
|
||||
AfterPush
|
||||
};
|
||||
|
||||
/**
|
||||
* The primary game UI layout of your game. This widget class represents how to layout, push and display all layers
|
||||
* of the UI for a single player. Each player in a split-screen game will receive their own primary game layout.
|
||||
*/
|
||||
UCLASS(Abstract, meta = (DisableNativeTick))
|
||||
class COMMONGAME_API UPrimaryGameLayout : public UCommonUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
static UPrimaryGameLayout* GetPrimaryGameLayoutForPrimaryPlayer(const UObject* WorldContextObject);
|
||||
static UPrimaryGameLayout* GetPrimaryGameLayout(APlayerController* PlayerController);
|
||||
static UPrimaryGameLayout* GetPrimaryGameLayout(ULocalPlayer* LocalPlayer);
|
||||
|
||||
public:
|
||||
UPrimaryGameLayout(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
/** A dormant root layout is collapsed and responds only to persistent actions registered by the owning player */
|
||||
void SetIsDormant(bool Dormant);
|
||||
bool IsDormant() const { return bIsDormant; }
|
||||
|
||||
public:
|
||||
template <typename ActivatableWidgetT = UCommonActivatableWidget>
|
||||
TSharedPtr<FStreamableHandle> PushWidgetToLayerStackAsync(FGameplayTag LayerName, bool bSuspendInputUntilComplete, TSoftClassPtr<UCommonActivatableWidget> ActivatableWidgetClass)
|
||||
{
|
||||
return PushWidgetToLayerStackAsync<ActivatableWidgetT>(LayerName, bSuspendInputUntilComplete, ActivatableWidgetClass, [](EAsyncWidgetLayerState, ActivatableWidgetT*) {});
|
||||
}
|
||||
|
||||
template <typename ActivatableWidgetT = UCommonActivatableWidget>
|
||||
TSharedPtr<FStreamableHandle> PushWidgetToLayerStackAsync(FGameplayTag LayerName, bool bSuspendInputUntilComplete, TSoftClassPtr<UCommonActivatableWidget> ActivatableWidgetClass, TFunction<void(EAsyncWidgetLayerState, ActivatableWidgetT*)> StateFunc)
|
||||
{
|
||||
static_assert(TIsDerivedFrom<ActivatableWidgetT, UCommonActivatableWidget>::IsDerived, "Only CommonActivatableWidgets can be used here");
|
||||
|
||||
static FName NAME_PushingWidgetToLayer("PushingWidgetToLayer");
|
||||
const FName SuspendInputToken = bSuspendInputUntilComplete ? UCommonUIExtensions::SuspendInputForPlayer(GetOwningPlayer(), NAME_PushingWidgetToLayer) : NAME_None;
|
||||
|
||||
FStreamableManager& StreamableManager = UAssetManager::Get().GetStreamableManager();
|
||||
TSharedPtr<FStreamableHandle> StreamingHandle = StreamableManager.RequestAsyncLoad(ActivatableWidgetClass.ToSoftObjectPath(), FStreamableDelegate::CreateWeakLambda(this,
|
||||
[this, LayerName, ActivatableWidgetClass, StateFunc, SuspendInputToken]()
|
||||
{
|
||||
UCommonUIExtensions::ResumeInputForPlayer(GetOwningPlayer(), SuspendInputToken);
|
||||
|
||||
ActivatableWidgetT* Widget = PushWidgetToLayerStack<ActivatableWidgetT>(LayerName, ActivatableWidgetClass.Get(), [StateFunc](ActivatableWidgetT& WidgetToInit) {
|
||||
StateFunc(EAsyncWidgetLayerState::Initialize, &WidgetToInit);
|
||||
});
|
||||
|
||||
StateFunc(EAsyncWidgetLayerState::AfterPush, Widget);
|
||||
})
|
||||
);
|
||||
|
||||
// Setup a cancel delegate so that we can resume input if this handler is canceled.
|
||||
StreamingHandle->BindCancelDelegate(FStreamableDelegate::CreateWeakLambda(this,
|
||||
[this, StateFunc, SuspendInputToken]()
|
||||
{
|
||||
UCommonUIExtensions::ResumeInputForPlayer(GetOwningPlayer(), SuspendInputToken);
|
||||
StateFunc(EAsyncWidgetLayerState::Canceled, nullptr);
|
||||
})
|
||||
);
|
||||
|
||||
return StreamingHandle;
|
||||
}
|
||||
|
||||
template <typename ActivatableWidgetT = UCommonActivatableWidget>
|
||||
ActivatableWidgetT* PushWidgetToLayerStack(FGameplayTag LayerName, UClass* ActivatableWidgetClass)
|
||||
{
|
||||
return PushWidgetToLayerStack<ActivatableWidgetT>(LayerName, ActivatableWidgetClass, [](ActivatableWidgetT&) {});
|
||||
}
|
||||
|
||||
template <typename ActivatableWidgetT = UCommonActivatableWidget>
|
||||
ActivatableWidgetT* PushWidgetToLayerStack(FGameplayTag LayerName, UClass* ActivatableWidgetClass, TFunctionRef<void(ActivatableWidgetT&)> InitInstanceFunc)
|
||||
{
|
||||
static_assert(TIsDerivedFrom<ActivatableWidgetT, UCommonActivatableWidget>::IsDerived, "Only CommonActivatableWidgets can be used here");
|
||||
|
||||
if (UCommonActivatableWidgetContainerBase* Layer = GetLayerWidget(LayerName))
|
||||
{
|
||||
return Layer->AddWidget<ActivatableWidgetT>(ActivatableWidgetClass, InitInstanceFunc);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Find the widget if it exists on any of the layers and remove it from the layer.
|
||||
void FindAndRemoveWidgetFromLayer(UCommonActivatableWidget* ActivatableWidget);
|
||||
|
||||
// Get the layer widget for the given layer tag.
|
||||
UCommonActivatableWidgetContainerBase* GetLayerWidget(FGameplayTag LayerName);
|
||||
|
||||
protected:
|
||||
/** Register a layer that widgets can be pushed onto. */
|
||||
UFUNCTION(BlueprintCallable, Category="Layer")
|
||||
void RegisterLayer(UPARAM(meta = (Categories = "UI.Layer")) FGameplayTag LayerTag, UCommonActivatableWidgetContainerBase* LayerWidget);
|
||||
|
||||
virtual void OnIsDormantChanged();
|
||||
|
||||
void OnWidgetStackTransitioning(UCommonActivatableWidgetContainerBase* Widget, bool bIsTransitioning);
|
||||
|
||||
private:
|
||||
bool bIsDormant = false;
|
||||
|
||||
// Lets us keep track of all suspended input tokens so that multiple async UIs can be loading and we correctly suspend
|
||||
// for the duration of all of them.
|
||||
TArray<FName> SuspendInputTokens;
|
||||
|
||||
// The registered layers for the primary layout.
|
||||
UPROPERTY(Transient, meta = (Categories = "UI.Layer"))
|
||||
TMap<FGameplayTag, TObjectPtr<UCommonActivatableWidgetContainerBase>> Layers;
|
||||
};
|
||||
Reference in New Issue
Block a user