QTE, crash fixes, bug fixes, optimizations, warnings clearing

This commit is contained in:
Oleg Petruny 2024-09-05 18:43:40 +02:00
parent 1e277a3b5b
commit a58ef0abcd
43 changed files with 828 additions and 155 deletions

View File

@ -5,7 +5,7 @@ GameDefaultMap=/Game/Levels/Test/L_Test.L_Test
EditorStartupMap=/Game/Levels/Test/L_Test.L_Test
bUseSplitscreen=False
GlobalDefaultGameMode=/Game/Blueprints/GameModes/BP_MainGameMode.BP_MainGameMode_C
GameInstanceClass=/Game/Blueprints/Other/BP_CustomGameInstance.BP_CustomGameInstance_C
GameInstanceClass=/Game/Blueprints/BP_CustomGameInstance.BP_CustomGameInstance_C
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12

View File

@ -78,6 +78,7 @@ DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown
DefaultViewportMouseLockMode=LockOnCapture
FOVScale=0.011110
DoubleClickTime=0.200000
+ActionMappings=(ActionName="AnyKey",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=AnyKey)
DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput
DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent
DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks

BIN
Content/Blueprints/BP_CustomGameInstance.uasset (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Levels/Test/L_Test.umap (Stored with Git LFS)

Binary file not shown.

BIN
Content/Levels/Test/Sequences/Seq_Test_Ball.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Levels/Test/Sequences/Seq_Test_Cutscene.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/UI/BP_MainWidgetManager.uasset (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -7,6 +7,7 @@
#include "Kismet/GameplayStatics.h"
#include "UObject/Object.h"
#include "CustomGameInstanceBase.h"
#include "Levels/LevelBase.h"
#include "PlayerBase.h"
@ -42,6 +43,7 @@ void UCommonFunctions::DestroyActorRecursively(AActor* actor)
ALevelBase* UCommonFunctions::GetCurrentLevelScript(UObject* obj)
{
if(obj)
if(auto world = obj->GetWorld())
if(auto level = world->GetCurrentLevel())
if(auto script = level->GetLevelScriptActor())
@ -51,8 +53,68 @@ ALevelBase* UCommonFunctions::GetCurrentLevelScript(UObject* obj)
APlayerBase* UCommonFunctions::GetPlayer(UObject* obj)
{
if(obj)
if(auto pc = UGameplayStatics::GetPlayerController(obj->GetWorld(), 0))
if(auto pawn = pc->GetPawn())
return Cast<APlayerBase>(pawn);
return nullptr;
}
namespace SlowMotion
{
FTimerHandle timer;
float targetDilation;
FWorldDilationChangedDelegate worldDilationChangedDelegate;
}
void UCommonFunctions::EnterSlowMotion(float duration)
{
if(SlowMotion::targetDilation == SlowMotion::slowDilation)
return;
SlowMotion::targetDilation = SlowMotion::slowDilation;
auto GI = UCustomGameInstanceBase::GetGameInstance();
if(!GI)
return;
auto world = GI->GetWorld();
world->GetTimerManager().SetTimer(SlowMotion::timer, []() { SlowMotionTick(); }, 0.01f, true); // wrapped in lambda due to some template function class referencing
}
void UCommonFunctions::ExitSlowMotion(float duration)
{
if(SlowMotion::targetDilation == SlowMotion::normalDilation)
return;
SlowMotion::targetDilation = SlowMotion::normalDilation;
auto GI = UCustomGameInstanceBase::GetGameInstance();
if(!GI)
return;
auto world = GI->GetWorld();
world->GetTimerManager().SetTimer(SlowMotion::timer, []() { SlowMotionTick(); }, 0.01f, true);
}
FWorldDilationChangedDelegate& UCommonFunctions::GetWorldDilationChangedDelegate()
{
return SlowMotion::worldDilationChangedDelegate;
}
void UCommonFunctions::SlowMotionTick()
{
const UWorld* world = UCustomGameInstanceBase::GetGameInstance()->GetWorld();
const float currentDilation = UGameplayStatics::GetGlobalTimeDilation(world);
float newDilation = FMath::FInterpTo(currentDilation, SlowMotion::targetDilation, 1, SlowMotion::interpolationSpeed);
UGameplayStatics::SetGlobalTimeDilation(world, newDilation);
SlowMotion::worldDilationChangedDelegate.Broadcast(newDilation);
if(FMath::IsNearlyEqual(newDilation, SlowMotion::targetDilation, UE_KINDA_SMALL_NUMBER))
{
UGameplayStatics::SetGlobalTimeDilation(world, SlowMotion::targetDilation);
world->GetTimerManager().ClearTimer(SlowMotion::timer);
}
}

View File

@ -6,6 +6,8 @@
#include "CommonFunctions.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FWorldDilationChangedDelegate, float, newDilation);
UCLASS()
class UCommonFunctions : public UBlueprintFunctionLibrary
{
@ -26,4 +28,23 @@ public:
UFUNCTION(BlueprintCallable, Category = Player)
static class APlayerBase* GetPlayer(class UObject* obj);
UFUNCTION(BlueprintCallable, Category = World)
static void EnterSlowMotion(float duration = 1.0f);
UFUNCTION(BlueprintCallable, Category = World)
static void ExitSlowMotion(float duration = 1.0f);
static FWorldDilationChangedDelegate& GetWorldDilationChangedDelegate();
private:
static void SlowMotionTick();
};
namespace SlowMotion
{
constexpr float normalDilation = 1.0f;
constexpr float slowDilation = 0.2f;
constexpr float interpolationSpeed = 0.1f;
}

View File

@ -14,8 +14,6 @@
void ALevel3::BeginPlay()
{
AMainGameModeBase::leadLevel = this;
ALevelBase::BeginPlay();
CallNextState();

View File

@ -14,8 +14,6 @@
void ALevel4::BeginPlay()
{
AMainGameModeBase::leadLevel = this;
ALevelBase::BeginPlay();
CallNextState();

View File

@ -14,8 +14,6 @@
void ALevel5::BeginPlay()
{
AMainGameModeBase::leadLevel = this;
ALevelBase::BeginPlay();
CallNextState();

View File

@ -18,7 +18,7 @@
void ALevelBase::BeginPlay()
{
AMainGameModeBase::leadLevel = this;
AMainGameModeBase::leadLevel = TStrongObjectPtr<ALevelBase>{ this };
if(auto world = GetWorld())
{

View File

@ -13,20 +13,23 @@
#include "DialogueManager.h"
#include "Levels/LevelBase.h"
#include "QuestManager.h"
#include "QuickTimeEvent.h"
#include "Widgets/WidgetsManager.h"
UWidgetsManager* AMainGameModeBase::_widgetsManager = nullptr;
UCutsceneManager* AMainGameModeBase::_cutsceneManager = nullptr;
UDialogueManager* AMainGameModeBase::_dialogueManager = nullptr;
UQuestManager* AMainGameModeBase::_questManager = nullptr;
ALevelBase* AMainGameModeBase::leadLevel = nullptr;
TStrongObjectPtr<UWidgetsManager> AMainGameModeBase::_widgetsManager = nullptr;
TStrongObjectPtr<UCutsceneManager> AMainGameModeBase::_cutsceneManager = nullptr;
TStrongObjectPtr<UQuickTimeEventManager> AMainGameModeBase::_quickTimeEventManager = nullptr;
TStrongObjectPtr<UDialogueManager> AMainGameModeBase::_dialogueManager = nullptr;
TStrongObjectPtr<UQuestManager> AMainGameModeBase::_questManager = nullptr;
TStrongObjectPtr<ALevelBase> AMainGameModeBase::leadLevel = nullptr;
void AMainGameModeBase::StartPlay()
{
_widgetsManager = NewObject<UWidgetsManager>(this, widgetManagerClass);
_cutsceneManager = NewObject<UCutsceneManager>(this);
_dialogueManager = NewObject<UDialogueManager>(this);
_questManager = NewObject<UQuestManager>(this);
_widgetsManager = TStrongObjectPtr<UWidgetsManager>{ NewObject<UWidgetsManager>(this, widgetManagerClass) };
_cutsceneManager = TStrongObjectPtr<UCutsceneManager>{ NewObject<UCutsceneManager>(this) };
_quickTimeEventManager = TStrongObjectPtr<UQuickTimeEventManager>{ NewObject<UQuickTimeEventManager>(this) };
_dialogueManager = TStrongObjectPtr<UDialogueManager>{ NewObject<UDialogueManager>(this) };
_questManager = TStrongObjectPtr<UQuestManager>{ NewObject<UQuestManager>(this) };
AGameModeBase::StartPlay();
@ -58,16 +61,21 @@ bool AMainGameModeBase::SetPause(APlayerController* PC, FCanUnpause CanUnpauseDe
UWidgetsManager* AMainGameModeBase::GetWidgetsManager()
{
return _widgetsManager;
return _widgetsManager.Get();
}
UCutsceneManager* AMainGameModeBase::GetCutsceneManager()
{
return _cutsceneManager;
return _cutsceneManager.Get();
}
UQuickTimeEventManager* AMainGameModeBase::GetQuickTimeEventManager()
{
return _quickTimeEventManager.Get();
}
UDialogueManager* AMainGameModeBase::GetDialogueManager()
{
return _dialogueManager;
return _dialogueManager.Get();
}
void AMainGameModeBase::CallNextLevelState()

View File

@ -23,6 +23,8 @@ public:
UFUNCTION(BlueprintPure)
static class UCutsceneManager* GetCutsceneManager();
UFUNCTION(BlueprintPure)
static class UQuickTimeEventManager* GetQuickTimeEventManager();
UFUNCTION(BlueprintPure)
static class UDialogueManager* GetDialogueManager();
UFUNCTION(BlueprintCallable)
@ -32,7 +34,7 @@ public:
void SwitchCameraMode();
static class ALevelBase* leadLevel;
static TStrongObjectPtr<class ALevelBase> leadLevel;
FQuestsUpdateDelegate questsUpdateDelegate;
@ -41,8 +43,9 @@ protected:
TSubclassOf<class UWidgetsManager> widgetManagerClass;
private:
static class UWidgetsManager* _widgetsManager;
static class UCutsceneManager* _cutsceneManager;
static class UDialogueManager* _dialogueManager;
static class UQuestManager* _questManager;
static TStrongObjectPtr<class UWidgetsManager> _widgetsManager;
static TStrongObjectPtr<class UCutsceneManager> _cutsceneManager;
static TStrongObjectPtr<class UQuickTimeEventManager> _quickTimeEventManager;
static TStrongObjectPtr<class UDialogueManager> _dialogueManager;
static TStrongObjectPtr<class UQuestManager> _questManager;
};

View File

@ -12,6 +12,7 @@
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"
#include "CustomGameInstanceBase.h"
#include "CustomGameUserSettings.h"
#include "Interactable/Activators/InteractableActivator.h"
@ -87,8 +88,6 @@ void APlayerBase::BeginPlay()
playerController = Cast<APlayerController>(GetController());
if(playerController)
{
inputComponent = Cast<UEnhancedInputComponent>(playerController->InputComponent);;
if(auto inputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(playerController->GetLocalPlayer()))
{
if(auto GI = Cast<UCustomGameInstanceBase>(GetWorld()->GetGameInstance()))
@ -100,11 +99,30 @@ void APlayerBase::BeginPlay()
}
}
}
inputComponent = Cast<UEnhancedInputComponent>(playerController->InputComponent);
if(inputComponent)
{
((UInputComponent*)inputComponent)->BindAction(TEXT("AnyKey"), IE_Pressed, this, &APlayerBase::OnAnyKeyPressed);
((UInputComponent*)inputComponent)->BindAction(TEXT("AnyKey"), IE_Released, this, &APlayerBase::OnAnyKeyReleased);
}
}
LoadInteractablesActivators();
}
void APlayerBase::OnAnyKeyPressed(FKey key)
{
if(onAnyKeyPressedDelegate.IsBound())
onAnyKeyPressedDelegate.Broadcast(key);
}
void APlayerBase::OnAnyKeyReleased(FKey key)
{
if(onAnyKeyReleasedDelegate.IsBound())
onAnyKeyReleasedDelegate.Broadcast(key);
}
bool APlayerBase::IsMoving()
{
return bIsMoving;

View File

@ -8,6 +8,8 @@
enum class EActivatorType : uint8;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPlayerAnyKeyPressedDelegate, FKey, key);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPlayerAnyKeyReleasedDelegate, FKey, key);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FPlayerMovedDelegate);
USTRUCT(BlueprintType)
@ -63,6 +65,9 @@ public:
FPlayerMovedDelegate OnPlayerMoved;
FVector moveVector;
FPlayerAnyKeyPressedDelegate onAnyKeyPressedDelegate;
FPlayerAnyKeyReleasedDelegate onAnyKeyReleasedDelegate;
class UEnhancedInputComponent* inputComponent = nullptr;
class AActor* leftPocketItem = nullptr;
@ -71,6 +76,9 @@ public:
protected:
virtual void BeginPlay() override;
void OnAnyKeyPressed(FKey key);
void OnAnyKeyReleased(FKey key);
UFUNCTION(BlueprintCallable, Category = CameraMode)
void SwitchToCameraPawn();

View File

@ -0,0 +1,182 @@
// Oleg Petruny proprietary.
#include "QuickTimeEvent.h"
#include "Kismet/GameplayStatics.h"
#include "CommonFunctions.h"
#include "MainGameModeBase.h"
#include "PlayerBase.h"
#include "Widgets/WidgetsManager.h"
int32 Event::counter = 0;
Event::Event()
{
_id = counter++;
}
Event::Event(EQuickTimeEventType type, FKey& key, FQuickTimeEventEndCallback& callback, bool sequence)
: Event()
{
this->type = type;
this->key = key;
this->callback = callback;
this->sequence = sequence;
}
inline int32 Event::GetId()
{
return _id;
}
void UQuickTimeEventManager::ShowQuickTimeEvent(FQuickTimeEventEnqueProperties properties)
{
UE_LOG(LogTemp, Log, TEXT("ShowQuickTimeEvent Start"));
OnFirstEventInit();
FScopeLock lock1(&_lock);
CreateEvent(properties, false);
UE_LOG(LogTemp, Log, TEXT("ShowQuickTimeEvent End"));
}
void UQuickTimeEventManager::EnqueQuickTimeEvent(FQuickTimeEventEnqueProperties properties)
{
UE_LOG(LogTemp, Log, TEXT("EnqueQuickTimeEvent Start"));
OnFirstEventInit();
{
FScopeLock lock1(&_lock);
_nextEvents.Enqueue(properties);
}
ShowNextEvent();
UE_LOG(LogTemp, Log, TEXT("EnqueQuickTimeEvent End"));
}
void UQuickTimeEventManager::ShowNextEvent()
{
UE_LOG(LogTemp, Log, TEXT("ShowNextEvent Start"));
FScopeLock lock1(&_lock);
FQuickTimeEventEnqueProperties properties;
_nextEvents.Dequeue(properties);
CreateEvent(properties, true);
UE_LOG(LogTemp, Log, TEXT("ShowNextEvent End"));
}
void UQuickTimeEventManager::OnEventEnd(int32 id, EQuickTimeEventResult result)
{
UE_LOG(LogTemp, Log, TEXT("OnEventEnd Start"));
FScopeLock lock1(&_lock);
Event event;
if(!_events.RemoveAndCopyValue(id, event))
return;
GetWorld()->GetTimerManager().ClearTimer(event.timer);
if(event.callback.IsBound())
event.callback.Execute(result);
if(event.sequence && !_nextEvents.IsEmpty())
{
ShowNextEvent();
UE_LOG(LogTemp, Log, TEXT("OnEventEnd End"));
return;
}
if(_events.IsEmpty())
{
if(auto player = UCommonFunctions::GetPlayer(this))
{
player->onAnyKeyPressedDelegate.RemoveDynamic(this, &UQuickTimeEventManager::OnInputPressed);
player->onAnyKeyReleasedDelegate.RemoveDynamic(this, &UQuickTimeEventManager::OnInputReleased);
}
UCommonFunctions::ExitSlowMotion();
}
UE_LOG(LogTemp, Log, TEXT("OnEventEnd End"));
}
void UQuickTimeEventManager::OnInput(const FKey& key, bool released)
{
for(auto eventMapping : _events)
{
if(eventMapping.Value.key == key)
{
EQuickTimeEventResult result = EQuickTimeEventResult::Success;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
result = WM->ProcessQuickTimeEvent(eventMapping.Key, released);
switch(result)
{
case EQuickTimeEventResult::FailedKey: // its not a failed key, but special state to do nothing (eg. holding event)
return;
default:
// the result is success or failed time and widget is self removed
break;
}
}
OnEventEnd(eventMapping.Key, result);
return;
}
}
if(released)
return;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
while(_events.Num() > 0)
{
WM->RemoveQuickTimeEvent(_events.begin()->Key);
OnEventEnd(_events.begin()->Key, EQuickTimeEventResult::FailedKey);
}
}
else
{
while(_events.Num() > 0)
OnEventEnd(_events.begin()->Key, EQuickTimeEventResult::FailedKey);
}
}
void UQuickTimeEventManager::OnInputPressed(FKey key)
{
OnInput(key, false);
}
void UQuickTimeEventManager::OnInputReleased(FKey key)
{
OnInput(key, true);
}
void UQuickTimeEventManager::OnFirstEventInit()
{
if(_events.IsEmpty())
{
if(auto player = UCommonFunctions::GetPlayer(this))
{
GetWorld()->GetTimerManager().SetTimerForNextTick([manager = this, player = player]()
{
player->onAnyKeyPressedDelegate.AddDynamic(manager, &UQuickTimeEventManager::OnInputPressed);
player->onAnyKeyReleasedDelegate.AddDynamic(manager, &UQuickTimeEventManager::OnInputReleased);
});
}
UCommonFunctions::EnterSlowMotion();
}
}
void UQuickTimeEventManager::CreateEvent(FQuickTimeEventEnqueProperties& properties, bool sequence)
{
Event event{ properties.type, properties.key, properties.callback, sequence };
GetWorld()->GetTimerManager().SetTimer(event.timer, [id = event.GetId(), manager = this]()
{
EQuickTimeEventResult result = EQuickTimeEventResult::FailedTime;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
result = WM->RemoveQuickTimeEvent(id);
manager->OnEventEnd(id, result);
}, properties.duration, false);
_events.Add(event.GetId(), event);
if(auto WM = AMainGameModeBase::GetWidgetsManager())
WM->ShowQuickTimeEvent(event.GetId(), properties);
}

View File

@ -0,0 +1,95 @@
// Oleg Petruny proprietary.
#pragma once
#include "UObject/Object.h"
#include "QuickTimeEvent.generated.h"
UENUM(BlueprintType)
enum class EQuickTimeEventType : uint8
{
SinglePress,
MultiPress,
Hold
};
UENUM(BlueprintType)
enum class EQuickTimeEventResult : uint8
{
Success,
FailedTime,
FailedKey
};
DECLARE_DYNAMIC_DELEGATE_OneParam(FQuickTimeEventEndCallback, EQuickTimeEventResult, result);
USTRUCT(BlueprintType)
struct FQuickTimeEventEnqueProperties
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
EQuickTimeEventType type = EQuickTimeEventType::SinglePress;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FKey key = EKeys::F;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float duration = 1.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FVector2D anchor = FVector2D::Unit45Deg;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FQuickTimeEventEndCallback callback;
};
struct Event
{
public:
Event();
Event(EQuickTimeEventType type, FKey& key, FQuickTimeEventEndCallback& callback, bool sequence);
inline int32 GetId();
EQuickTimeEventType type;
FKey key;
FTimerHandle timer;
FQuickTimeEventEndCallback callback;
bool sequence;
private:
int32 _id;
static int32 counter;
};
UCLASS(Blueprintable, BlueprintType)
class UQuickTimeEventManager : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
void ShowQuickTimeEvent(FQuickTimeEventEnqueProperties properties);
UFUNCTION(BlueprintCallable)
void EnqueQuickTimeEvent(FQuickTimeEventEnqueProperties properties);
protected:
void ShowNextEvent();
void OnEventEnd(int32 id, EQuickTimeEventResult result);
void OnInput(const FKey& key, bool released);
UFUNCTION()
void OnInputPressed(FKey key);
UFUNCTION()
void OnInputReleased(FKey key);
void OnFirstEventInit();
void CreateEvent(FQuickTimeEventEnqueProperties& properties, bool sequence);
TQueue<FQuickTimeEventEnqueProperties> _nextEvents;
TMap<int32, Event> _events;
FCriticalSection _lock;
};

