Tutorial: Your First 60 Minutes with Mass

Hey James,

thanks a lot for the detailed tutorial! It’s a perfect starting point for the MassEntity Plugins. :slight_smile:

One bug (?) I come across is that the Mass Spawner seems to have a max entity count. The EntityConfig currently only holds stationary, simple actors. As soon as I set the Spawn|Count Property of the MassSpawner to anything over 100, no entities are spawned at all. With a Count of 100 it is all fine.

grafik

With a DebugVisualization Trait instead of a Visualization the Count can be higher - so I fear that the reserved memory is at maximum here. If so, can I tweak that? (My machine does not use all available memory by far here.)

Best
Stephen

Okay, had some more time to keep digging and just in case anyone wonders: The Visualization Trait has a parameter for max counts for each LOD:

This did the trick on my side and I can continue testing. :slight_smile:

hello @James.Keeling
thanks for spending time making this great tutorial.
is there anyway to upload the project used on this tutorial ?
thanks

I am checking on this. We do not have a way to host files on the Epic Dev Community, but it may be possible to host on another file sharing site.

I am working on a part 2 of this guide with more content added to the project, so I do not currently have a clean copy of the project that only has elements found in this guide.

2 Likes

wow what a nice and good news - thanks for your effort - we need more about this topic since its been long time since its revealed but not much tutorials about it - im looking forward in some topics - like replication - and transition between VAT (ISM ) and the full character (bc right now the transition is very bad when changing from high res to low res and ISM ) , and how to interact with the player character (is C++ required for it ? )

ps: i followed again ur tutorial and i have succeed to do the same as you

That’s really great to hear! Do you have any ETA when Mass AI/Zone will be production ready? Mass is really great in brining life to an environment!

How would you go about spawning different entities? Something like this doesnt work:

I do not have an ETA for this. I would imagine several releases before this is ready. The Mass plugins will likely be worked in the order of MassGameplay->MassAI->MassCrowd.

ZoneGraph has not seen much development outside of some bug fixes since 5.0. I do not know when it will be production ready as efforts have largely been focused on other areas. I imagine it will start to see more development once we have moved onto the MassAI plugin as that is the primary user of ZoneGraph currently.

1 Like

Thanks for getting back <3

@Tbunreal
Having the exact same issue on my end - Didn’t manage to spawn different Mass controlled actors.

Did anybody manage to achieve that?

1 Like

Hello!
If a cube is put in the zone graph curve, the mass crowd will just stop. How to let them get around the cube?

@Tbunreal as well for viz.

There was an issue in 5.3 where if you duplicated a mass entity config, it would also duplicate the GUID instead of creating a new one. Are you trying to use a duplicated entity config? Can you re-create one of them from scratch by adding all of the traits and/or fragments manually and try this again?

Mass does not have a direct link with the physics system, so it will not just “know” about an obstacle such as a mesh placed into a lane. If you wish for the crowd to avoid it, you can give the obstacle a mass component and config marking it as an avoidance obstacle. If it fully spans the lane, the crowd may not wander outside of the lane, but I do not fully recall how that works for finding a way around it.

I may be able to give some better guidance if you can explain what you are trying to do along with some screenshots of what you are seeing in PIE.

Hello!
I need help with a MassCrowd level in the “AI/Map” of “City Sample” where multiple cars and crowds appear.
The cars are managed by MassTraffic and the people by MassCrowed.
What are the differences between these?
I want multiple BP_humans to appear randomly, is it not a good idea to use them like cars in MassTraffic?

Hello, how does Mass work with multiplayer?

Mass currently only has minimal support for replication/multiplayer. Movement is the only thing that has any replication, but you could use the code for replicating movement and apply it to other areas for yourself. While that is possible, I would advise to wait if you can because…

We have plans on adding more serialization and replication support for Mass that will need to leverage the new networking tech in the engine which is called Iris. Another issue of built-in replication for various engine classes is that the data that needs replicating will be different for every project.

Depending on what you are using Mass to do, it is possible to have it work in multiplayer settings. As an example, the resources (rocks, tress, etc.) in Lego Fortnite are being run by Mass on the server. This makes use of a new plugin called Instanced Actors that will be experimental in 5.4, but we are actively working on the plugin to have most, if not all, of the functionality we made for Lego FN.

1 Like

Hi!

So the MassTraffic is a very complicated setup of rules and managers for ONLY the vehicles so they can merge, change lanes, avoid, react to traffic signals, and manage the parked cars. It is not an official plugin for the engine or supported outside of the City Sample project.

MassCrowd is a collection of common behaviors we had for simulating the pedestrians in the CitySample, and it is an official engine plugin that is experimental. It builds on top of our other Mass plugins for Gameplay and AI. It contains some of the StateTree tasks used in this tutorial such as the Find ZoneGraph Wander Location task and some navigation code directly made for the pedestrians. The plugin also has support for waiting for lane closures e.g. waiting for the crosswalks to be open at the traffic signals.

For your desire to have multiple humans appearing randomly, I would think to use the Crowd instead of the traffic plugin. You can have multiple entity configs created that have different classes for the visualization/representation and add them to the MassSpawner with the ratios you want them to be used. Alternately, you can use randomization either inside the BP for changing the mesh. We use a setup similar to this in CitySample where one entity config and BP class is used, but there are several different MetaHuman meshes used for the crowd.

Hey @James.Keeling; thanks for the tutorial!

Are these changes to support Iris targeted for 5.4.0 or later?

