搬了lyra的hudlayout
This commit is contained in:
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user