View File

@ -1,64 +0,0 @@
// Oleg Petruny proprietary.
#include "AdvanceButton.h"
#include "Components/ButtonSlot.h"
//FReply SAdvanceButton::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
//{
// if(!hitboxTexture)
// return SButton::OnMouseMove(MyGeometry, MouseEvent);
//
// FVector2D locPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
// locPos /= MyGeometry.GetLocalSize();
// int width = hitboxTexture->GetPlatformData()->SizeX;
// locPos.X *= width;
// locPos.Y *= hitboxTexture->GetPlatformData()->SizeY;
// long pixelIndex = (locPos.Y * width) + locPos.X;
//
// FColor* imageData = static_cast<FColor*>((hitboxTexture->GetPlatformData()->Mips[0]).BulkData.Lock(LOCK_READ_ONLY));
// bool hovered = imageData && imageData[pixelIndex].A <= hitboxTextureAlphaThreshold;
// hitboxTexture->GetPlatformData()->Mips[0].BulkData.Unlock();
//
// if(hovered != bIsHovered)
// {
// bIsHovered = hovered;
// if(bIsHovered)
// SButton::OnMouseEnter(MyGeometry, MouseEvent);
// else
// SButton::OnMouseLeave(MouseEvent);
// }
//
// return SButton::OnMouseMove(MyGeometry, MouseEvent);
//}
//
//void UAdvanceButton::SynchronizeProperties()
//{
// Super::SynchronizeProperties();
// (static_cast<SAdvanceButton*>(MyButton.Get()))->hitboxTexture = hitboxTexture;
// (static_cast<SAdvanceButton*>(MyButton.Get()))->hitboxTextureAlphaThreshold = hitboxTextureAlphaThreshold;
//}
//
//TSharedRef<SWidget> UAdvanceButton::RebuildWidget()
//{
// TSharedPtr<SAdvanceButton> advanceButton = SNew(SAdvanceButton)
// .OnClicked(BIND_UOBJECT_DELEGATE(FOnClicked, SlateHandleClicked))
// .OnPressed(BIND_UOBJECT_DELEGATE(FSimpleDelegate, SlateHandlePressed))
// .OnReleased(BIND_UOBJECT_DELEGATE(FSimpleDelegate, SlateHandleReleased))
// .OnHovered_UObject(this, &ThisClass::SlateHandleHovered)
// .OnUnhovered_UObject(this, &ThisClass::SlateHandleUnhovered)
// .ButtonStyle(&WidgetStyle)
// .ClickMethod(ClickMethod)
// .TouchMethod(TouchMethod)
// .IsFocusable(IsFocusable);
//
// advanceButton->hitboxTexture = hitboxTexture;
// advanceButton->hitboxTextureAlphaThreshold = hitboxTextureAlphaThreshold;
// MyButton = advanceButton;
//
// if(GetChildrenCount() > 0)
// Cast<UButtonSlot>(GetContentSlot())->BuildSlot(MyButton.ToSharedRef());
//
// return MyButton.ToSharedRef();
//}