Also are there plans to push much more for the 5.4.0 release for Instanced Actors compared to what’s in the GitHub 5.4 branch? i.e. is it worth waiting days/weeks or can I get started with what’s on GitHub?

Hello!

We are actively working on the Instance Actors plugin. I think the majority of the work will be going into 5.5 now. So I believe the 5.4 version will not see much more work outside of bugfixes or changes in the .cpp files so they can be a part of hotfixes.

So start using the 5.4 version if you want to investigate the plugin. However, it is still experimental (but we are dedicated to getting it finished) and is subject to having API or header changes that can break code/assets in future major release versions.

-James

2 Likes

James. I write navmesh navigation for mass. I separate Build path and Follow path to two tasks. I create some move styles - Slow. Standart, Fast, and Run. For each styles I add two speed variations. When I try to run task then actors always use Max Speed but not desired speed from selected style. Then I force set Max Speed to desired. It right way?

And next - it right way to force call movement_params.Update()? I search calling this method and nothhing found (in examples too).

EStateTreeRunStatus FNavMeshPathFollowTask::EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
{
	FMassStateTreeExecutionContext& mass_context = static_cast<FMassStateTreeExecutionContext&>(Context);
	FNavMeshPathFollowTaskInstanceData& instance_data = Context.GetInstanceData<FNavMeshPathFollowTaskInstanceData>(*this);
	FAgentRadiusFragment& fragment_agent_radius = Context.GetExternalData(AgentRadiusHandle);
	FMassMoveTargetFragment& fragment_move_target = Context.GetExternalData(MoveTargetHandle);
	FNavMeshPathFragment& fragment_path = Context.GetExternalData(NavMeshPathFragmentHandle);
	FMassMovementParameters& movement_params = Context.GetExternalData(MovementParamsHandle);
	const FVector& agent_nav_location = Context.GetExternalData(TransformHandle).GetTransform().GetLocation();
	UNavigationSystemV1* navmesh_subsystem = Cast<UNavigationSystemV1>(Context.GetWorld()->GetNavigationSystem());
	FNavPathSharedPtr path = fragment_path.Path; // From Path Build Task

	if (!path->IsValid() && !fragment_path.active)
		return EStateTreeRunStatus::Failed;

	if (path->GetPathPoints().Num() <  2)
		return EStateTreeRunStatus::Failed;

	fragment_path.current_point_index = 1;
	movement_params.Update();
	fragment_path.desired_speed = FMath::Min(movement_params.GenerateDesiredSpeed(instance_data.MovementStyle, mass_context.GetEntity().Index) * instance_data.SpeedScale, movement_params.MaxSpeed); 
	movement_params.DefaultDesiredSpeed = fragment_path.desired_speed;

       // Force max speed to desired (FMassMoveTargetFragment always use MaxSpeed unstead DesiredSpeed)
	movement_params.MaxSpeed = fragment_path.desired_speed; 

	const TArray<FNavPathPoint>& path_points = path->GetPathPoints();
	const FVector& path_point_location = path_points[fragment_path.current_point_index];
	float segment_length = (path_point_location - agent_nav_location).Length();

	fragment_move_target.DesiredSpeed.Set(fragment_path.desired_speed);
	fragment_move_target.DistanceToGoal = segment_length;
	fragment_move_target.SlackRadius = 15;
	fragment_move_target.Center = path_point_location;
	fragment_move_target.Forward = (path_point_location - agent_nav_location).GetSafeNormal();
	fragment_move_target.CreateNewAction(EMassMovementAction::Move, *Context.GetWorld());

	return EStateTreeRunStatus::Running;
}

And next - is any plan to make smooth vertical interpolation from start point to end point for low LOD of static mesh? Now it change vertical position over time from params.

void UMassNavigationSmoothHeightProcessor::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context)
{
  EntityQuery.ForEachEntityChunk(EntityManager, Context, [this](FMassExecutionContext& Context)
  {
  #if WITH_MASSGAMEPLAY_DEBUG
  	if (UE::MassMovement::bFreezeMovement)
  	{
  		return;
  	}
  #endif // WITH_MASSGAMEPLAY_DEBUG
  	const int32 NumEntities = Context.GetNumEntities();
  	const float DeltaTime = Context.GetDeltaTimeSeconds();
  
  	const FMassMovementParameters& MovementParams = Context.GetConstSharedFragment<FMassMovementParameters>();
  	const TArrayView<FTransformFragment> LocationList = Context.GetMutableFragmentView<FTransformFragment>();
  	const TConstArrayView<FMassMoveTargetFragment> MoveTargetList = Context.GetFragmentView<FMassMoveTargetFragment>();
  
  	for (int32 EntityIndex = 0; EntityIndex < NumEntities; ++EntityIndex)
  	{
  	  FTransform& CurrentTransform = LocationList[EntityIndex].GetMutableTransform();
  	  const FMassMoveTargetFragment& MoveTarget = MoveTargetList[EntityIndex];
	     
  	  if (MoveTarget.GetCurrentAction() == EMassMovementAction::Move || MoveTarget.GetCurrentAction() == EMassMovementAction::Stand)
  	  {
  	  	// Set height smoothly to follow current move targets height.
  	  	FVector CurrentLocation = CurrentTransform.GetLocation();
  	  	FMath::ExponentialSmoothingApprox(CurrentLocation.Z, MoveTarget.Center.Z, DeltaTime, MovementParams.HeightSmoothingTime);
  	  	CurrentTransform.SetLocation(CurrentLocation);
  	  }
  	}
  });
}

Next - How to use signals? Where handling of signals is executed? I want to handle signals in task or another place. Example needed.