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();
}
}
}