View File

@ -1,51 +0,0 @@
// Oleg Petruny proprietary.
#pragma once
#include "Components/Button.h"
#include "AdvanceButton.generated.h"
class SAdvanceButton : public SButton
{
public:
//virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override
//{
// if(!bIsHovered) return FReply::Unhandled();
// return SButton::OnMouseButtonDown(MyGeometry, MouseEvent);
//}
//virtual FReply OnMouseButtonDoubleClick(const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent) override
//{
// if(!bIsHovered) return FReply::Unhandled();
// return SButton::OnMouseButtonDoubleClick(InMyGeometry, InMouseEvent);
//}
//virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
//virtual void OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override
//{
// if(hitboxTexture) return;
// return SButton::OnMouseEnter(MyGeometry, MouseEvent);
//}
//virtual TSharedPtr<IToolTip> GetToolTip() override
//{
// return (bIsHovered ? SWidget::GetToolTip() : nullptr);
//}
//
//UTexture2D* hitboxTexture = nullptr;
//uint8 hitboxTextureAlphaThreshold = 0;
};
UCLASS()
class UAdvanceButton : public UButton
{
GENERATED_BODY()
//
//public:
// virtual void SynchronizeProperties() override;
// virtual TSharedRef<SWidget> RebuildWidget() override;
//
// UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AdvanceButton")
// UTexture2D* hitboxTexture = nullptr;
//
// UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AdvanceButton", meta = (ClampMin = "0.0", ClampMax = "255.0", UIMin = "0.0", UIMax = "255.0"))
// int hitboxTextureAlphaThreshold = 0;
};

