NebulaGames

C++ to Blueprints Dynamically sized progress bar

Apr 8th, 2019
516
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.15 KB | None | 0 0
  1. // Fill out your copyright notice in the Description page of Project Settings.
  2.  
  3. #pragma once
  4.  
  5. #include "CoreMinimal.h"
  6. #include "Components/ActorComponent.h"
  7. #include "Runtime/Engine/Public/TimerManager.h"
  8. #include "Runtime/UMG/Public/Blueprint/UserWidget.h"
  9. #include "Runtime/UMG/Public/Components/ProgressBar.h"
  10. #include "Runtime/SlateCore/Public/Layout/Geometry.h"
  11. #include "Dynamic_Fill_Bar.generated.h"
  12.  
  13. // Custom struct to hold our ability data, also has "hidden" variables we will pass along to save changes we previously made
  14. USTRUCT(BlueprintType)
  15. struct FDynamic_Ability_Struct
  16. {
  17.     GENERATED_USTRUCT_BODY()
  18.  
  19. public:
  20.    
  21.     // The current capacity of our ability, if a player has taken damage this value may be 75 when max health is 100
  22.     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nebula Structs", meta = (ClampMin = "0", UIMin = "0"))
  23.         float Ability_Current_Value;
  24.     // 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
  25.     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nebula Structs", meta = (ClampMin = "0", UIMin = "0"))
  26.         float Ability_Max_Value;
  27.     // The name of our progress bar
  28.     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nebula Structs")
  29.         FName Progress_Bar_Name;
  30.     // Hidden value to pass through that keeps track of our total "scale" when an ability has been modified previously
  31.     float Hidden_Current_Scale;
  32.     // Hidden value to pass for original conversion ratio
  33.     float Hidden_Conversion_Ratio;
  34.     // Hidden value to pass for the original size of the progress bar in the widget designer
  35.     float Hidden_Original_Size;
  36.     // Hidden value to pass for the bar type
  37.     bool Hidden_Horizontal_Bar;
  38.  
  39. };
  40.  
  41.  
  42. UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
  43. class CODE_TEST_API UDynamic_Fill_Bar : public UActorComponent
  44. {
  45.     GENERATED_BODY()
  46.  
  47. public:
  48.    
  49.     // Sets default values for this component's properties
  50.     UDynamic_Fill_Bar();
  51.  
  52.     // Functions to expand the progress bar of a given ability/stat
  53.     UFUNCTION(BlueprintCallable, Category = "Nebula Nodes", meta = (Keywords = "call, change, progress bar, ability, update"))
  54.         void Change_Ability_Bar(UUserWidget* Widget_Reference, UPARAM(ref) FDynamic_Ability_Struct &Ability_Data, int32 Delta_Ability, float Rate);
  55.     // Function to save an ability bar
  56.     UFUNCTION(BlueprintCallable, Category = "Nebula Nodes", meta = (Keywords = "call, save, progress bar, ability"))
  57.         void Save_Ability_Bar(FDynamic_Ability_Struct &Saved_Ability_Data);
  58.  
  59.     // Function to set default values based on whether we are loading saved data or not
  60.     void Set_Ability_Start_Values();
  61.     // Function that actually modifies the progress bar length
  62.     void UpdateBarLength();
  63.     // Function to set the epansion defaults based on the bar fill type parameters
  64.     void SetFillTypeParameters();
  65.     // Function to prevent bugs when the "Change Ability Bar" function is called prior to having cached geometry to work on
  66.     void NoCacheGeometryYet();
  67.  
  68.     // Global variables
  69.     bool HorizontalBar;
  70.     float I_Delta_Ability;
  71.     float I_CurrentScale;
  72.     float GoalScale;
  73.     float ConversionRatio;
  74.     float OriginalSize;
  75.     float Increase_Per_Cycle;
  76.     float CurrentCycle;
  77.     float I_Rate;
  78.     float CapacityCorrection;
  79.     float TranslationDirection;
  80.     FVector2D Temp2DScaleVec;
  81.     FVector2D Temp2DTransVec;
  82.     FName I_ProgressBarName;
  83.     FTimerHandle DynamicBarHandle;
  84.     EProgressBarFillType::Type FillType;
  85.     FDynamic_Ability_Struct* I_Ability_Data = nullptr;
  86.     UUserWidget* I_WidgetRef = nullptr;
  87.     UProgressBar* ProgressBarRef = nullptr;
  88.     AActor* Parent_Actor = nullptr;
  89.  
  90.     // Called every frame
  91.     virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
  92.  
  93. protected:
  94.     // Called when the game starts
  95.     virtual void BeginPlay() override;     
  96. };
  97.  
  98. ---------------------------------------------------------------------------
  99.  
  100. // Fill out your copyright notice in the Description page of Project Settings.
  101.  
  102. #include "Dynamic_Fill_Bar.h"
  103.  
  104. // Sets default values for this component's properties
  105. UDynamic_Fill_Bar::UDynamic_Fill_Bar()
  106. {
  107.     // Turn tick off for this component
  108.     PrimaryComponentTick.bCanEverTick = false;
  109.  
  110.     // Retrieve actor component's Parent Actor
  111.     Parent_Actor = GetOwner();
  112.  
  113.     // We always default the current scale and cycle to 1 with an initial translation of (0,0), a postive translation and a horizontal bar
  114.     Temp2DScaleVec = { 1.0f, 1.0f };
  115.     Temp2DTransVec = { 0.0f, 0.0f };
  116.     I_CurrentScale = 1.0f;
  117.     CurrentCycle = 1.0f;
  118.     TranslationDirection = 1.0f;
  119.     HorizontalBar = true;
  120.  
  121.     // This is the amount to increment the changes in the progress bar each cycle
  122.     Increase_Per_Cycle = 0.001f;
  123. }
  124.  
  125. // Called when the game starts
  126. void UDynamic_Fill_Bar::BeginPlay()
  127. {
  128.     Super::BeginPlay();
  129.  
  130.     // ...
  131.    
  132. }
  133.  
  134. // Called every frame
  135. void UDynamic_Fill_Bar::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
  136. {
  137.     Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
  138.  
  139.     // ...
  140. }
  141.  
  142. // Function to modify an ability bar (progress bar) to allow for increased capacity at the same ratio of the original bar
  143. void UDynamic_Fill_Bar::Change_Ability_Bar(UUserWidget* Widget_Reference, UPARAM(ref) FDynamic_Ability_Struct &Ability_Data, int32 Delta_Ability, float Rate)
  144. {
  145.     // Copy all input data to our internal variables for use in other functions
  146.     I_Ability_Data = &Ability_Data;
  147.     I_WidgetRef = Widget_Reference;
  148.     I_ProgressBarName = I_Ability_Data->Progress_Bar_Name;
  149.  
  150.     // Ensure we have a "max" value for our ability to prevent dividing by "0" and crashing the engine
  151.     if (I_Ability_Data->Ability_Max_Value > 0)
  152.     {
  153.         I_Delta_Ability = (float(Delta_Ability) / I_Ability_Data->Ability_Max_Value);
  154.  
  155.         // Keep track of the desired "true" maximum capacity following expansion, we assume this value should be a whole number
  156.         CapacityCorrection = (float(Delta_Ability) + I_Ability_Data->Ability_Max_Value);
  157.     }
  158.     else
  159.     {
  160.         // We will make no changes to the ability bar if there is no "max" capacity to begin with
  161.         I_Delta_Ability = 0.f;
  162.     }
  163.  
  164.     // Ensure we have a positive value for our "Rate" to prevent dividing by "0" and crashing the engine
  165.     if (Rate > 0)
  166.     {
  167.         I_Rate = Rate;
  168.     }
  169.     else
  170.     {
  171.         // We will provide an arbitrary "rate" to increase the bar by if none is provided
  172.         I_Rate = 250.f;
  173.     }  
  174.  
  175.     // Get access to the actual widget component progress bar through the given name identifier
  176.     ProgressBarRef = dynamic_cast<UProgressBar*>(I_WidgetRef->GetWidgetFromName(FName(I_Ability_Data->Progress_Bar_Name)));
  177.    
  178.     // If we have successfully acquired a progress bar to work with, grab the fill type and set the starting values of our ability bar
  179.     if (ProgressBarRef != nullptr)
  180.     {
  181.         FillType = ProgressBarRef->BarFillType;
  182.         Set_Ability_Start_Values();
  183.     }
  184. }
  185.  
  186. // 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
  187. void UDynamic_Fill_Bar::Set_Ability_Start_Values()
  188. {
  189.     // 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)
  190.     if (I_Ability_Data->Hidden_Current_Scale == 0)
  191.     {
  192.         // Get the default size of the widget in the direction of expansion as specfied in the widget designer
  193.         FVector2D LocalSize = ProgressBarRef->GetCachedGeometry().GetLocalSize();
  194.         if (FillType == EProgressBarFillType::LeftToRight || FillType == EProgressBarFillType::RightToLeft || FillType == EProgressBarFillType::FillFromCenter)
  195.         {
  196.             OriginalSize = LocalSize.X;
  197.             HorizontalBar = true;
  198.             I_Ability_Data->Hidden_Horizontal_Bar = true;
  199.  
  200.         }
  201.         else if (FillType == EProgressBarFillType::TopToBottom || FillType == EProgressBarFillType::BottomToTop)
  202.         {
  203.             OriginalSize = LocalSize.Y;
  204.             HorizontalBar = false;
  205.             I_Ability_Data->Hidden_Horizontal_Bar = false;
  206.         }
  207.  
  208.         // We will calculate our goal scale and conversion ratio the first time we link a progress bar to this actor component for expansion
  209.         GoalScale = (I_CurrentScale + I_Delta_Ability);
  210.         ConversionRatio = (I_Ability_Data->Ability_Max_Value * Increase_Per_Cycle);
  211.     }
  212.     else
  213.     {
  214.         // 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
  215.         if (I_Ability_Data->Hidden_Horizontal_Bar == true)
  216.         {
  217.             HorizontalBar = true;
  218.         }
  219.         else if (I_Ability_Data->Hidden_Horizontal_Bar == false)
  220.         {
  221.             HorizontalBar = false;
  222.         }
  223.  
  224.         GoalScale = (I_Ability_Data->Hidden_Current_Scale + I_Delta_Ability);
  225.         ConversionRatio = I_Ability_Data->Hidden_Conversion_Ratio;
  226.         OriginalSize = I_Ability_Data->Hidden_Original_Size;
  227.     }
  228.  
  229.     // Check that we have cached geometry from a pre-pass, if not we will delay setting our widget parameters to prevent unwanted crashes
  230.     if (OriginalSize == 0.0f)
  231.     {
  232.         // Check every 0.1 seconds if we have cached geometry to work with, if not wait until we do before continuing
  233.         Parent_Actor->GetWorldTimerManager().SetTimer(DynamicBarHandle, this, &UDynamic_Fill_Bar::NoCacheGeometryYet, (0.1f), true);
  234.     }
  235.     else
  236.     {
  237.         // In case we started this timer we will clear it before we use it again to set the bar expansion rate
  238.         Parent_Actor->GetWorldTimerManager().ClearTimer(DynamicBarHandle);
  239.  
  240.         // Call the function to set our desired expansion direction
  241.         SetFillTypeParameters();
  242.     }  
  243. }
  244.  
  245. // Function to set our variable "Translation Directon" based on the selected fill type in the widegt designer
  246. void UDynamic_Fill_Bar::SetFillTypeParameters()
  247. {
  248.     switch (FillType)
  249.     {
  250.     case EProgressBarFillType::LeftToRight:
  251.         TranslationDirection = 1.0f;
  252.         break;
  253.     case EProgressBarFillType::RightToLeft:
  254.         TranslationDirection = -1.0f;
  255.         break;
  256.     case EProgressBarFillType::FillFromCenter:
  257.         TranslationDirection = 0.0f;
  258.         break;
  259.     case EProgressBarFillType::TopToBottom:
  260.         TranslationDirection = 1.0f;
  261.         break;
  262.     case EProgressBarFillType::BottomToTop:
  263.         TranslationDirection = -1.0f;
  264.         break;
  265.     default:
  266.         break;
  267.     }
  268.  
  269.     // Create a timer to expand the progress bar
  270.     Parent_Actor->GetWorldTimerManager().SetTimer(DynamicBarHandle, this, &UDynamic_Fill_Bar::UpdateBarLength, (1 / I_Rate), true);
  271. }
  272.  
  273. // Function to incrementally expand our progress bar
  274. void UDynamic_Fill_Bar::UpdateBarLength()
  275. {
  276.     // Continue to increase our progress bar size until it reaches the desired value, then we can clear our timer
  277.     if (FMath::RoundToInt(GoalScale * 100.f) > FMath::RoundToInt(I_CurrentScale * 100.f))
  278.     {
  279.         I_CurrentScale += Increase_Per_Cycle;
  280.  
  281.         // If we have a horizontal bar we will expand along the "X" axis, otherwise we will expand along the "Y" axis
  282.         if (HorizontalBar == true)
  283.         {
  284.             Temp2DScaleVec.X = (I_CurrentScale);
  285.             Temp2DTransVec.X = ((OriginalSize / 2) * (Increase_Per_Cycle * CurrentCycle)) * (TranslationDirection);
  286.            
  287.         }
  288.         else if (HorizontalBar == false)
  289.         {
  290.             Temp2DScaleVec.Y = (I_CurrentScale);
  291.             Temp2DTransVec.Y = ((OriginalSize / 2) * (Increase_Per_Cycle * CurrentCycle)) * (TranslationDirection);
  292.         }
  293.  
  294.         // Set our progress bar's new scale and translation
  295.         ProgressBarRef->SetRenderScale(Temp2DScaleVec);
  296.         ProgressBarRef->SetRenderTranslation(Temp2DTransVec);
  297.  
  298.         // Increment our cycle counter (this keeps track of how far we have translated our bar)
  299.         CurrentCycle++;
  300.  
  301.         // Increase the actual variable that holds our "max" capacity
  302.         I_Ability_Data->Ability_Max_Value += ConversionRatio;
  303.  
  304.         // Update our progress bar's percent to reflect the new capacity
  305.         ProgressBarRef->SetPercent(I_Ability_Data->Ability_Current_Value / I_Ability_Data->Ability_Max_Value);
  306.     }
  307.     else
  308.     {
  309.         // Once the bar has reached full expansion we will clear our timer and set the percent to a more precise value
  310.         Parent_Actor->GetWorldTimerManager().ClearTimer(DynamicBarHandle);
  311.         ProgressBarRef->SetPercent(I_Ability_Data->Ability_Current_Value / CapacityCorrection);
  312.  
  313.         // We will correct for rounding errors encountered during expansion here and simply "set" our final capacity to the Old Max + Delta value
  314.         I_Ability_Data->Ability_Max_Value = CapacityCorrection;
  315.  
  316.         // Set our new "hidden" values once we are finished expanding our ability bar
  317.         I_Ability_Data->Hidden_Current_Scale = I_CurrentScale;
  318.         I_Ability_Data->Hidden_Original_Size = OriginalSize;
  319.     }
  320. }
  321.  
  322. // 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
  323. void UDynamic_Fill_Bar::Save_Ability_Bar(FDynamic_Ability_Struct &Saved_Ability_Data)
  324. {
  325.     Saved_Ability_Data.Ability_Current_Value = I_Ability_Data->Ability_Current_Value;
  326.     Saved_Ability_Data.Ability_Max_Value = I_Ability_Data->Ability_Max_Value;
  327.     Saved_Ability_Data.Progress_Bar_Name = I_ProgressBarName;
  328.     Saved_Ability_Data.Hidden_Current_Scale = I_Ability_Data->Hidden_Current_Scale;
  329.     Saved_Ability_Data.Hidden_Conversion_Ratio = I_Ability_Data->Hidden_Conversion_Ratio;
  330.     Saved_Ability_Data.Hidden_Original_Size = I_Ability_Data->Hidden_Original_Size;
  331.     Saved_Ability_Data.Hidden_Horizontal_Bar = I_Ability_Data->Hidden_Horizontal_Bar;
  332. }
  333.  
  334. // Function to prevent crashes when attempts are made to access non-existent cached geometry
  335. void UDynamic_Fill_Bar::NoCacheGeometryYet()
  336. {
  337.     Set_Ability_Start_Values();
  338. }
Advertisement
Add Comment
Please, Sign In to add comment