Current state and plans for ECS integration?

I am also working on an authoring component, so far its working well. The performance of pure ECS has me blown away, those transform syncs add up when you get to a few thousand units.

For this to work you have to add [ChunkSerializable] to the ManagedState class or the authoring Baker isn’t able to add it.

using Pathfinding.ECS;
using Pathfinding.PID;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;

namespace Pathfinding
{
	public class FollowerEntityAuthoring : MonoBehaviour
	{
		[SerializeField]
		AgentCylinderShape shape = new AgentCylinderShape
		{
			height = 2,
			radius = 0.5f,
		};
		[SerializeField]
		MovementSettings movement = new MovementSettings
		{
			follower = new PIDMovement
			{
				rotationSpeed = 600,
				speed = 5,
				maxRotationSpeed = 720,
				maxOnSpotRotationSpeed = 720,
				slowdownTime = 0.5f,
				desiredWallDistance = 0.5f,
				allowRotatingOnSpot = true,
				leadInRadiusWhenApproachingDestination = 1f,
			},
			stopDistance = 0.2f,
			rotationSmoothing = 0f,
			groundMask = -1,
			isStopped = false,
		};

		[SerializeField]
		OrientationMode orientationBacking = OrientationMode.ZAxisForward;
		[SerializeField]
		MovementPlaneSource movementPlaneSourceBacking = MovementPlaneSource.Graph;

		[SerializeField]
		public ManagedState managedState = new ManagedState
		{
			enableLocalAvoidance = false,
			pathfindingSettings = PathRequestSettings.Default,
		};

		[SerializeField]
		Pathfinding.ECS.AutoRepathPolicy autoRepathBacking = Pathfinding.ECS.AutoRepathPolicy.Default;

		public class Baker : Baker<FollowerEntityAuthoring>
		{
			public override void Bake(FollowerEntityAuthoring authoring)
			{
				Entity entity = GetEntity(TransformUsageFlags.Dynamic);
				var pos = authoring.transform.position;

				//Seems like no initial are values required
				AddComponent(entity, new MovementControl { });
				AddComponent(entity, new SearchState { });
				AddComponent(entity, new ResolvedMovement { });
				AddComponent(entity, new SimulateMovement { });
				AddComponent(entity, new SimulateMovementRepair { });
				AddComponent(entity, new SimulateMovementControl { });
				AddComponent(entity, new SimulateMovementFinalize { });

				//The components for follower entity to function
				AddComponent(entity, new MovementState(pos));
				AddComponent(entity, new AgentMovementPlane(authoring.transform.rotation));
				AddComponent(entity, new DestinationPoint
				{
					destination = new float3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity),
				});
				AddComponent(entity, authoring.autoRepathBacking);
				AddComponent(entity, authoring.movement);
				//We have to initialize the pathTracer temporarily in the Baker for it to succeed
				//Since this points to the authoring GameObject's ManagedState it will get disposed anyways
				var pathTracer = new PathTracer(Allocator.Temp);
				if (!authoring.managedState.pathTracer.isCreated)
				{
					authoring.managedState.pathTracer = pathTracer;
				}
				AddComponentObject(entity, authoring.managedState);
				AddComponent(entity, new ManagedStateInit { });
				AddComponent(entity, new MovementStatistics
				{
					estimatedVelocity = float3.zero,
					lastPosition = pos,
				});
				AddComponent(entity, authoring.shape);

				AddComponent(entity, new GravityState { });
				SetComponentEnabled<GravityState>(entity, authoring.managedState.enableGravity);

				if (authoring.orientationBacking == OrientationMode.YAxisForward)
				{
					AddComponent<OrientationYAxisForward>(entity);
				}

				AddComponent(entity, new ReadyToTraverseOffMeshLink { });
				SetComponentEnabled<ReadyToTraverseOffMeshLink>(entity, false);
				AddSharedComponent(entity, new AgentMovementPlaneSource { value = authoring.movementPlaneSourceBacking });
				pathTracer.Dispose();
			}
		}
	}

	//Tag to initialize ManagedState on an entity, the ManagedState should already be added to the entity
	public struct ManagedStateInit : IComponentData { }

	//Update in TransformSystemGroup ensures that the ManagedState is setup before any simulation occurs
	[UpdateInGroup(typeof(TransformSystemGroup))]
	public partial struct ManagedStateSetupSystem : ISystem
	{
		void OnUpdate(ref SystemState state)
		{
			var world = World.DefaultGameObjectInjectionWorld;
			NativeList<Entity> removeEntities = new NativeList<Entity>(Allocator.Temp);
			foreach (var (managedState, entity) in SystemAPI.Query<ManagedState>().WithAny<ManagedStateInit>().WithEntityAccess())
			{
				//The path tracer gets disposed in the authoring component we need to create it
				if (!managedState.pathTracer.isCreated)
				{
					managedState.pathTracer = new PathTracer(Allocator.Persistent);
					world.EntityManager.SetComponentData(entity, managedState);
				}
				removeEntities.Add(entity);
			}
			//Clear the init tag
			world.EntityManager.RemoveComponent(removeEntities.AsArray(), typeof(ManagedStateInit));
			removeEntities.Dispose();
		}
	}
}
1 Like