View File

@ -0,0 +1,58 @@
// Oleg Petruny proprietary.
#include "QuickTimeEventWidget.h"
#include "Animation/WidgetAnimation.h"
#include "QuickTimeEvent.h"
#include "QuickTimeEventWidgetManager.h"
void UQuickTimeEventWidget::NativeConstruct()
{
FWidgetAnimationDynamicEvent onTimerFinish;
onTimerFinish.BindDynamic(this, &UQuickTimeEventWidget::RemoveEvent);
BindToAnimationFinished(eventAnimation, onTimerFinish);
FWidgetAnimationDynamicEvent onRemove;
onRemove.BindDynamic(this, &UQuickTimeEventWidget::RemoveFromManager);
BindToAnimationFinished(removeAnimation, onRemove);
return Super::NativeConstruct();
}
void UQuickTimeEventWidget::Show()
{
SetVisibility(ESlateVisibility::Visible);
const float playbackSpeed = eventAnimation->GetEndTime() / duration;
PlayAnimation(eventAnimation, 0, 1, EUMGSequencePlayMode::Forward, playbackSpeed, false);
}
void UQuickTimeEventWidget::RemoveFromManager()
{
manager->RemoveEvent(id);
}
EQuickTimeEventResult UQuickTimeEventWidget::_Process(const bool released)
{
Process(released);
return result;
}
EQuickTimeEventResult UQuickTimeEventWidget::_ProcessBeforeRemoving()
{
ProcessBeforeRemoving();
return result;
}
void UQuickTimeEventWidget::RemoveEvent()
{
if(removing)
return;
removing = true;
PlayAnimation(removeAnimation, 0, 1, EUMGSequencePlayMode::Forward, 1, false);
}

