Developer_Bastian

Expose TArray to Blueprint

Feb 4th, 2024 (edited)
132
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.94 KB | Gaming | 0 0
  1. // Developer Bastian © 2024
  2. // License Creative Commons DEED 4.0 (https://creativecommons.org/licenses/by-sa/4.0/deed.en)
  3. // Tutorial video at https://youtu.be/RBKHzy4XRbc
  4. // Part of an Unreal Basics video tutorial series at: https://bit.ly/Unreal_Basics_en
  5.  
  6. #pragma once
  7.  
  8. #include "CoreMinimal.h"
  9. #include "Engine/DataTable.h"
  10. #include "UObject/NoExportTypes.h"
  11. #include "Templates/SharedPointer.h"
  12. #include "Containers/Array.h"
  13. #include "Runtime/Core/Public/Async/ParallelFor.h"
  14. #include "Misc/Guid.h"
  15. #include "Timer.h"
  16. #include "TArray.generated.h"
  17.  
  18.  
  19. /**
  20.  * Enum to structure sorting for FTArrayTestStruct.
  21.  */
  22. UENUM(BlueprintType)
  23.     enum class ETestArraySorting : uint8 {
  24.         E_NumberAsc     UMETA(DisplayName = "Number - Ascending"),
  25.         E_NumberDesc    UMETA(DisplayName = "Number - Descending"),
  26.         E_NameAsc       UMETA(DisplayName = "Name - Ascending"),
  27.         E_NameDesc      UMETA(DisplayName = "Name - Descending")
  28.     };
  29.  
  30. /**
  31.  * Struct to showcase the TArray.
  32.  */
  33. USTRUCT(BlueprintType)
  34. struct FTArrayTestStruct : public FTableRowBase
  35. {
  36. public:
  37.     GENERATED_USTRUCT_BODY()
  38.  
  39.     UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Array Test Struct")
  40.     FString Name;
  41.  
  42.     UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Array Test Struct")
  43.     int32 Number;
  44.  
  45.     FTArrayTestStruct() : Name(""), Number(0)
  46.     {
  47.     }
  48.  
  49.     #pragma region Sorting
  50.     static bool CompareNumberAscending(const FTArrayTestStruct& A, const FTArrayTestStruct& B)
  51.     {
  52.         return A.Number < B.Number;
  53.     }
  54.  
  55.     static bool CompareNumberDescending(const FTArrayTestStruct& A, const FTArrayTestStruct& B)
  56.     {
  57.         return A.Number > B.Number;
  58.     }
  59.  
  60.     static bool CompareNameAscending(const FTArrayTestStruct& A, const FTArrayTestStruct& B)
  61.     {
  62.         return A.Name < B.Name;
  63.     }
  64.  
  65.     static bool CompareNameDescending(const FTArrayTestStruct& A, const FTArrayTestStruct& B)
  66.     {
  67.         return A.Name > B.Name;
  68.     }
  69.     #pragma endregion Sorting
  70.  
  71.     #pragma region Mandatory Functions
  72. // generates a hash from the struct
  73.     // MANDATORY FOR MOST TArray FUNCTIONS!
  74.     friend uint32 GetTypeHash(const FTArrayTestStruct& Struct)
  75.     {
  76.         return HashCombine(GetTypeHash(Struct.Name), Struct.Number);
  77.     }
  78.  
  79.     // define a value that acts as comparison between two struct
  80.     // MANDATORY FOR MOST TArray FUNCTIONS!
  81.     FORCEINLINE bool operator==(const FTArrayTestStruct& StructToCompare) const
  82.     {
  83.         return Number == StructToCompare.Number;
  84.     }
  85. #pragma endregion Mandatory Functions
  86. };
  87.  
  88. /**
  89.  * Delegates to indicate queue changes
  90.  */
  91. DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnArrayChanged, bool, Changed);
  92.  
  93. /**
  94.  * Class to encapsulate TArray with examples for sorting and filtering
  95.  */
  96. UCLASS(BlueprintType, Transient)
  97. class UTArray : public UObject
  98. {
  99.     GENERATED_BODY()
  100.  
  101. public:
  102.  
  103.     UTArray()
  104.     {}
  105.  
  106.     ~UTArray()
  107.     {
  108.         BA_Array.Reset();
  109.     }
  110.  
  111.     #pragma region Delegates
  112.  
  113.     UPROPERTY(BlueprintAssignable, Category = "BA Container - Array"
  114.         , meta = (ToolTip = "Delegate to indicate add vent to array"))
  115.     FOnArrayChanged OnArrayAdd_Delegate;
  116.  
  117.     UPROPERTY(BlueprintAssignable, Category = "BA Container - Array"
  118.         , meta = (ToolTip = "Delegate to indicate remove event from array"))
  119.     FOnArrayChanged OnArrayRemove_Delegate;
  120.  
  121. #pragma endregion Delegates
  122.  
  123. private:
  124.     TArray<FTArrayTestStruct> BA_Array;
  125.  
  126. public:
  127.     #pragma region Public Functions
  128.  
  129.     #pragma region Adding Elements
  130.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  131.         , meta = (CompactNodeTitle = "Add - Emplace"
  132.             , ToolTip = "Add an item to the Array"))
  133.     FORCEINLINE void Array_Add(UPARAM(ref) FTArrayTestStruct& Value, bool Broadcast)
  134.     {
  135.         // Emplace avoids creating a temporary variable,
  136.         // which is often undesirable for non-trivial value types.
  137.         // As a rule of thumb, use Add for trivial types and Emplace otherwise.
  138.         // Emplace will never be less efficient than Add.
  139.         this->BA_Array.Emplace(Value);
  140.         if (Broadcast)
  141.             this->OnArrayAdd_Delegate.Broadcast(true);
  142.     }
  143.  
  144.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  145.         , meta = (CompactNodeTitle = "Add - Move Temp"
  146.             , ToolTip = "Add an item to the Array"))
  147.     FORCEINLINE void Array_AddMoveTemp(UPARAM(ref) FTArrayTestStruct& Value, bool Broadcast)
  148.     {
  149.         // MoveTemp will cast a reference to an rvalue reference.
  150.         // It essentially just shifts points instead of doing a Value copy to a new address.
  151.         this->BA_Array.Add(MoveTemp(Value));
  152.         if (Broadcast)
  153.             this->OnArrayAdd_Delegate.Broadcast(true);
  154.     }
  155.  
  156.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  157.         , meta = (CompactNodeTitle = "Add - Push"
  158.             , ToolTip = "Add an item to the end of the Array. Wil use MoveTemp if possible"))
  159.     FORCEINLINE void Array_Push(UPARAM(ref) FTArrayTestStruct& Value, bool Broadcast)
  160.     {
  161.         if (Broadcast)
  162.             this->OnArrayAdd_Delegate.Broadcast(true);
  163.         // tries to use MoveTemp internally.
  164.         this->BA_Array.Push(Value);
  165.     }
  166.  
  167.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  168.         , meta = (CompactNodeTitle = "Add - Unique"
  169.             , ToolTip = "Add an item to the Array while checking uniqueness"))
  170.     FORCEINLINE void Array_AddUnique(UPARAM(ref) FTArrayTestStruct& Value, bool Broadcast)
  171.     {
  172.         // AddUnique only adds a new element to the container if an equivalent element doesn't already exist.
  173.         // Equivalence is checked by using the element type's operator==:
  174.         //
  175.         // AddUnique will have to parse the entire array checking for duplicates!
  176.         this->BA_Array.AddUnique(Value);
  177.         if (Broadcast)
  178.             this->OnArrayAdd_Delegate.Broadcast(true);
  179.     }
  180.  
  181.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  182.         , meta = (CompactNodeTitle = "Insert At"
  183.             , ToolTip = "Insert an item to the Array into a given index. Will preserve order"))
  184.     FORCEINLINE void Array_InsertAt(UPARAM(ref) FTArrayTestStruct& Value, int32 Position, bool Broadcast)
  185.     {
  186.         this->BA_Array.Insert(Value, Position);
  187.         if (Broadcast)
  188.             this->OnArrayAdd_Delegate.Broadcast(true);
  189.     }
  190.  
  191. #pragma endregion Adding Elements
  192.  
  193.     #pragma region Removing Elements
  194.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  195.         , meta = (CompactNodeTitle = "Remove"
  196.             , ToolTip = "Removes an item from the array"))
  197.     FORCEINLINE void Array_Remove(UPARAM(ref) FTArrayTestStruct& Value, bool Broadcast)
  198.     {
  199.         this->BA_Array.Remove(Value);
  200.         if (Broadcast)
  201.             this->OnArrayRemove_Delegate.Broadcast(true);
  202.     }
  203.  
  204.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  205.         , meta = (CompactNodeTitle = "Remove At"
  206.             , ToolTip = "Removes an item from the array on a given position. Will return true on successful removal, or false if position is not valid"))
  207.     FORCEINLINE bool Array_RemoveAt(int32 Position, bool Broadcast)
  208.     {
  209.         if (this->BA_Array.IsValidIndex(Position))
  210.         {
  211.             this->BA_Array.RemoveAt(Position);
  212.             if (Broadcast)
  213.                 this->OnArrayRemove_Delegate.Broadcast(true);
  214.             return true;
  215.         }
  216.         else
  217.             return false;
  218.     }
  219.  
  220.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  221.         , meta = (CompactNodeTitle = "Pop"
  222.             , ToolTip = "Removes first item from array. If this is standard access pattern, think about using a queue"))
  223.     FORCEINLINE FTArrayTestStruct Array_Pop(int32 Position, bool Broadcast)
  224.     {
  225.         return this->BA_Array.Pop(true);
  226.     }
  227.  
  228.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  229.         , meta = (CompactNodeTitle = "Remove All Starting With"
  230.             , ToolTip = "Removes all elements that match a defined predicate"))
  231.     FORCEINLINE void Array_RemoveAllStartingWith(FString StartsWith, bool Broadcast)
  232.     {
  233.         // Example for using a predicate to filter all items matching the predicate condition
  234.         this->BA_Array.RemoveAll(
  235.             [StartsWith](const FTArrayTestStruct& A) {
  236.                 return A.Name.StartsWith(StartsWith, ESearchCase::IgnoreCase);
  237.             }
  238.         );
  239.         if (Broadcast)
  240.             this->OnArrayRemove_Delegate.Broadcast(true);
  241.     }
  242.  
  243.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  244.         , meta = (CompactNodeTitle = "Empty"
  245.             , ToolTip = "Empties the array - set NewCapacity to zero if you dont need to reserve space for new content, otherwise provide the expected capacity"))
  246.     FORCEINLINE void Array_Empty(int32 NewCapacity, bool Broadcast)
  247.     {
  248.         this->BA_Array.Empty(NewCapacity);
  249.         if (Broadcast)
  250.             this->OnArrayRemove_Delegate.Broadcast(true);
  251.     }
  252. #pragma endregion Removing Elements
  253.    
  254.    
  255.     UFUNCTION(BlueprintCallable, BlueprintPure, Category = "BA Container - Array"
  256.         , meta = (CompactNodeTitle = "No of values"
  257.             , ToolTip = "Returns the number of values within this Array"))
  258.     FORCEINLINE int32 Array_NumberOfValues()
  259.     {
  260.         return this->BA_Array.Num();
  261.     }
  262.    
  263.     UFUNCTION(BlueprintCallable, BlueprintPure, Category = "BA Container - Array"
  264.         , meta = (CompactNodeTitle = "Contains"
  265.             , ToolTip = "Check if a given value exists"))
  266.     FORCEINLINE bool Array_Contains(UPARAM(ref) FTArrayTestStruct& Value)
  267.     {
  268.         return this->BA_Array.Contains(Value);
  269.     }
  270.  
  271.     UFUNCTION(BlueprintCallable, BlueprintPure, Category = "BA Container - Array"
  272.         , meta = (CompactNodeTitle = "Values"
  273.             , ToolTip = "Returns all array values"))
  274.     FORCEINLINE TArray<FTArrayTestStruct> Array_Values()
  275.     {
  276.         return this->BA_Array;
  277.     }
  278.  
  279.     #pragma region Searching
  280.  
  281.     /**
  282.      * Example of a Filter Function
  283.      * Add your custom filter criteria in lambda function within FilterByPredicate
  284.      *
  285.      * @StartsWith String that the Values needs to start with
  286.      * @returns TArray<FTArrayTestStruct>
  287.      */
  288.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  289.         , meta = (CompactNodeTitle = "Find Name Start"
  290.             , ToolTip = "Returns all items with name starting like parameter given"))
  291.     FORCEINLINE TArray<FTArrayTestStruct> Array_GetNamesStartingWith(const FString& StartsWith)
  292.     {
  293.         return this->BA_Array.FilterByPredicate(
  294.             [StartsWith](const FTArrayTestStruct& A) {
  295.                 return A.Name.StartsWith(StartsWith, ESearchCase::IgnoreCase);
  296.             }
  297.         );
  298.     }
  299.  
  300. #pragma endregion Searching
  301.  
  302.     #pragma region Sorting
  303.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  304.         , meta = (CompactNodeTitle = "Sort Array"
  305.             , ToolTip = "Sort the Values as FTArrayTestStruct by Enum ETestArraySorting"))
  306.     FORCEINLINE void Array_Sort(ETestArraySorting Sort)
  307.     {
  308.         // Sorting with Lambda
  309.         //this->BA_Array.Sort([](const FTArrayTestStruct& A, const FTArrayTestStruct& B) {
  310.         //  return A.Number > B.Number;
  311.         //  }
  312.         //);
  313.        
  314.         // Better to make the search logic part of your Struct class and reference this:
  315.         switch (Sort)
  316.         {
  317.         case ETestArraySorting::E_NumberAsc:
  318.             this->BA_Array.Sort(FTArrayTestStruct::CompareNumberAscending);
  319.             break;
  320.         case ETestArraySorting::E_NumberDesc :
  321.             this->BA_Array.Sort(FTArrayTestStruct::CompareNumberDescending);
  322.             break;
  323.         case ETestArraySorting::E_NameAsc:
  324.             this->BA_Array.Sort(FTArrayTestStruct::CompareNameAscending);
  325.             break;
  326.         case ETestArraySorting::E_NameDesc:
  327.             this->BA_Array.Sort(FTArrayTestStruct::CompareNameDescending);
  328.             break;
  329.  
  330.         default: break;
  331.         }
  332.     }
  333. #pragma endregion Sorting
  334.  
  335.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  336.         , meta = (CompactNodeTitle = "Iterate Array"
  337.             , ToolTip = "Example to iterate over array, adding a prefix to all Struct.Names"))
  338.     FORCEINLINE float Array_Iterate(UPARAM(ref) FString& Prefix)
  339.     {
  340.         // for demonstration, we set a timer and report total time needed for operation
  341.         Timer t;
  342.         t.Start();
  343.         for (FTArrayTestStruct& Value : BA_Array)
  344.         {
  345.             Value.Name.InsertAt(0, Prefix);
  346.         }
  347.         return t.Stop();
  348.     }
  349.  
  350.     UFUNCTION(BlueprintCallable, Category = "BA Container - Array"
  351.         , meta = (CompactNodeTitle = "Parallel Iterate Array"
  352.             , ToolTip = "Example to parallel-foreach iterate over array, adding a prefix to all Struct.Names"))
  353.     FORCEINLINE float Array_IterateParallel(UPARAM(ref) FString& Prefix)
  354.     {
  355.         // for demonstration, we set a timer and report total time needed for operation
  356.         Timer t;
  357.         t.Start();
  358.         FCriticalSection Mutex;
  359.         ParallelFor(BA_Array.Num(), [&, Prefix](int32 i)
  360.         {
  361.             FScopeLock Lock(&Mutex);
  362.             // whatever logic you need to do with this element
  363.             BA_Array[i].Name.InsertAt(0, Prefix);
  364.         });
  365.         return t.Stop();
  366.     }
  367.  
  368. #pragma endregion Public Functions
  369.  
  370.    
  371.  
  372. };
  373.  
Add Comment
Please, Sign In to add comment