A project primarily used to test UE5 Mass AI system
- Entities go to smart objects (tree/rock) and 'collect' the resource
- Entities then go back to their 'house' which is their initial location
- Repeat until there are no more resources to gather
- Entities use the SmartObjectSubsystem to communicate with smart objects
- This ensures that entities wont fight over a single resource
- Smart objects use simple gameplaytags to determine what is a rock and what is a tree
MassAITesting-GatherAI2.mp4
MassAITesting-Simulation.mp4
MassAITesting-GatheringEntities.mp4
- Visualization Trait
- Mass Viewer Info Fragment
- Mass Actor Fragment (For actor visualization)
- Mass Visualization LOD Processor (Index 41)
- LODCollectorProcessor is disabled by default for some reason, which is needed when entity counts are high
- Mass LOD Collector Trait
- Mass LOD Collector Processor (Index 17)
- Using Assorted Fragments and Agent Radius Fragment will allow you to override the default radius of agents for the entity
- The height is simply interpolated based on the target location and current height. It should not be relied on to give completely accurate results when the distance is far. A possible solution would be to update the target location z during movement.
- Fragments hold data (FMassFragment)
- Filter entities using tags (FMassTag)
- Traits contain fragments/tags (UMassEntityTraitBase)
- Processors use fragment data to perform tasks on entities (UMassProcessor)
- StateTree and Fragments are sortof like BehaviorTree and Blackboard
- ObserverProcessors can observe more than one fragment/tag by overriding the Register() function.
- Evaluators basically gather data to be used in the state tree
- Enter Conditions are used on leaf nodes to see whether a leaf should be executed
- Tasks from ST are like BT, execute logic
- Transitions allow the state tree to go to other branches based on a condition
- Reference: https://docs.unrealengine.com/5.0/en-US/overview-of-state-tree-in-unreal-engine/
- Category for UPROPERTY in InstanceData determines what kind of value it is (Input, Output, Parameter)
- State Tree will throw an ensure when there are fragments missing for a task to execute. The only way I know to debug which fragments are missing is to goto the task and look for TStateTreeExternalDataHandle in the header file.
State Tree Experimental Findings
- Tick on StateTree Tasks are only ran once and with subscribed signals (see UMassStateTreeProcessor)
- I found no feasable way to subscribe signals in MassStateTreeProcessor. As a hacky solution just reuse one of the hardcoded signals
- A SmartObjectDefinition needs USmartObjectMassBehaviorDefinition and ALL default tag filters to show on Mass SmartObject Eval evaluator.
- UseSmartObjectTask will only execute USmartObjectMassBehaviorDefinition, meaning only C++ logic for the time
- Destroying a smart object safely in USmartObjectMassBehaviorDefinition should be done using PushCommand(). Lets the SmartObjectUseTask release the smart object before destruction. (may be source of ensures being fired, need to investigate further)
- Empty states with transitions seem to produce unexpected behavior. The state tree also always needs to be in an active state, even if idle.
- SmartObjectUseTask modifies MassMoveTarget around line 163-164, caused a headache since entities would not move after using a smart object.
- FMassSmartObjectHandler should be used rather than directly getting the smart object subsystem in mass....i think (seems to be used in~~~~ state tree tasks).
- Its possible to apply unique textures to each instance through an atlas and getting a random num to choose the frame - dont forget to use Vertex Interpolator and use a small float to fix precision issues.
- Vertex animation can be achieved by a similar tactic of using instance custom data to determine which animation to play
- To setup vertex animation, I used Vertex_Anim_Toolset and a UE5 fork
- In the processor, order is important when giving instance custom floats. FMassRepresentationFragment, FMassRepresentationLODFragment, and RepresentationSubsystem should be all you need to get started with instance custom data. (See URTSAnimationProcessor)
- Processors get their data from MassRepresentationSubsystem. At some point, actor templates are added via FindOrAddTemplateActor(). (Update: This is done for us most likely somewhere in the visualization/representation processors if you have actor visualization)
- The CDO is then retrieved via GetDefaultObject() and data can be retrieved. In this case, it is a CrowdCharacterDataAsset.
- A FCrowdCharacterDefinition is generated based on the data asset which contains the key info for animation among other things. (its a little more complex than described since the data is really retrieved from the FCrowdCharacterDefinition, just selected based on the human's properties)
- Finally, the animation data (UAnimToTextureDataAsset) is saved to the entities FCrowdAnimationFragment for future use in processors
- Note: Data assets appear to be added to the character BP (high actor visualization), this is why the data can be accessed.
- The actor can be retrieved using FMassRepresentationFragment.HighResTemplateActorIndex and RepresentationSubsystem->GetTemplateActorClass
- To be honest, a simple SharedFragment is probably sufficient for simple use-cases. I definitely might change my mind when I attempt to sync actor/ISM animation
- Actual anim state index is updated in UMassProcessor_Animation and custom data is updated at UMassCrowdUpdateISMVertexAnimationProcessor::UpdateISMVertexAnimation in various processors
- TPointHashGrid3 performance is considerably worse compared to THierarchicalHashGrid2D
- This could be caused by the extra dimension, testing scenario, or more efficient logic for mass (as THierarchicalHashGrid2D is used in MassAvoidance)
- Worst case scenario for similar search query: 235.9μs -> 8μs (x30 performance boost!)
- Find a way to use Mass SmartObject Eval effectively in the State Tree (DONE)
- Convert logic in RTSMovementProcessor to State Tree (KINDOF DONE)
- Agent gets stuck after using one smart object, find out why (DONE)
- The UseSmartObject task also messes with the MoveTargetFragment. It seems to be doing some stuff in ActivateActionAnimate() at MassZoneGraphNavigationUtils too. (DONE)