View File

@ -0,0 +1,54 @@
// Oleg Petruny proprietary.
#pragma once
#include "Widgets/ResolutionResponsiveUserWidget.h"
#include "QuickTimeEventWidget.generated.h"
enum class EQuickTimeEventResult : uint8;
UCLASS(Blueprintable, Abstract)
class UQuickTimeEventWidget : public UResolutionResponsiveUserWidget
{
GENERATED_BODY()
public:
void Show();
UFUNCTION(BlueprintCallable)
void RemoveEvent();
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable)
void Process(const bool released);
EQuickTimeEventResult _Process(const bool released);
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable)
void ProcessBeforeRemoving();
EQuickTimeEventResult _ProcessBeforeRemoving();
UPROPERTY(meta = (BindWidget))
class UTextBlock* keyText;
int32 id;
class UQuickTimeEventWidgetManager* manager;
float duration;
protected:
virtual void NativeConstruct() override;
UFUNCTION()
void RemoveFromManager();
UPROPERTY(Transient, meta = (BindWidgetAnim))
class UWidgetAnimation* eventAnimation;
UPROPERTY(Transient, meta = (BindWidgetAnim))
class UWidgetAnimation* removeAnimation;
UPROPERTY(BlueprintReadWrite)
EQuickTimeEventResult result = EQuickTimeEventResult::FailedKey; // special state to do nothing;
bool removing = false;
};

