Megafunk / MassSample

My understanding of Unreal Engine 5's experimental ECS plugin with a small sample project.
MIT License
681 stars 112 forks source link

Notes on how Mass Traffic Simulation works #43

Closed hvent90 closed 1 year ago

hvent90 commented 1 year ago

Hi folks,

The Mass Traffic plugin (i.e. the vehicles in the City Sample/Matrix Awakens demo) has a lot going on - vehicle simulation, damage, drivers, trailers, etc.

I went through the vehicle simulation and took some notes. This might be out of scope of this repo, so feel free to close it. However, I wanted to post it here in the event it helps anyone else out.

In the notes below, I explain the processors executed in order by each of the three simulation level of details (instanced static mesh, low resolution actor, high resolution actor).

Findings

Overall, the movement simulation is tightly coupled to the ZoneGraph. Vehicles choose which lane they should head towards based off of downstream traffic density. Vehicles don't have a destination in mind, however, the act of them evenly distributing traffic density gives the appearance that they do.

ZoneGraph has an A* pathfinding wrapper. It would be easier to retrofit the existing Mass Traffic vehicles implementation to intentionally pathfind to a location than it would be to have the vehicle go in a straight line towards a random location not on a ZoneGraph.

ISMC Actor

The ISMC simulation determines what lane it should be on in UMassTrafficLaneChangingProcessor. It then calculates its speed and its current progress in the lane in UMassTrafficVehicleControlProcessor. It then calculates the transform based off the current lane progress in UMassTrafficInterpolationProcessor. It finally updates the ISMC's position in UMassTrafficVehicleUpdateCustomVisualizationProcessor.

UMassTrafficLaneChangingProcessor::StartNewLaneChangesEntityQuery_Conditional, UpdateLaneChangesEntityQuery_Conditional

Has ReadWrite access on these fragments:

StartNewLaneChangeEntityQuery_Conditional finds what the next lane should be based off of downstream density. Updates FMassTrafficVehicleLaneChangeFragment.

FMassTrafficVehicleLaneChangeFragment calls FMassTrafficVehicleLaneChangeFragment::UpdateLaneChange. I think this is used to determine if the changing of lanes has finished.

UMassTrafficVehicleControlProcessor::SimpleVehicleControlEntityQuery_Conditional

Has ReadWrite access on these fragments:

Determines what speed the vehicle should be going and stores it in FMassTrafficVehicleControlFragment.Speed. It also calculates the new lane progress and updates FMassZoneGraphLaneLocationFragment.DistanceAlongLane!

UMassTrafficChooseNextLaneProcessor::EntityQuery_Conditional

Has ReadWrite access on these fragments:

This may keep the lane previously chosen by UMassTrafficLaneChangingProcessor::StartNewLaneChangesEntityQuery_Conditional. It will change if the upcoming intersection becomes closed. It does this by changing FMassTrafficVehicleControlFragment.ChooseNextLanePreference and FMassTrafficVehicleControlFragment.NextLane.

UMassTrafficInterpolationProcessor::EntityQueryNonOffLOD_Conditional

Has ReadWrite access on these fragments:

Based off of the calculations from UMassTrafficVehicleControlProcessor::SimpleVehicleControlEntityQuery_Conditional, it uses this to get the new position & rotation of the ISMC. Calculates the current position along the lane given the data found in FMassZoneGraphLaneLocationFragment. This doesn't appear to calculate the next position, but just interpolates given off of already done movement calculations.

UMassTrafficUpdateVelocityProcessor::EntityQuery_Conditional

Has ReadWrite access on these fragments:

Updates FMassVelocityFragment from the Speed found in FMassTrafficVehicleControlFragment.

VelocityFragment.Value = TransformFragment.GetTransform().GetRotation().GetForwardVector() * VehicleControlFragment.Speed;

AngularVelocityFragment.AngularVelocity = Chaos::FRotation3::CalculateAngularVelocity(RepresentationFragment.PrevTransform.GetRotation(), TransformFragment.GetTransform().GetRotation(), SimulationVariableTickFragment.DeltaTime);

UMassTrafficVehicleUpdateCustomVisualizationProcessor::EntityQuery

Updates the ISMC position via AddBatchedTransform.

ISMInfo[RepresentationFragment.StaticMeshDescIndex].AddBatchedTransform(GetTypeHash(Entity), TransformFragment.GetTransform(), RepresentationFragment.PrevTransform, RepresentationLODFragment.LODSignificance);

UMassTrafficUpdateDistanceToNearestObstacleProcessor::EntityQuery_Conditional

Has ReadWrite access on these fragments:

Calculates the distance to the next vehicle or obstacles

High Resolution Actor

