A way to drag(place) an actor into a map and have it automatically register its Mass agent component to take on Mass processor behaviors.
If there is anything I need to do differently for spawning an actor (ideally using SpawnActor) to also be processed by Mass’s systems.
The crowd example that’s being passed around YouTube from the docs isn’t quite what I’m looking for and it’s not that helpful for lead me to the functionality I’m looking for. With the that Mass crowd setup, when I drag an actor into the level, it actually creates a duplicate of itself that undergoes all mass process behaviors while the original is lifeless.
Also the Mass Spawner isn’t that flexible so far. I’ve been considering changing its code but I feel like I need to know that’s a rabbit hole that I should jump into first.
There’s a function the UMSBPFunctionLibrary::SpawnEntityFromEntityConfig in the demo. They use it to create bullets firing from the gun via blueprints.
I’m not too sure if a placed actor can just attach and use Mass Processors. I’m still look into that but this should give people some hope and help.
After looking over the example project some more. It really hit me that Mass Entity is really meant to be the sole driver and actors are just placeholders, “representations” or “visualizations” of the Entity. If I’m understanding this right, it’s a paradigm shift of putting much of the behavior for Entities inside Traits, Fragments, Processors and StateTrees and not much inside the actor itself. The StateTree being the real star of the show.
OnHit and events like that still get called from the spawned “visualizations” actors. Since High and Low detail visualization actors can be spawned based on LOD settings, there’s a few ideas come to mind to handle instance data such as Health. One way is to create a Health Fragment that is retrieved within the High and Low LOD Actor’s code that get’s updated OnHit . Or perhaps, the LOD setting can be set to never switch from High to Low, thus the High version will always be the instance data. Also maybe there’s ways to have the Processors handle eventsOnHit too. If Health Data is setup as a Fragment, then it’ll be an easy transition to figure out the “OnHit” Processor approach if needed later.
Here’s a bit of code related to the main question:
/**
* FMassEntityView wrapper for general blueprint use
* This can be rather evil due to the fact that the EntityView is transient in representing the actual state
* If you want to store an entity ID longer term you might be better off with the FMSEntityHandleBPWrapper
*/
USTRUCT(BlueprintType)
struct FMSEntityViewBPWrapper
{
GENERATED_BODY()
FMSEntityViewBPWrapper() = default;
FMSEntityViewBPWrapper(const FMassArchetypeHandle& Archetype, FMassEntityHandle EntityHandle)
{
EntityView = FMassEntityView(Archetype, EntityHandle);
}
FMSEntityViewBPWrapper(const FMassEntityManager& Manager, FMassEntityHandle EntityHandle)
{
EntityView = FMassEntityView(Manager,EntityHandle);
}
//This goofy function is needed due to protected functions in FMassEntityView preventing non template use :(
FMassArchetypeHandle TempArchetypeGet(const FMassEntityManager& Manager) const
{
return Manager.GetArchetypeForEntity(EntityView.GetEntity());
}
FMassEntityView EntityView;
};
// -------------------------------------------------------------
FMSEntityViewBPWrapper UMSBPFunctionLibrary::SpawnEntityFromEntityConfig(UMassEntityConfigAsset* MassEntityConfig, const UObject* WorldContextObject,
EReturnSuccess& ReturnBranch)
{
if (!MassEntityConfig)
{
ReturnBranch = EReturnSuccess::Failure;
return FMSEntityViewBPWrapper();
}
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3
const FMassEntityTemplate& EntityTemplate = MassEntityConfig->GetConfig().GetOrCreateEntityTemplate(*WorldContextObject->GetWorld(),*WorldContextObject->GetWorld());
#else
const FMassEntityTemplate& EntityTemplate = MassEntityConfig->GetConfig().GetOrCreateEntityTemplate(*WorldContextObject->GetWorld());
#endif
FMassEntityManager& EntityManager = WorldContextObject->GetWorld()->GetSubsystem<UMassEntitySubsystem>()->GetMutableEntityManager();
auto SpawnerSubsystem = WorldContextObject->GetWorld()->GetSubsystem<UMassSpawnerSubsystem>();
TArray<FMassEntityHandle> Entities;
SpawnerSubsystem->SpawnEntities(EntityTemplate.GetTemplateID(), 1, FStructView(), TSubclassOf<UMassProcessor>(), Entities);
//If no observers did anything, we can just assume the archetype is the same as our template
FMSEntityViewBPWrapper NewEntityWrapper;
NewEntityWrapper.EntityView = FMassEntityView(EntityManager, Entities[0]);
ReturnBranch = EReturnSuccess::Success;
return NewEntityWrapper;
}