View File

@ -0,0 +1,77 @@
// Oleg Petruny proprietary.
#include "QuickTimeEventWidgetManager.h"
#include "Animation/UMGSequencePlayer.h"
#include "Animation/WidgetAnimation.h"
#include "Blueprint/WidgetTree.h"
#include "Components/CanvasPanel.h"
#include "Components/CanvasPanelSlot.h"
#include "Components/TextBlock.h"
#include "CommonFunctions.h"
#include "QuickTimeEvent.h"
#include "Widgets/QuickTimeEventWidget.h"
void UQuickTimeEventWidgetManager::ShowEvent(const int32 id, const struct FQuickTimeEventEnqueProperties& properties)
{
FScopeLock Lock(&mappingLock);
if(eventsMap.Contains(id) || !eventWidgetClasses.Contains(properties.type))
return;
auto eventWidget = WidgetTree->ConstructWidget<UQuickTimeEventWidget>(eventWidgetClasses[properties.type]);
eventWidget->SetVisibility(ESlateVisibility::Hidden);
eventWidget->keyText->SetText(UCommonFunctions::GetKeyDisplayName(properties.key));
eventWidget->id = id;
eventWidget->manager = this;
eventWidget->duration = properties.duration;
((UCanvasPanelSlot*)canvas->AddChild(eventWidget))->SetAnchors({ (float)properties.anchor.X, (float)properties.anchor.Y });
eventWidget->Show();
eventsMap.Add(id, eventWidget);
}
EQuickTimeEventResult UQuickTimeEventWidgetManager::RemoveEvent(const int32 id)
{
FScopeLock Lock(&mappingLock);
if(!eventsMap.Contains(id))
return EQuickTimeEventResult::FailedTime;
UQuickTimeEventWidget* eventWidget;
eventsMap.RemoveAndCopyValue(id, eventWidget);
auto result = eventWidget->_ProcessBeforeRemoving();
eventWidget->RemoveEvent();
return result;
}
EQuickTimeEventResult UQuickTimeEventWidgetManager::AnimateEvent(const int32 id, const bool released)
{
FScopeLock Lock(&mappingLock);
if(!eventsMap.Contains(id))
return EQuickTimeEventResult::FailedTime;
return eventsMap[id]->_Process(released);
}
void UQuickTimeEventWidgetManager::UpdateAnimation(UWidget* widget, float newSpeed)
{
if(auto eventWidget = Cast<UQuickTimeEventWidget>(widget))
{
for(auto player : eventWidget->ActiveSequencePlayers)
{
if(player->GetAnimation()->GetDisplayLabel() != TEXT("eventAnimation"))
continue;
newSpeed *= player->GetAnimation()->GetEndTime() / eventWidget->duration;
player->SetPlaybackSpeed(newSpeed);
}
return;
}
Super::UpdateAnimation(widget, newSpeed);
}

