Limit number of items addable to a TArray property

The title is probably self-explanatory. I’ve got a c++ SceneComponent, and it has a TArray property exposed to Blueprint. I want the user of this (the blueprint authors) to be limited as to the number of items they can add to the TArray.

And to get greedy… I also want this limit to be dynamic (not hard coded to a specific number in the UPROPERTY macro.)

Any chance this is possible?

Then don’t expose the TArray directly to blueprint. Make functions that access it and expose those to bp.



.h
virtual void PostEditChangeProperty([FPropertyChangedEvent](https://docs.unrealengine.com/latest/INT/API/Runtime/CoreUObject/UObject/FPropertyChangedEvent/index.html) & PropertyChangedEvent) override;

.cpp
void MyClass::PostEditChangeProperty([FPropertyChangedEvent](https://docs.unrealengine.com/latest/INT/API/Runtime/CoreUObject/UObject/FPropertyChangedEvent/index.html) & PropertyChangedEvent){
Super::PostEditChangeProperty(PropertyChangedEvent);

// if (array > 5) array.pop

}


^ Will run when any property is changed. You can then see if the array is bigger than a number you want, and just remove the last element of the array.

I thought that doesnt work? last time I checked

I don’t think it’s possible. The only way you could enforce this is through accessors.
On the back end, the TArray is like a linked list and can dynamically grow in size as more size is needed. Compare this to traditional C++, where creating an array with a fixed size and then allocating past that would cause a buffer overflow… On top of that, you’re asking for an array that can be dynamically resized. It sounds like you should use a TArray and put some sanity checking code into your blueprint constructor to enforce array limits?

It’s not! It’s stored exactly the same way like a normal c++ array, except it can allocate a larger block and move all the elements over when it runs out of space.

Linked list or single block is irrelevant.

  • Do I want to force the allocation of x items all the time… even if I don’t need that many in some cases? (The issue if I use a fixed size array.) No. Waste of memory.
  • Do I want to allow blueprint designers to add as many items as they want even if they are killing runtime performance on platform x? No. Waste of time.
  • Do I want to force setters and getters for something so simple, making blueprints look worse than they already do? Not really.

So… I guess maybe I have to modify UE4 source code to add limits as a settable property in UPROPERTY markup. But then I have to maintain a code branch. :frowning:

The correct way to do what you want (if you want the array to be editable in component details panel) is using editor details panel customizations. You need a struct that will represent your array, the struct will have the array and the functions to access it. Then you define a property detail customization for that struct type. You will need to make your own detail panel controls for the type and that means that you have to learn slate.

It’s not necessary to edit UE4 source code, just make an editor module and add all your editor tools and customizations like this there.
This method is fully dynamic, so you will be able to change the array limit even at runtime.

The correct answer has already been posted by @Chris528.

Keep the array private or protected and only allow items to be added via a setter method. This is good practise anyway, as you know when items are being added.

The logic is simply:



void MyClass::AddItem(const FItem& Item)
{
    if (ItemArray.Num < MaxItems) {
        ItemArray.Add(Item);
    } else {
        UE_LOG(LogTemp, Warning, TEXT("Not adding item as MaxItems reached"));
    }
}


That’s not full solution for this specific problem but if someone wanted a fixed number of slots that could be filled from BP, they could use EditFixedSize property specifier(Property Specifiers | Unreal Engine 4.27 Documentation) and in constructor or header create necessary number of ‘slots’.

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, EditFixedSize)
TArray<SomeClass*> Array {nullptr, nullptr…}
or
Fill the Array in constructor if you don’t want to fill array in header

PS. Works for TMaps too :smiley: