Dynamic Arrays
Introduction to Dynamic Arrays
Use Case (1)
You can use dynamic arrays to store references to every ACharacter that crosses a certain point in your level, and then, after at least 5 ACharacters have crossed the point, you can iterate over all the actors and perform an action.
So in this way, you could make it a game-ending condition that if 5 bunny-rabbits reach their home, then the game ends in success.
During runtime you never know how long it will take for the bunny-rabbits to reach home, but via code you can track how many have reached home, whenever they do, and the iterate over all of them to make them perform some kind of victory dance as the level ends.
But you also never know which bunny-rabbits will be doing the dancing!
thus the word "dynamic"
Dynamic arrays enable you to track dynamically changing game conditions from the UE4 C++
Use Case (2)
You could make a dynamic array that is accessible to blueprints so your team members working in blueprints can add information to the dynamic array, which you as the programmer will then use in c++ during runtime.
But you as the programmer do not know how much data they will add!
So you could give the level designers a dynamic array to edit in blueprints, and they will fill it with 3 or 10 or 100 level names.
Then you can, in the C++, iterate over all of the level names and display them, without ever knowing in advance how many levels the designers added!
And the level designers can add more any time!
Summary
Dynamic arrays are one of the most essential tools for any game-logic that you want to do,
where the actions of the player, the in-game AI, and the rest of your team cannot be known in advance,
but need to be tracked, organized, and facilitated via UE4 C++ code systems.
Available Types
- Any C++ type
- Any UE4 C++ type, such as FLinearColor
- Pointer to a UObject or an AActor extending class
- Pointer to Blueprint Classes
- UE4++ Enums
- USTRUCTS() or USTRUCT() pointers
C++ Type
TArray BinaryArray;
UE4 C++ Type
TArray StarLocations;
Pointer to UObject Class
TArray Weapons;
Pointer to AActor Class
TArray FrogsThatAreHopping;
Pointer to Blueprint Class
TArray FlowerBlueprints;
UE4 C++ Enums
TArray GameControlKeys;
Arrays of USTRUCTS() or Pointers to UStructs
Let's say you have defined this USTRUCT()
Sample USTRUCT()
USTRUCT()
struct FFlowerStruct
{
GENERATED_USTRUCT_BODY()
UPROPERTY()
int32 NumPetals;
UPROPERTY()
FLinearColor Color;
UPROPERTY()
FVector Scale3D;
void SetFlowerColor(const FLinearColor& NewColor)
{
Color = NewColor;
}
FFlowerStruct()
{
NumPetals = 5;
Scale3D = FVector(1,1,1);
Color = FLinearColor(1,0,0,1);
}
};
Array of USTRUCTS()
You can make an array of FFlowerStructs as follows!
TArray Flowers;
The Pointer Version
TArray FlowerPtrs;
Blueprint-Accessible Dynamic Arrays
/** Add entries in BP Defaults, or during Runtime! Iterate over them using the For Each Loop BP Node */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Flowers")
TArray FlowerNames;
Core Functions
.Add()
TArray StarLocations;
StarLocations.Add(FVector(0,0,2000000));
.Remove()
//Defined in the .h file:
// TArray FrogsThatAreHopping;
// ACharacter* FrogThatIsTired;
FrogsThatAreHopping.Remove(FrogThatIsTired);
.RemoveAt()
//Remove first frog from the array
if(FrogsThatAreHopping.Num() > 0)
{
FrogsThatAreHopping.RemoveAt(0);
}
.Num()
Returns number of elements in the array (can be used when you need the length of the array).
ClientMessage("Total Flower Count");
ClientMessage(FString::FromInt(Flowers.Num()));
For Loops
Basic
// defined in .h TArray StarLocations;
//Print Star Locations
for(int32 b = 0; b < StarLocations.Num(); b++)
{
ClientMessage(StarLocations[b].ToString());
}
For AActor*
I am including very rigorous pointer safety and AActor validity checks,
because you dont want your whole game to crash to desktop, ever :)
//defined in the .h : TArray Flowers;
//Print out each flower's UE4 Name
AFlower* CurFlower = NULL;
for(int32 b = 0; b < Flowers.Num(); b++)
{
CurFlower = Flowers[b];
if(!CurFlower) continue;
if(!CurFlower->IsValidLowLevel()) continue;
//~~~~~~~~~~~~~~~~~~~~~~
ClientMessage(CurFlower->GetName());
}
Using Iterator
Posted originally by Solid Snake, thanks Solid Snake!
for (auto Iter(TArray.CreateIterator()); Iter; Iter++)
{
// *Iter to access what this iterator is pointing to.
}
Iterator AActor* Example
//defined in the .h : TArray Flowers;
//Print out each flower's UE4 Name
for (auto Itr(Flowers.CreateIterator()); Itr; Itr++)
{
if(!(*Itr)->IsValidLowLevel()) continue;
//~~~~~~~~~~~~~~~~~~~~~~
ClientMessage((*Itr)->GetName());
}
Abbreviated C++11 Syntax
This one is great when you don't need to know the index as you are iterating! ()
this:
for(int32 b = 0; b < StarLocations.Num(); b++)
{
ClientMessage(StarLocations[b].ToString());
}
becomes this:
for(const FVector& EachLocation : StarLocations)
{
ClientMessage(EachLocation.ToString());
}
If you want to change the value in the loop, and not just read it, then remove the const :)
Index Safety Check To Prevent Crashes
.IsValidIndex()
//defined in .h
//TArray RandomPercentValues;
//Pick a random Percent Value!
float AMyPlayerController::GetRandomPercenttValue() const
{
//Negative array indicies are always invalid
// and will always cause a crash if you try to use one.
//Use IsValidIndex to verify the index is in range
// ***before*** you try to access the array!
const int32 RandomIndex = FMath::RandRange(-50,10000);
//Safety Check
if( ! RandomPercentValues.IsValidIndex(RandomIndex)) return -1;
//~~~~~~~~~~~~~~~~~~~~~~~
return RandomPercentValues[RandomIndex];
}
Awesome Functions
.Empty()
Empty array of all current contents
//It's nighttime, no more hopping;
FrogsThatAreHopping.Empty();
.Append()
Add the entire contents of one array to the end of another!
TArray StarLocations;
TArray CloudLocations;
TArray StarAndCloudLocations;
StarLocations.Add(0,0,200000);
CloudLocations.Add(50,25,11000);
CloudLocations.Add(50,25,22200);
StarAndCloudLocations.Append(StarLocations);
StarAndCloudLocations.Append(CloudLocations);
//Print out all Locations
for(int32 b; b < StarAndCloudLocations.Num(); b++)
{
ClientMessage(StarAndCloudLocations[b].ToString());
}
Min/Max of Array (4.3 and higher)
I offered two Math Library functions to Epic which are now part of the Engine in 4.3, allowing you to get the Min/Max of a Dynamic Array of any datatype for which the operator< is defined!
Full details here:
Multi Dimensional Arrays
To make a 2 or higher dimensional array,
wrap the array in a UStruct, and then make an Array of the UStructs
.H
USTRUCT()
struct FFlowerField
{
GENERATED_USTRUCT_BODY()
UPROPERTY()
TArray Flowers;
FFlowerField()
{
}
};
//All Flower Fields on The Island
TArray FlowerFields;
.CPP
Then to Access a single Flower
//Is there at least 1 Flower Field?
if(FlowerFields.Num() > 0)
{
//Does the first field have at least 3 flowers? (could use .Num() too)
if(FlowerFields[0].Flowers.IsValidIndex(2))
{
//Number of petals on the 3rd flower in 1st field
ClientMessage(FString::FromInt(FlowerFields[0].Flowers[2].NumPetals));
}
}
Full 2D Array Code Sample
I have two extensive code samples for how to make 2D arrays that are BP-friendly and can be replicated!
My Fully Coded 2D Array Example
Replicating BP-Friendly 2D Array
[Epic Docs] TArray Optimization Techniques
Epic has posted an awesome article on ways you can optimize your use of TArray!