View File

@ -0,0 +1,37 @@
// Oleg Petruny proprietary.
#pragma once
#include "Widgets/WorldDilationResponsiveUserWidget.h"
#include "QuickTimeEventWidgetManager.generated.h"
enum class EInputAnimatedWidgetAnimation : uint8;
enum class EQuickTimeEventType : uint8;
enum class EQuickTimeEventResult : uint8;
class UQuickTimeEventWidget;
UCLASS(Blueprintable, Abstract)
class UQuickTimeEventWidgetManager : public UWorldDilationResponsiveUserWidget
{
GENERATED_BODY()
friend UQuickTimeEventWidget;
public:
void ShowEvent(const int32 id, const struct FQuickTimeEventEnqueProperties& properties);
EQuickTimeEventResult RemoveEvent(const int32 id);
EQuickTimeEventResult AnimateEvent(const int32 id, const bool released);
protected:
virtual void UpdateAnimation(UWidget* widget, float newSpeed) override;
UPROPERTY(EditDefaultsOnly)
TMap<EQuickTimeEventType, TSubclassOf<UQuickTimeEventWidget>> eventWidgetClasses;
UPROPERTY(meta = (BindWidget))
class UCanvasPanel* canvas;
TMap<const int32, UQuickTimeEventWidget*> eventsMap;
FCriticalSection mappingLock;
};

View File

