Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Fill out your copyright notice in the Description page of Project Settings.
- #pragma once
- #include "CoreMinimal.h"
- #include "Components/ActorComponent.h"
- #include "Runtime/Engine/Public/TimerManager.h"
- #include "Runtime/UMG/Public/Blueprint/UserWidget.h"
- #include "Runtime/UMG/Public/Components/ProgressBar.h"
- #include "Runtime/SlateCore/Public/Layout/Geometry.h"
- #include "Dynamic_Fill_Bar.generated.h"
- // Custom struct to hold our ability data, also has "hidden" variables we will pass along to save changes we previously made
- USTRUCT(BlueprintType)
- struct FDynamic_Ability_Struct
- {
- GENERATED_USTRUCT_BODY()
- public:
- // The current capacity of our ability, if a player has taken damage this value may be 75 when max health is 100
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nebula Structs", meta = (ClampMin = "0", UIMin = "0"))
- float Ability_Current_Value;
- // The maximum capacity of our ability, if a player starts with 100/100 health and gets a pick up giving +10 health capacity this value is now 110
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nebula Structs", meta = (ClampMin = "0", UIMin = "0"))
- float Ability_Max_Value;
- // The name of our progress bar
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nebula Structs")
- FName Progress_Bar_Name;
- // Hidden value to pass through that keeps track of our total "scale" when an ability has been modified previously
- float Hidden_Current_Scale;
- // Hidden value to pass for original conversion ratio
- float Hidden_Conversion_Ratio;
- // Hidden value to pass for the original size of the progress bar in the widget designer
- float Hidden_Original_Size;
- // Hidden value to pass for the bar type
- bool Hidden_Horizontal_Bar;
- };
- UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
- class CODE_TEST_API UDynamic_Fill_Bar : public UActorComponent
- {
- GENERATED_BODY()
- public:
- // Sets default values for this component's properties
- UDynamic_Fill_Bar();
- // Functions to expand the progress bar of a given ability/stat
- UFUNCTION(BlueprintCallable, Category = "Nebula Nodes", meta = (Keywords = "call, change, progress bar, ability, update"))
- void Change_Ability_Bar(UUserWidget* Widget_Reference, UPARAM(ref) FDynamic_Ability_Struct &Ability_Data, int32 Delta_Ability, float Rate);
- // Function to save an ability bar
- UFUNCTION(BlueprintCallable, Category = "Nebula Nodes", meta = (Keywords = "call, save, progress bar, ability"))
- void Save_Ability_Bar(FDynamic_Ability_Struct &Saved_Ability_Data);
- // Function to set default values based on whether we are loading saved data or not
- void Set_Ability_Start_Values();
- // Function that actually modifies the progress bar length
- void UpdateBarLength();
- // Function to set the epansion defaults based on the bar fill type parameters
- void SetFillTypeParameters();
- // Function to prevent bugs when the "Change Ability Bar" function is called prior to having cached geometry to work on
- void NoCacheGeometryYet();
- // Global variables
- bool HorizontalBar;
- float I_Delta_Ability;
- float I_CurrentScale;
- float GoalScale;
- float ConversionRatio;
- float OriginalSize;
- float Increase_Per_Cycle;
- float CurrentCycle;
- float I_Rate;
- float CapacityCorrection;
- float TranslationDirection;
- FVector2D Temp2DScaleVec;
- FVector2D Temp2DTransVec;
- FName I_ProgressBarName;
- FTimerHandle DynamicBarHandle;
- EProgressBarFillType::Type FillType;
- FDynamic_Ability_Struct* I_Ability_Data = nullptr;
- UUserWidget* I_WidgetRef = nullptr;
- UProgressBar* ProgressBarRef = nullptr;
- AActor* Parent_Actor = nullptr;
- // Called every frame
- virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
- protected:
- // Called when the game starts
- virtual void BeginPlay() override;
- };
- ---------------------------------------------------------------------------
- // Fill out your copyright notice in the Description page of Project Settings.
- #include "Dynamic_Fill_Bar.h"
- // Sets default values for this component's properties
- UDynamic_Fill_Bar::UDynamic_Fill_Bar()
- {
- // Turn tick off for this component
- PrimaryComponentTick.bCanEverTick = false;
- // Retrieve actor component's Parent Actor
- Parent_Actor = GetOwner();
- // We always default the current scale and cycle to 1 with an initial translation of (0,0), a postive translation and a horizontal bar
- Temp2DScaleVec = { 1.0f, 1.0f };
- Temp2DTransVec = { 0.0f, 0.0f };
- I_CurrentScale = 1.0f;
- CurrentCycle = 1.0f;
- TranslationDirection = 1.0f;
- HorizontalBar = true;
- // This is the amount to increment the changes in the progress bar each cycle
- Increase_Per_Cycle = 0.001f;
- }
- // Called when the game starts
- void UDynamic_Fill_Bar::BeginPlay()
- {
- Super::BeginPlay();
- // ...
- }
- // Called every frame
- void UDynamic_Fill_Bar::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
- {
- Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
- // ...
- }
- // Function to modify an ability bar (progress bar) to allow for increased capacity at the same ratio of the original bar
- void UDynamic_Fill_Bar::Change_Ability_Bar(UUserWidget* Widget_Reference, UPARAM(ref) FDynamic_Ability_Struct &Ability_Data, int32 Delta_Ability, float Rate)
- {
- // Copy all input data to our internal variables for use in other functions
- I_Ability_Data = &Ability_Data;
- I_WidgetRef = Widget_Reference;
- I_ProgressBarName = I_Ability_Data->Progress_Bar_Name;
- // Ensure we have a "max" value for our ability to prevent dividing by "0" and crashing the engine
- if (I_Ability_Data->Ability_Max_Value > 0)
- {
- I_Delta_Ability = (float(Delta_Ability) / I_Ability_Data->Ability_Max_Value);
- // Keep track of the desired "true" maximum capacity following expansion, we assume this value should be a whole number
- CapacityCorrection = (float(Delta_Ability) + I_Ability_Data->Ability_Max_Value);
- }
- else
- {
- // We will make no changes to the ability bar if there is no "max" capacity to begin with
- I_Delta_Ability = 0.f;
- }
- // Ensure we have a positive value for our "Rate" to prevent dividing by "0" and crashing the engine
- if (Rate > 0)
- {
- I_Rate = Rate;
- }
- else
- {
- // We will provide an arbitrary "rate" to increase the bar by if none is provided
- I_Rate = 250.f;
- }
- // Get access to the actual widget component progress bar through the given name identifier
- ProgressBarRef = dynamic_cast<UProgressBar*>(I_WidgetRef->GetWidgetFromName(FName(I_Ability_Data->Progress_Bar_Name)));
- // If we have successfully acquired a progress bar to work with, grab the fill type and set the starting values of our ability bar
- if (ProgressBarRef != nullptr)
- {
- FillType = ProgressBarRef->BarFillType;
- Set_Ability_Start_Values();
- }
- }
- // Function to set the starting values of our ability bar, will start bar at previously saved size or the set widget size from the designer
- void UDynamic_Fill_Bar::Set_Ability_Start_Values()
- {
- // Check if we have saved data within the input struct (Hidden values are only modified in C++ so if they are not the default we must have saved data)
- if (I_Ability_Data->Hidden_Current_Scale == 0)
- {
- // Get the default size of the widget in the direction of expansion as specfied in the widget designer
- FVector2D LocalSize = ProgressBarRef->GetCachedGeometry().GetLocalSize();
- if (FillType == EProgressBarFillType::LeftToRight || FillType == EProgressBarFillType::RightToLeft || FillType == EProgressBarFillType::FillFromCenter)
- {
- OriginalSize = LocalSize.X;
- HorizontalBar = true;
- I_Ability_Data->Hidden_Horizontal_Bar = true;
- }
- else if (FillType == EProgressBarFillType::TopToBottom || FillType == EProgressBarFillType::BottomToTop)
- {
- OriginalSize = LocalSize.Y;
- HorizontalBar = false;
- I_Ability_Data->Hidden_Horizontal_Bar = false;
- }
- // We will calculate our goal scale and conversion ratio the first time we link a progress bar to this actor component for expansion
- GoalScale = (I_CurrentScale + I_Delta_Ability);
- ConversionRatio = (I_Ability_Data->Ability_Max_Value * Increase_Per_Cycle);
- }
- else
- {
- // Here we pull the "hidden" data from the previous save point so the ability bar doesn't start at the default value if we previously changed it
- if (I_Ability_Data->Hidden_Horizontal_Bar == true)
- {
- HorizontalBar = true;
- }
- else if (I_Ability_Data->Hidden_Horizontal_Bar == false)
- {
- HorizontalBar = false;
- }
- GoalScale = (I_Ability_Data->Hidden_Current_Scale + I_Delta_Ability);
- ConversionRatio = I_Ability_Data->Hidden_Conversion_Ratio;
- OriginalSize = I_Ability_Data->Hidden_Original_Size;
- }
- // Check that we have cached geometry from a pre-pass, if not we will delay setting our widget parameters to prevent unwanted crashes
- if (OriginalSize == 0.0f)
- {
- // Check every 0.1 seconds if we have cached geometry to work with, if not wait until we do before continuing
- Parent_Actor->GetWorldTimerManager().SetTimer(DynamicBarHandle, this, &UDynamic_Fill_Bar::NoCacheGeometryYet, (0.1f), true);
- }
- else
- {
- // In case we started this timer we will clear it before we use it again to set the bar expansion rate
- Parent_Actor->GetWorldTimerManager().ClearTimer(DynamicBarHandle);
- // Call the function to set our desired expansion direction
- SetFillTypeParameters();
- }
- }
- // Function to set our variable "Translation Directon" based on the selected fill type in the widegt designer
- void UDynamic_Fill_Bar::SetFillTypeParameters()
- {
- switch (FillType)
- {
- case EProgressBarFillType::LeftToRight:
- TranslationDirection = 1.0f;
- break;
- case EProgressBarFillType::RightToLeft:
- TranslationDirection = -1.0f;
- break;
- case EProgressBarFillType::FillFromCenter:
- TranslationDirection = 0.0f;
- break;
- case EProgressBarFillType::TopToBottom:
- TranslationDirection = 1.0f;
- break;
- case EProgressBarFillType::BottomToTop:
- TranslationDirection = -1.0f;
- break;
- default:
- break;
- }
- // Create a timer to expand the progress bar
- Parent_Actor->GetWorldTimerManager().SetTimer(DynamicBarHandle, this, &UDynamic_Fill_Bar::UpdateBarLength, (1 / I_Rate), true);
- }
- // Function to incrementally expand our progress bar
- void UDynamic_Fill_Bar::UpdateBarLength()
- {
- // Continue to increase our progress bar size until it reaches the desired value, then we can clear our timer
- if (FMath::RoundToInt(GoalScale * 100.f) > FMath::RoundToInt(I_CurrentScale * 100.f))
- {
- I_CurrentScale += Increase_Per_Cycle;
- // If we have a horizontal bar we will expand along the "X" axis, otherwise we will expand along the "Y" axis
- if (HorizontalBar == true)
- {
- Temp2DScaleVec.X = (I_CurrentScale);
- Temp2DTransVec.X = ((OriginalSize / 2) * (Increase_Per_Cycle * CurrentCycle)) * (TranslationDirection);
- }
- else if (HorizontalBar == false)
- {
- Temp2DScaleVec.Y = (I_CurrentScale);
- Temp2DTransVec.Y = ((OriginalSize / 2) * (Increase_Per_Cycle * CurrentCycle)) * (TranslationDirection);
- }
- // Set our progress bar's new scale and translation
- ProgressBarRef->SetRenderScale(Temp2DScaleVec);
- ProgressBarRef->SetRenderTranslation(Temp2DTransVec);
- // Increment our cycle counter (this keeps track of how far we have translated our bar)
- CurrentCycle++;
- // Increase the actual variable that holds our "max" capacity
- I_Ability_Data->Ability_Max_Value += ConversionRatio;
- // Update our progress bar's percent to reflect the new capacity
- ProgressBarRef->SetPercent(I_Ability_Data->Ability_Current_Value / I_Ability_Data->Ability_Max_Value);
- }
- else
- {
- // Once the bar has reached full expansion we will clear our timer and set the percent to a more precise value
- Parent_Actor->GetWorldTimerManager().ClearTimer(DynamicBarHandle);
- ProgressBarRef->SetPercent(I_Ability_Data->Ability_Current_Value / CapacityCorrection);
- // We will correct for rounding errors encountered during expansion here and simply "set" our final capacity to the Old Max + Delta value
- I_Ability_Data->Ability_Max_Value = CapacityCorrection;
- // Set our new "hidden" values once we are finished expanding our ability bar
- I_Ability_Data->Hidden_Current_Scale = I_CurrentScale;
- I_Ability_Data->Hidden_Original_Size = OriginalSize;
- }
- }
- // Function to save our ability struct data including the "hidden" values we will need to re-initialize our bar to the appropriate size when loaded up
- void UDynamic_Fill_Bar::Save_Ability_Bar(FDynamic_Ability_Struct &Saved_Ability_Data)
- {
- Saved_Ability_Data.Ability_Current_Value = I_Ability_Data->Ability_Current_Value;
- Saved_Ability_Data.Ability_Max_Value = I_Ability_Data->Ability_Max_Value;
- Saved_Ability_Data.Progress_Bar_Name = I_ProgressBarName;
- Saved_Ability_Data.Hidden_Current_Scale = I_Ability_Data->Hidden_Current_Scale;
- Saved_Ability_Data.Hidden_Conversion_Ratio = I_Ability_Data->Hidden_Conversion_Ratio;
- Saved_Ability_Data.Hidden_Original_Size = I_Ability_Data->Hidden_Original_Size;
- Saved_Ability_Data.Hidden_Horizontal_Bar = I_Ability_Data->Hidden_Horizontal_Bar;
- }
- // Function to prevent crashes when attempts are made to access non-existent cached geometry
- void UDynamic_Fill_Bar::NoCacheGeometryYet()
- {
- Set_Ability_Start_Values();
- }
Advertisement
Add Comment
Please, Sign In to add comment