Crowd Visualization trait in Mass Framework

How does Crowd Visualization Trait works in Mass Framework? Mass Framework is a ECS for managing crowds and traffic in Unreal 5.0 and above.

Crowd Visualization Trait

Setup:

  • Params

    • RepresentationActorManagementClass: Class for adjusted controlling of spawned Actors, such as position adjustments
      // Copyright Epic Games, Inc. All Rights Reserved.
      #pragma once
      
      #include "MassCrowdRepresentationActorManagement.h"
      
      #include "SampleMassCrowdRepresentationActorManagement.generated.h"
      
      struct FMassEntityManager;
      class UMassCrowdSpawnerSubsystem;
      
      /**
      * Overridden actor management fro forsty specific
      */
      UCLASS(meta = (DisplayName = "Vrealmatic Crowd Visualization"))
      class VREALMATICSAMPLE_API USampleMassCrowdRepresentationActorManagement : public UMassCrowdRepresentationActorManagement
      {
      	GENERATED_BODY()
      
      	/**
      	* Method that will be bound to a delegate used post-spawn to notify and let the requester configure the actor
      	* @param SpawnRequestHandle the handle of the spawn request that was just spawned
      	* @param SpawnRequest of the actor that just spawned
      	* @param EntitySubsystem to use to retrieve the mass agent fragments
      	* @return The action to take on the spawn request, either keep it there or remove it.
      	*/
      	virtual EMassActorSpawnRequestAction OnPostActorSpawn(const FMassActorSpawnRequestHandle& SpawnRequestHandle, FConstStructView SpawnRequest, FMassEntityManager* EntitySubsystem) const override;
      
      	/**
      	 * Teleports the actor at the specified transform by preserving its velocity and without collision.
      	 * The destination will be adjusted to fit an existing capsule.
      	 * @param Transform is the new actor's transform
      	 * @param Actor is the actual actor to teleport
      	 * @param CommandBuffer to queue up anything that is thread sensitive
      	 */
      	 virtual void TeleportActor(const FTransform& Transform, AActor& Actor, FMassCommandBuffer& CommandBuffer) const override;
      };
      
      	
      // Copyright Epic Games, Inc. All Rights Reserved.
      
      #include "Mass/SampleMassCrowdRepresentationActorManagement.h"
      #include "IMassCrowdActor.h"
      #include "MassEntityManager.h"
      
      #include "MassCrowdSpawnerSubsystem.h"
      #include "Components/CapsuleComponent.h"
      #include "Character/OccupantCharacter.h"
      #include "Enum/MovementAction.h"
      
      EMassActorSpawnRequestAction USampleMassCrowdRepresentationActorManagement::OnPostActorSpawn(const FMassActorSpawnRequestHandle& SpawnRequestHandle, FConstStructView SpawnRequest, FMassEntityManager* EntitySubsystem) const
      {
      	check(EntitySubsystem);
      
      	const EMassActorSpawnRequestAction Result = Super::OnPostActorSpawn(SpawnRequestHandle, SpawnRequest, EntitySubsystem);
      	
      	const FMassActorSpawnRequest& MassActorSpawnRequest = SpawnRequest.Get<FMassActorSpawnRequest>();
      	checkf(MassActorSpawnRequest.SpawnedActor, TEXT("Expecting valid spawned actor"));
      
      	if (IMassCrowdActorInterface* MassCrowdActor = Cast<IMassCrowdActorInterface>(MassActorSpawnRequest.SpawnedActor))
      	{
      		MassCrowdActor->OnGetOrSpawn(EntitySubsystem, MassActorSpawnRequest.MassAgent);
      		//MassCrowdActor->SetMovementMode(EMovementMode::MOVE_Custom);
      	}
      
      	return Result;
      }
      
      void USampleMassCrowdRepresentationActorManagement::TeleportActor(const FTransform& Transform, AActor& Actor, FMassCommandBuffer& CommandBuffer) const
      {
      	FTransform RootTransform = Transform;
      
      	if (const UCapsuleComponent* CapsuleComp = Actor.FindComponentByClass<UCapsuleComponent>())
      	{
      		const FVector HalfHeight(0.0f, 0.0f, CapsuleComp->GetScaledCapsuleHalfHeight());
      		RootTransform.AddToTranslation(HalfHeight);
      
      		const FVector RootLocation = RootTransform.GetLocation();
      		const FVector SweepOffset(0.0f, 0.0f, 20.0f);
      		const FVector Start = RootLocation + SweepOffset;
      		const FVector End = RootLocation - SweepOffset;
      		FCollisionQueryParams Params;
      		Params.AddIgnoredActor(&Actor);
      		FHitResult OutHit;
      		if (Actor.GetWorld()->SweepSingleByChannel(OutHit, Start, End, Transform.GetRotation(), CapsuleComp->GetCollisionObjectType(), CapsuleComp->GetCollisionShape(), Params))
      		{
      			if (IMassCrowdActorInterface* MassCrowdActor = Cast(&Actor))
      			{
      				MassCrowdActor->SetAdditionalMeshOffset(RootTransform.GetLocation().Z - OutHit.Location.Z);
      			}
      
      			if (AOccupantCharacter* Character = Cast<AOccupantCharacter>(&Actor))
                  {
                      // Now you can access properties and methods of AOccupantCharacter
                      // ...
                  }
      			RootTransform.SetLocation(OutHit.Location);
      		}
      	}
      	// Skip Super(UMassCrowdRepresentationActorManagement) because we already done that work here and we needed to know the offset to send to the feet IK logic
      	UMassRepresentationActorManagement::TeleportActor(RootTransform, Actor, CommandBuffer);
      }
  • LODParams

    • LODMax Count

      • High: Maximum number of displayed entities with LODRepresentation::High
  • Overriding default value

    You may need to set Visualization trait values based on user preference, e.g. from the settings page. This can be done by overriding default values, see steps below:

    1. Creat a new derivated class of UMassCrowdVisualizationTrait trait and use it in the project instead of the default trait
    2. Use an override of the BuildTemplate
    #pragma once
    
    #include "CoreMinimal.h"
    #include "MassCrowdVisualizationTrait.h"
    #include "OccupantsVisualizationTrait.generated.h"
    
    /**
     * 
     */
    UCLASS()
    class PATHFINDERIMPORTER_API UOccupantsVisualizationTrait : public UMassCrowdVisualizationTrait
    {
    	GENERATED_BODY()
    
    public:
    	UFUNCTION(BlueprintCallable, Category = "Visualization")
    	void SetLODMaxCountFromSession();
    
    	virtual void BuildTemplate(FMassEntityTemplateBuildContext& BuildContext, const UWorld& World) const override;
    };
    
    #include "Mass/Traits/OccupantsVisualizationTrait.h"
    #include "MassEntityTemplateRegistry.h"
    #include "Data/PermanentData.h"
    
    void UOccupantsVisualizationTrait::SetLODMaxCountFromSession()
    {
    	UPermanentData* SaveGameInstance = NewObject<UPermanentData>();
        if (SaveGameInstance)
        {
            int32 HlodOccupantsCount = SaveGameInstance->GetLodMaxCountHigh();
            LODParams.LODMaxCount[EMassLOD::High] = HlodOccupantsCount;
    
            UE_LOG(LogTemp, Display, TEXT("[UOccupantsVisualizationTrait] SET HLOD occupants count param to %d"), HlodOccupantsCount);
        }
        else
        {
            UE_LOG(LogTemp, Warning, TEXT("[UOccupantsVisualizationTrait] Failed to load SaveGame data, using default value."));
        }
    }
    
    void UOccupantsVisualizationTrait::BuildTemplate(FMassEntityTemplateBuildContext& BuildContext, const UWorld& World) const
    {	
        UOccupantsVisualizationTrait* MutableThis = const_cast<UOccupantsVisualizationTrait*>(this);
    	MutableThis->SetLODMaxCountFromSession();
    
    	Super::BuildTemplate(BuildContext, World);
    }