@ -22,6 +22,7 @@
#include "Widgets/InventoryWidget.h"
#include "Widgets/JournalWidget.h"
#include "Widgets/MainMenu/MainMenuWidget.h"
#include "Widgets/QuickTimeEventWidgetManager.h"
void UWidgetsManager::Init()
{
@ -52,6 +53,7 @@ void UWidgetsManager::Init()
if(auto instance = CreateWidget<UInteractableHintWidgetManager>(PC, interactableHintWidgetManagerClass))
{
interactableHintWidgetManager = instance;
interactableHintWidgetManager->SetVisibility(ESlateVisibility::Visible);
interactableHintWidgetManager->AddToViewport();
}
if(auto instance = CreateWidget<UCutsceneSkipWidget>(PC, cutsceneSkipWidgetClass))
@ -60,6 +62,12 @@ void UWidgetsManager::Init()
cutsceneSkipWidget->SetVisibility(ESlateVisibility::Hidden);
cutsceneSkipWidget->AddToViewport();
}
if(auto instance = CreateWidget<UQuickTimeEventWidgetManager>(PC, quickTimeEventWidgetManagerClass))
{
quickTimeEventWidgetManager = instance;
quickTimeEventWidgetManager->SetVisibility(ESlateVisibility::Visible);
quickTimeEventWidgetManager->AddToViewport();
}
if(auto instance = CreateWidget<UDialogueRowWidgetManager>(PC, dialogueRowWidgetManagerClass))
{
dialogueRowWidgetManager = instance;
@ -183,6 +191,23 @@ void UWidgetsManager::AnimateCutsceneWidget(const EInputAnimatedWidgetAnimation
void UWidgetsManager::ShowQuickTimeEvent(const int32 id, const struct FQuickTimeEventEnqueProperties& properties)
{
quickTimeEventWidgetManager->ShowEvent(id, properties);
}
EQuickTimeEventResult UWidgetsManager::RemoveQuickTimeEvent(const int32 id)
{
return quickTimeEventWidgetManager->RemoveEvent(id);
}
EQuickTimeEventResult UWidgetsManager::ProcessQuickTimeEvent(const int32 id, const bool released)
{
return quickTimeEventWidgetManager->AnimateEvent(id, released);
}
void UWidgetsManager::SetInputDialogueWidget(const FKey key, const FText desc, FDialogueSkipDelegate& delegate)
{
dialogueRowWidgetManager->SetInput(key, desc, delegate);

View File

@ -7,6 +7,7 @@
#include "WidgetsManager.generated.h"
enum class EInputAnimatedWidgetAnimation : uint8;
enum class EQuickTimeEventResult : uint8;
UCLASS(Blueprintable)
class UWidgetsManager : public UObject
@ -34,6 +35,10 @@ public:
void DisableCutsceneWidget();
void AnimateCutsceneWidget(const EInputAnimatedWidgetAnimation animation);
void ShowQuickTimeEvent(const int32 id, const struct FQuickTimeEventEnqueProperties& properties);
EQuickTimeEventResult RemoveQuickTimeEvent(const int32 id);
EQuickTimeEventResult ProcessQuickTimeEvent(const int32 id, const bool released);
void SetInputDialogueWidget(const FKey key, const FText desc, class FDialogueSkipDelegate& delegate);
void ShowDialogueWidget(const struct FDialogueRow& dialogue);
void HideDialogueWidget(const struct FDialogueRow& dialogue);
@ -68,6 +73,10 @@ protected:
TSubclassOf<class UCutsceneSkipWidget> cutsceneSkipWidgetClass; // never hidden
class UCutsceneSkipWidget* cutsceneSkipWidget = nullptr;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<class UQuickTimeEventWidgetManager> quickTimeEventWidgetManagerClass; // never hidden
class UQuickTimeEventWidgetManager* quickTimeEventWidgetManager = nullptr;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<class UDialogueRowWidgetManager> dialogueRowWidgetManagerClass; // hidden in cutscene
class UDialogueRowWidgetManager* dialogueRowWidgetManager = nullptr;

View File

@ -0,0 +1,81 @@
// Oleg Petruny proprietary.
#include "WorldDilationResponsiveUserWidget.h"
#include "Animation/UMGSequencePlayer.h"
#include "Animation/WidgetAnimation.h"
#include "Components/PanelWidget.h"
#include "CommonFunctions.h"
bool UWorldDilationResponsiveUserWidget::Initialize()
{
UCommonFunctions::GetWorldDilationChangedDelegate().AddDynamic(this, &UWorldDilationResponsiveUserWidget::UpdateAnimations);
return UUserWidget::Initialize();
}
void UWorldDilationResponsiveUserWidget::BeginDestroy()
{
UCommonFunctions::GetWorldDilationChangedDelegate().RemoveDynamic(this, &UWorldDilationResponsiveUserWidget::UpdateAnimations);
UUserWidget::BeginDestroy();
}
void UWorldDilationResponsiveUserWidget::UpdateAnimations(float newSpeed)
{
UpdateAnimation(GetRootWidget(), newSpeed);
if(auto root = Cast<UPanelWidget>(GetRootWidget()))
{
if(root->GetChildrenCount() == 0)
return;
// recursion would be simplier...
TArray<int32> path{ 0 };
UWidget* widget = root->GetChildAt(0);
while(widget != root)
{
UpdateAnimation(widget, newSpeed);
if(path.Num() < maxDepth)
{
if(auto panel = Cast<UPanelWidget>(widget))
{
if(panel->GetChildrenCount())
{
path.Add(0);
widget = panel->GetChildAt(0);
continue;
}
}
}
while(path.Num())
{
auto panel = widget->GetParent();
int32& i = path[path.Num() - 1];
if(++i < panel->GetChildrenCount())
{
widget = panel->GetChildAt(i);
break;
}
else
{
path.RemoveAt(path.Num() - 1, EAllowShrinking::No);
widget = panel;
}
}
}
}
}
void UWorldDilationResponsiveUserWidget::UpdateAnimation(UWidget* widget, float newSpeed)
{
if(auto userWidget = Cast<UUserWidget>(widget))
{
for(auto player : userWidget->ActiveSequencePlayers)
player->SetPlaybackSpeed(newSpeed);
}
}

View File

@ -0,0 +1,25 @@
// Oleg Petruny proprietary.
#pragma once
#include "Blueprint/UserWidget.h"
#include "WorldDilationResponsiveUserWidget.generated.h"
UCLASS()
class UWorldDilationResponsiveUserWidget : public UUserWidget
{
GENERATED_BODY()
public:
virtual bool Initialize() override;
virtual void BeginDestroy() override;
protected:
UFUNCTION()
void UpdateAnimations(float newSpeed);
virtual void UpdateAnimation(UWidget* widget, float newSpeed);
UPROPERTY(EditDefaultsOnly)
int32 maxDepth = 2;
};