Storing multidimensional data without nested containers

I’m writing a component of my AI manager which needs to keep track of two things for every actor participating in a dynamic cutscene: what “phase” (arbitrary measure) of the cutscene they’re in, measured by an int, and which behavior tree(s) hold the desired AI logic for them to perform. The idea is that every time the stage of a cutscene changes, every participating actor checks the behavior tree they’re in against the behavior tree they’re supposed to be in, updating if needed. My first attempt did this with a struct, which works pretty well:



USTRUCT()
struct FSmartZoneRoleStruct
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY()
	FString RoleName;

	UPROPERTY()
	Int currentPhase; 

	UPROPERTY()
	AActor* roleActor; //This is the actor whose AIController we'll give trees from behaviorList to

	UPROPERTY(EditAnywhere)
	TArray<UBehaviorTree*> behaviorList;

	void Destroy() //For GC
	{
		RoleName = NULL;
		behaviorList.Empty();
		roleActor = nullptr;
	}
};

With this, all I need to do is check if roleActor’s AIController’s BehaviorTree == behaviorList[currentPhase], and if it doesn’t, change it. The problem I’m having is that I would like to add more than one behavior tree to a single phase, and let the actor pick one of the trees at random when it updates itself. This lets me do things like simulate a concert audience, by putting CheerBT, MoshBT, and HeadbangBT behavior trees in the FSmartZoneRoleStruct spectator’s behaviorList[1], and trusting that approximately one third of the crowd will end up doing each of the three desired behaviors.

This is where I’m running into trouble- I tried making my behaviorList a TMap<int, TArray<UBehaviorTree*>, but the compiler said that ue doesn’t like nested data containers- is there another obvious way to accomplish this?

I believe you can work around this by just wrapping the inner container in a struct.


USTRUCT()
struct FPossibleBehaviors
{
  GENERATED_BODY()

  UPROPERTY()
  TArray< UBehaviorTree* > Options;
};

...

UPROPERTY()
TArray< FPossibleBehaviors > behaviorList;

By the way, I’ve been working on a modification to the engine behavior tree to include a selector node which can choose a child at random or probabilistically based on some criteria. It should make stuff like this a bit easier. Hopefully it might make it into the engine before too long.

Ooh that works perfectly, thank you for the suggestion!

The selector node sounds very neat, I hope you have an easy time working it in; I’ve had to hack that behavior in before, so it will be very cool having it supported at the engine level. Random selection, especially at terminal nodes, is a surprisingly easy way to make background activity persuasive- the way hobos in Human Revolution rooted around in wastebins is an excellent example of that.