The High Resolution Actor determines what lane it should be on in UMassTrafficLaneChangingProcessor. It then calculates PID values for throttle, brake, and steering in UMassTrafficVehicleControlProcessor. It calculates a simple physics sim in the event of switching to a lower LOD in UMassTrafficVehiclePhysicsProcessor - however, this won't be used if it stays in the high resolution actor. The PID values calculated earlier and then sent to the high resolution actor in UMassTrafficActorVehiclePhysicsProcessor. Finally, the FTransformFragment is synced with the high resolution actor in UMassTrafficPostPhysicsUpdateTrafficVehiclesProcessor.

UMassTrafficLaneChangingProcessor::StartNewLaneChangesEntityQuery_Conditional, UpdateLaneChangesEntityQuery_Conditional

No change from ISMC.

UMassTrafficVehicleControlProcessor::PIDVehicleControlEntityQuery_Conditional

Has ReadWrite access on these fragments:

Sets throttle, brake, and steering values on FMassTrafficPIDVehicleControlFragment.

UMassTrafficVehiclePhysicsProcessor::SimplePhysicsVehiclesQuery

This is done regardless if it is the high or low resolution actor. If it is the high resolution actor, it is discarded.

// Note: Simple vehicle physics is always run for both high & low viewer LOD vehicles. Most of the time
//       this simple simulation is discarded / ignored by the high LOD physics actor which does its
//       own simulation. However, when a high LOD drops back to medium LOD on a frame, this simulation
//       will have been done to ensure the spawned medium LOD will have been advanced forward.  

Has ReadWrite access on these fragments:

Runs a lite physics simulation of the vehicle suspension and drive forces and stores them in FMassTrafficVehiclePhysicsFragment.

UMassTrafficChooseNextLaneProcessor::EntityQuery_Conditional

No change from ISMC.

UMassTrafficActorVehiclePhysicsProcessor::ChaosPhysicsVehiclesQuery

Has ReadWrite access on these fragments:

This applies the PID values from FMassTrafficPIDVehicleControlFragment that were calculated back in UMassTrafficVehicleControlProcessor::PIDVehicleControlEntityQuery_Conditional to the vehicle actor via an interface function IMassTrafficVehicleControlInterface::Execute_SetVehicleInputs. The actor is responsible for mapping these values to the VMC.

UMassTrafficVehicleUpdateCustomVisualizationProcessor::EntityQuery

Doesn't do much besides sending some custom packed data to the actor's primitive components. I am guessing this is for lights?

UMassTrafficPostPhysicsUpdateTrafficVehiclesProcessor::PIDControlTrafficVehicleQuery

Has ReadWrite access on these fragments:

Syncs FTransformFragment to match with chaos vehicle actor. Applies linear velocity from FMassVelocityFragment and angular velocity from FMassTrafficAngularVelocityFragment to the root component.

Updates FMassZoneGraphLaneLocationFragment.DistanceAlongLane from actor location.

UMassTrafficUpdateDistanceToNearestObstacleProcessor::EntityQuery_Conditional

No change from ISMC.

Low Resolution Actor

The Low Resolution Actor determines what lane it should be on in UMassTrafficLaneChangingProcessor. It then calculates PID values for throttle, brake, and steering in UMassTrafficVehicleControlProcessor. It then calculates a simple physics sim in UMassTrafficVehiclePhysicsProcessor which will later be synced to the vehicle and wheels in UMassTrafficVehicleUpdateCustomVisualizationProcessor. Finally, the FTransformFragment is synced with the low resolution actor in UMassTrafficPostPhysicsUpdateTrafficVehiclesProcessor.

UMassTrafficLaneChangingProcessor::StartNewLaneChangesEntityQuery_Conditional, UpdateLaneChangesEntityQuery_Conditional

No change from ISMC.

UMassTrafficVehicleControlProcessor::PIDVehicleControlEntityQuery_Conditional

No change from High Resolution Actor.

UMassTrafficVehiclePhysicsProcessor::SimplePhysicsVehiclesQuery

No change from High Resolution Actor.

UMassTrafficChooseNextLaneProcessor::EntityQuery_Conditional

No change from ISMC.

UMassTrafficActorVehiclePhysicsProcessor::ChaosPhysicsVehiclesQuery

No change from High Resolution Actor.

UMassTrafficVehicleUpdateCustomVisualizationProcessor::EntityQuery

This is where the magic happens for the low resolution actor. It sets the actor's transform from the FTransformFragment, and it communicates with the UMassTrafficVehicleComponent to set the transform and angular velocities of the wheels.

UMassTrafficPostPhysicsUpdateTrafficVehiclesProcessor::PIDControlTrafficVehicleQuery

No change from High Resolution Actor.

UMassTrafficUpdateDistanceToNearestObstacleProcessor::EntityQuery_Conditional

No change from ISMC.

Megafunk commented 1 year ago

This is so much it merits its own repo/blog or something! Seems like a waste to bury it in here.