diff --git a/devlogs.org b/devlogs.org index a1f4f2dd..a9218f84 100644 --- a/devlogs.org +++ b/devlogs.org @@ -13,6 +13,7 @@ - [ ] [[id:f0d71869-42f9-43fd-a95a-76f2eb7300cb][Fluid Flux]] waterfall clips at edge of cliff - [ ] [[id:f0d71869-42f9-43fd-a95a-76f2eb7300cb][Fluid Flux]] surfaces trigger overlap events with projectiles - [ ] Arrows do not trigger overlaps with static meshes +- [ ] Magic effects are not currently within the git repo ** General @@ -25,6 +26,8 @@ - [ ] [[id:2646bd9e-c7f4-4542-b702-f0a209fe7c60][Gameplay Abilities System]] - [ ] Level Loading System - [ ] State Saving / Loading System +- [ ] [[id:bc1ba8f2-0c28-4b6a-9409-7b4e7cb3daec][Unreal Engine Data Tables]] +- [ ] [[id:bed29d32-6d95-499c-8f49-0ed2d3cc627e][Unreal Engine Subsystem Framework]] (lifetime managed singletons) ** Game @@ -48,6 +51,10 @@ - [ ] Update animals to use [[id:0d87b52e-b537-4e31-9425-389518e8af59][Behavior Trees]] - avoid player until after petting - [ ] Allow each animal to have a different amount of player avoidance - [ ] Move to using WotCharacter instead of ThirdPersonCharacter (migrate code) +- [ ] Fade objects between camera and player +- [ ] Add start menu +- [ ] Add pause menu +- [ ] Add death / respawn menu ** Code @@ -57,6 +64,9 @@ - [ ] Create [[id:7d5a755b-0806-4982-8f7b-4655056c1108][Inventory]] system - [ ] Migrate equippable code from BP to C++ - [ ] Migrate attack code from BP to C++ +- [ ] Migrate UI code from BP to C++ to allow c++ to create / manage UI widgets (healthbar, popup, interaction text) +- [ ] Create HUD (C++) https://benui.ca/unreal/ui-setup/ +- [ ] Character death should respawn (C++) ** Models @@ -108,6 +118,7 @@ - [X] Add WotGameModeBase (C++) Which spawns bots using [[id:9bce7262-b02d-48e9-b133-a6fde84730cb][Environment Query System (EQS)]] - [X] learn: Entity spawning system (NPC, items, etc.) - [X] bug: player respawn tied directly to specific game mode (doesn't need to be) +- [X] Restart player on death (c++) * Week of 2022-05-29 diff --git a/unreal/Content/ThirdPersonBP/Blueprints/ThirdPersonCharacter.uasset b/unreal/Content/ThirdPersonBP/Blueprints/ThirdPersonCharacter.uasset index 0462f7a4..9094e6b1 100644 Binary files a/unreal/Content/ThirdPersonBP/Blueprints/ThirdPersonCharacter.uasset and b/unreal/Content/ThirdPersonBP/Blueprints/ThirdPersonCharacter.uasset differ diff --git a/unreal/Source/VoxelRPG/Private/UI/WotButton.cpp b/unreal/Source/VoxelRPG/Private/UI/WotButton.cpp new file mode 100644 index 00000000..909f9d71 --- /dev/null +++ b/unreal/Source/VoxelRPG/Private/UI/WotButton.cpp @@ -0,0 +1 @@ +#include "UI/WotButton.h" diff --git a/unreal/Source/VoxelRPG/Private/UI/WotUWHealthBar.cpp b/unreal/Source/VoxelRPG/Private/UI/WotUWHealthBar.cpp new file mode 100644 index 00000000..254a2ca2 --- /dev/null +++ b/unreal/Source/VoxelRPG/Private/UI/WotUWHealthBar.cpp @@ -0,0 +1,26 @@ +#include "UI/WotUWHealthBar.h" +#include "UI/WotProgressBar.h" +#include "UI/WotTextBlock.h" +#include "WotAttributeComponent.h" + +void UWotUWHealthBar::NativeConstruct() +{ + Super::NativeConstruct(); +} + +void UWotUWHealthBar::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) +{ + Super::NativeTick(MyGeometry, InDeltaTime); + + if (!OwnerAttributeComp.IsValid()) { + return; + } + + float CurrentHealth = OwnerAttributeComp->GetHealth(); + float MaxHealth = OwnerAttributeComp->GetHealthMax(); + HealthBar->SetPercent(CurrentHealth / MaxHealth); + FNumberFormattingOptions Opts; + Opts.SetMaximumFractionalDigits(0); + CurrentHealthLabel->SetText(FText::AsNumber(CurrentHealth, &Opts)); + MaxHealthLabel->SetText(FText::AsNumber(MaxHealth, &Opts)); +} diff --git a/unreal/Source/VoxelRPG/Private/UI/WotUserWidget.cpp b/unreal/Source/VoxelRPG/Private/UI/WotUserWidget.cpp new file mode 100644 index 00000000..adeb2f62 --- /dev/null +++ b/unreal/Source/VoxelRPG/Private/UI/WotUserWidget.cpp @@ -0,0 +1,8 @@ +#include "UI/WotUserWidget.h" + +void UWotUserWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + // bind delegates, and set up default appearance +} diff --git a/unreal/Source/VoxelRPG/Private/WotCharacter.cpp b/unreal/Source/VoxelRPG/Private/WotCharacter.cpp index db8325c4..c2050848 100644 --- a/unreal/Source/VoxelRPG/Private/WotCharacter.cpp +++ b/unreal/Source/VoxelRPG/Private/WotCharacter.cpp @@ -6,15 +6,16 @@ #include "WotDeathEffectComponent.h" #include "WotInteractionComponent.h" #include "CineCameraComponent.h" +#include "GameFramework/GameModeBase.h" #include "GameFramework/SpringArmComponent.h" #include "GameFramework/CharacterMovementComponent.h" #include "Components/CapsuleComponent.h" +#include "Kismet/GameplayStatics.h" #include "Kismet/KismetMathLibrary.h" #include "Math/Color.h" #include "Engine/EngineTypes.h" - -// For Debug: -#include "DrawDebugHelpers.h" +#include "Blueprint/UserWidget.h" +#include "UI/WotUWHealthBar.h" // Sets default values AWotCharacter::AWotCharacter() @@ -209,6 +210,7 @@ void AWotCharacter::OnHealthChanged(AActor* InstigatorActor, UWotAttributeCompon void AWotCharacter::OnKilled(AActor* InstigatorActor, UWotAttributeComponent* OwningComp) { + // TODO: Disable movement // turn off collision & physics TurnOff(); // freezes the pawn state GetCapsuleComponent()->SetSimulatePhysics(false); @@ -237,7 +239,31 @@ void AWotCharacter::OnKilled(AActor* InstigatorActor, UWotAttributeComponent* Ow GetWorldTimerManager().SetTimer(TimerHandle_Destroy, this, &AWotCharacter::Destroy_TimeElapsed, KilledDestroyDelay); } +void AWotCharacter::ShowHealthBarWidget(float NewHealth, float Delta, float Duration) +{ + UUserWidget* HealthBarWidget = CreateWidget(GetWorld(), HealthBarWidgetClass); + HealthBarWidget->AddToViewport(); +} + +void AWotCharacter::ShowPopupWidget(FString Text, float Duration) +{ + UUserWidget* PopupWidget = CreateWidget(GetWorld(), PopupWidgetClass); + PopupWidget->AddToViewport(); +} + +void AWotCharacter::ShowActionTextWidget(FString Text, float Duration) +{ + UUserWidget* ActionTextWidget = CreateWidget(GetWorld(), ActionTextWidgetClass); + ActionTextWidget->AddToViewport(); +} + void AWotCharacter::Destroy_TimeElapsed() { + // Destroy the current player Destroy(); + // And restart + AGameModeBase* GameMode = UGameplayStatics::GetGameMode(this); + if (ensure(GameMode)) { + GameMode->RestartPlayer(GetController()); + } } diff --git a/unreal/Source/VoxelRPG/Private/WotGameplayStatics.cpp b/unreal/Source/VoxelRPG/Private/WotGameplayStatics.cpp new file mode 100644 index 00000000..0eb876e5 --- /dev/null +++ b/unreal/Source/VoxelRPG/Private/WotGameplayStatics.cpp @@ -0,0 +1,11 @@ +#include "WotGameplayStatics.h" +#include "Engine/UserInterfaceSettings.h" + +void UWotGameplayStatics::SetUIScale( float CustomUIScale ) +{ + UUserInterfaceSettings* UISettings = GetMutableDefault( UUserInterfaceSettings::StaticClass() ); + + if ( UISettings ) { + UISettings->ApplicationScale = CustomUIScale; + } +} diff --git a/unreal/Source/VoxelRPG/Public/UI/WotButton.h b/unreal/Source/VoxelRPG/Public/UI/WotButton.h new file mode 100644 index 00000000..7d415f4c --- /dev/null +++ b/unreal/Source/VoxelRPG/Public/UI/WotButton.h @@ -0,0 +1,11 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Components/Button.h" +#include "WotButton.generated.h" + +UCLASS() +class VOXELRPG_API UWotButton : public UButton +{ + GENERATED_BODY() +}; diff --git a/unreal/Source/VoxelRPG/Public/UI/WotImage.h b/unreal/Source/VoxelRPG/Public/UI/WotImage.h new file mode 100644 index 00000000..f13ea3d0 --- /dev/null +++ b/unreal/Source/VoxelRPG/Public/UI/WotImage.h @@ -0,0 +1,11 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Components/Image.h" +#include "WotImage.generated.h" + +UCLASS() +class VOXELRPG_API UWotImage : public UImage +{ + GENERATED_BODY() +}; diff --git a/unreal/Source/VoxelRPG/Public/UI/WotProgressBar.h b/unreal/Source/VoxelRPG/Public/UI/WotProgressBar.h new file mode 100644 index 00000000..f3302c24 --- /dev/null +++ b/unreal/Source/VoxelRPG/Public/UI/WotProgressBar.h @@ -0,0 +1,11 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Components/ProgressBar.h" +#include "WotProgressBar.generated.h" + +UCLASS() +class VOXELRPG_API UWotProgressBar : public UProgressBar +{ + GENERATED_BODY() +}; diff --git a/unreal/Source/VoxelRPG/Public/UI/WotTextBlock.h b/unreal/Source/VoxelRPG/Public/UI/WotTextBlock.h new file mode 100644 index 00000000..a493a88e --- /dev/null +++ b/unreal/Source/VoxelRPG/Public/UI/WotTextBlock.h @@ -0,0 +1,11 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Components/TextBlock.h" +#include "WotTextBlock.generated.h" + +UCLASS() +class VOXELRPG_API UWotTextBlock : public UTextBlock +{ + GENERATED_BODY() +}; diff --git a/unreal/Source/VoxelRPG/Public/UI/WotUWHealthBar.h b/unreal/Source/VoxelRPG/Public/UI/WotUWHealthBar.h new file mode 100644 index 00000000..6fd77677 --- /dev/null +++ b/unreal/Source/VoxelRPG/Public/UI/WotUWHealthBar.h @@ -0,0 +1,36 @@ +#pragma once + +#include "CoreMinimal.h" +#include "UI/WotUserWidget.h" +#include "WotAttributeComponent.h" +#include "WotUWHealthBar.generated.h" + +class UWotProgressBar; +class UWotTextBlock; + +UCLASS() +class VOXELRPG_API UWotUWHealthBar : public UWotUserWidget +{ + GENERATED_BODY() + +public: + void SetOwnerAttributeComponent(UWotAttributeComponent* InAttributeComponent) { OwnerAttributeComp = InAttributeComponent; } + +protected: + // Doing setup in the C++ constructor is not as + // useful as using NativeConstruct. + void NativeConstruct() override; + + void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; + + TWeakObjectPtr OwnerAttributeComp; + + UPROPERTY( meta = ( BindWidget ) ) + UWotProgressBar* HealthBar; + + UPROPERTY( meta = ( BindWidget ) ) + UWotTextBlock* CurrentHealthLabel; + + UPROPERTY( meta = ( BindWidget ) ) + UWotTextBlock* MaxHealthLabel; +}; diff --git a/unreal/Source/VoxelRPG/Public/UI/WotUserWidget.h b/unreal/Source/VoxelRPG/Public/UI/WotUserWidget.h new file mode 100644 index 00000000..c89ef20a --- /dev/null +++ b/unreal/Source/VoxelRPG/Public/UI/WotUserWidget.h @@ -0,0 +1,19 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "WotUserWidget.generated.h" + +// We make the class abstract, as we don't want to create +// instances of this, instead we want to create instances +// of our UMG Blueprint subclass. +UCLASS(Abstract) +class VOXELRPG_API UWotUserWidget : public UUserWidget +{ + GENERATED_BODY() + +protected: + // Doing setup in the C++ constructor is not as + // useful as using NativeConstruct. + virtual void NativeConstruct() override; +}; diff --git a/unreal/Source/VoxelRPG/Public/WotCharacter.h b/unreal/Source/VoxelRPG/Public/WotCharacter.h index c4e6fa47..0730037f 100644 --- a/unreal/Source/VoxelRPG/Public/WotCharacter.h +++ b/unreal/Source/VoxelRPG/Public/WotCharacter.h @@ -12,6 +12,8 @@ class USpringArmComponent; class UWotInteractionComponent; class UWotAttributeComponent; class UWotDeathEffectComponent; +class UUserWidget; +class UWotUWHealthBar; UCLASS() class VOXELRPG_API AWotCharacter : public ACharacter @@ -32,6 +34,15 @@ class VOXELRPG_API AWotCharacter : public ACharacter UPROPERTY(EditAnywhere, Category = "Attack") UAnimMontage* AttackAnim; + UPROPERTY(EditAnywhere, Category = "UI") + TSubclassOf HealthBarWidgetClass; + + UPROPERTY(EditAnywhere, Category = "UI") + TSubclassOf PopupWidgetClass; + + UPROPERTY(EditAnywhere, Category = "UI") + TSubclassOf ActionTextWidgetClass; + FTimerHandle TimerHandle_PrimaryAttack; public: @@ -83,6 +94,15 @@ class VOXELRPG_API AWotCharacter : public ACharacter UFUNCTION() void OnKilled(AActor* InstigatorActor, UWotAttributeComponent* OwningComp); + UFUNCTION(BlueprintCallable, Category = "UI") + void ShowHealthBarWidget(float NewHealth, float Delta, float Duration); + + UFUNCTION(BlueprintCallable, Category = "UI") + void ShowPopupWidget(FString Text, float Duration); + + UFUNCTION(BlueprintCallable, Category = "UI") + void ShowActionTextWidget(FString Text, float Duration); + float KilledDestroyDelay = 2.0f; FTimerHandle TimerHandle_Destroy; void Destroy_TimeElapsed(); diff --git a/unreal/Source/VoxelRPG/Public/WotGameplayStatics.h b/unreal/Source/VoxelRPG/Public/WotGameplayStatics.h new file mode 100644 index 00000000..62a41f81 --- /dev/null +++ b/unreal/Source/VoxelRPG/Public/WotGameplayStatics.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Kismet/BlueprintFunctionLibrary.h" +#include "EngineMinimal.h" +#include "WotGameplayStatics.generated.h" + +UCLASS() +class UWotGameplayStatics : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + // Custom UI scale 1.0f == 100%, 2.0f == 200%, 0.5f == 50% etc. + UFUNCTION( BlueprintCallable, Category = "User Interface" ) + static void SetUIScale( float CustomUIScale ); +}; diff --git a/unreal/Source/VoxelRPG/VoxelRPG.Build.cs b/unreal/Source/VoxelRPG/VoxelRPG.Build.cs index 024aeba8..e312bf8c 100644 --- a/unreal/Source/VoxelRPG/VoxelRPG.Build.cs +++ b/unreal/Source/VoxelRPG/VoxelRPG.Build.cs @@ -13,7 +13,7 @@ public VoxelRPG(ReadOnlyTargetRules Target) : base(Target) PrivateDependencyModuleNames.AddRange(new string[] { }); // Uncomment if you are using Slate UI - // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); + PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); // Uncomment if you are using online features // PrivateDependencyModuleNames.Add("OnlineSubsystem"); diff --git a/unreal/VoxelRPG.uproject b/unreal/VoxelRPG.uproject index a3cef308..821ea7e2 100644 --- a/unreal/VoxelRPG.uproject +++ b/unreal/VoxelRPG.uproject @@ -13,7 +13,8 @@ "CinematicCamera", "Niagara", "AIModule", - "GameplayTasks" + "GameplayTasks", + "UMG" ] } ], @@ -38,11 +39,6 @@ "Name": "EnhancedInput", "Enabled": true }, - { - "Name": "AdvancedFadeObjects", - "Enabled": true, - "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/5797fa36906648ba8ffdeb6ee94c903e" - }, { "Name": "Water", "Enabled": true @@ -65,11 +61,6 @@ "Linux" ] }, - { - "Name": "UIWS", - "Enabled": true, - "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/97ef36447df7427ca9b46edf1a438731" - }, { "Name": "NiagaraFluids", "Enabled": true @@ -80,4 +71,4 @@ "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/b719437f3fb54c259b34227363df8cab" } ] -} \ No newline at end of file +}