Rvo - custom movement script - how to update SetObstacleQuery

hi, i have a custom movement script with rvo

        [BurstCompile]
        [WithAll(typeof(RVOAgent), typeof(AgentBody))]
        partial struct UpdateMovementControlJob : IJobEntity
        {
            public void Execute(ref MovementControl movementControl, in LocalTransform transform, in AgentBody body,
                in AgentLocomotion locomotion, in AgentShape shape, in DestinationPoint destination)
            {
                movementControl = new MovementControl
                {
                    targetPoint = destination.destination,
                    endOfPath = destination.destination,
                    speed = locomotion.Speed,
                    maxSpeed = locomotion.Speed * 1.1f,
                    hierarchicalNodeIndex = -1,
                    targetRotation = 0,
                    targetRotationOffset = 0,
                    rotationSpeed = 0,
                    overrideLocalAvoidance = false,
                };
            }
        }

i am setting the destionation with other job, its not important.

the question is i have to set hierarchicalNodeIndex becouse i want unit to avoid walls, but all of my codes are in ECS

i examined the code, its using AstarPath.active.GetNearest(position, constraint) but i cant use in ecs, is there any other way to get nearest node in ecs ?

Hi

You can definitely use ECS, but it needs to be a managed job, not an unmanaged one.

To use GetNearest in a job, you’ll also need to use LockGraphDataForReading - A* Pathfinding Project

thanks, other question; i have like 20k entity, so calling AstarPath.active.GetNearest every frame might be not the best solution, is there any way to get hierarchicalNodeIndex ? i am sure agents are on walkable nodes

The built-in FollowerEntity movement script uses the PathTracer struct to handle locally repairing the path every frame. It is much faster than calling GetNearest for every agent.

However, handling 20k agents with that system is going to be a challenge in any case. You may have to ignore hierarchical nodes altogether to get that level of performance.

understood, i have a flow field from other system, i am setting rvo agents destination based on flow fields, and i have some obstacles. i want my agents to avoid obstacles.

what is your suggestion ? my obstacles are square like in the photo

whole code here;

  [BurstCompile]
    [RequireMatchingQueriesForUpdate]
    [UpdateInGroup(typeof(AIMovementSystemGroup))]
    [UpdateBefore(typeof(RVOSystem))]
    public partial struct RVOIntegrationSystem : ISystem
    {
        [BurstCompile]
        public void OnCreate(ref SystemState state)
        {
            state.RequireForUpdate<AgentBody>();
            state.RequireForUpdate<RVOAgent>();
        }


        public void OnUpdate(ref SystemState state)
        {
            var updateDestinationJob = new UpdateRVODestinationJob
            {
                LookAheadTime = 5.0f, // Look ahead factor for destination projection
            };

            state.Dependency = updateDestinationJob.Schedule(state.Dependency);

            var updateMovementControlJob = new UpdateMovementControlJob
            {
            };

            state.Dependency = updateMovementControlJob.Schedule(state.Dependency);
        }


        [BurstCompile]
        [WithAll(typeof(RVOAgent), typeof(AgentBody))]
        partial struct UpdateRVODestinationJob : IJobEntity
        {
            public float LookAheadTime;

            public void Execute(ref DestinationPoint destination, in LocalTransform transform, in AgentBody body,
                in AgentLocomotion locomotion)
            {
                float3 desiredDirection = body.Force;
                float forceLength = math.length(desiredDirection);

                if (forceLength > 0.01f)
                {
                    desiredDirection = desiredDirection / forceLength;

                    float3 projectedDestination =
                        transform.Position + desiredDirection * locomotion.Speed * LookAheadTime;

                    destination.destination = projectedDestination;
                }
                else if (math.lengthsq(body.Velocity) > 0.01f)
                {
                    float3 velocityDirection = math.normalize(body.Velocity);
                    destination.destination = transform.Position + velocityDirection * locomotion.Speed * LookAheadTime;
                }
                else
                {
                    destination.destination = transform.Position;
                }
            }
        }


        [BurstCompile]
        [WithAll(typeof(RVOAgent), typeof(AgentBody))]
        partial struct UpdateMovementControlJob : IJobEntity
        {
            public void Execute(ref MovementControl movementControl, in LocalTransform transform, in AgentBody body,
                in AgentLocomotion locomotion, in AgentShape shape, in DestinationPoint destination)
            {
                movementControl = new MovementControl
                {
                    targetPoint = destination.destination,
                    endOfPath = destination.destination,
                    speed = locomotion.Speed,
                    maxSpeed = locomotion.Speed * 1.1f,
                    hierarchicalNodeIndex = -1,
                    targetRotation = 0,
                    targetRotationOffset = 0,
                    rotationSpeed = 0,
                    overrideLocalAvoidance = false,
                };
            }
        }
    }
    
    [BurstCompile]
    [RequireMatchingQueriesForUpdate]
    [UpdateInGroup(typeof(AIMovementSystemGroup))]
    [UpdateAfter(typeof(RVOSystem))]
    public partial struct RVOMoveSystem : ISystem
    {
        EntityQuery entityQuery;

        public void OnCreate(ref SystemState state)
        {
            entityQuery = state.GetEntityQuery(
                ComponentType.ReadWrite<LocalTransform>(),
                ComponentType.ReadWrite<AgentBody>(),
                ComponentType.ReadOnly<AgentMovementPlane>(),
                ComponentType.ReadOnly<ResolvedMovement>(),
                ComponentType.ReadOnly<AgentLocomotion>()
            );
        }

        public void OnUpdate(ref SystemState state)
        {
            state.Dependency = new RVOMoveAgentsJob
            {
                deltaTime = SystemAPI.Time.DeltaTime
            }.ScheduleParallel(entityQuery, state.Dependency);
        }

        [BurstCompile]
        partial struct RVOMoveAgentsJob : IJobEntity
        {
            public float deltaTime;

            public void Execute(ref LocalTransform transform, ref AgentBody body,
                in AgentMovementPlane movementPlane, in ResolvedMovement resolvedMovement,
                in AgentLocomotion locomotion)
            {
               
                float3 movement = Pathfinding.ECS.JobMoveAgent.MoveWithoutGravity(
                    transform.Position,
                    in resolvedMovement,
                    in movementPlane,
                    deltaTime);

             
                transform.Position += movement;

        
                body.Velocity = movement / deltaTime;

   
                float3 velocity = body.Velocity;
                float speed = math.length(velocity);

                if (speed > 1e-3f)
                {
                    if (math.lengthsq(movement) > 0.00001f)
                    {
                        float angle = math.atan2(velocity.x, velocity.z);
                        transform.Rotation = math.slerp(
                            transform.Rotation,
                            quaternion.RotateY(angle),
                            deltaTime * locomotion.AngularSpeed
                        );
                    }
                }

                // Check if agent has reached destination
                // if (resolvedMovement.reachedDestination)
                // {
                //     body.Velocity = float3.zero;
                //     body.IsStopped = true;
                // }
            }
        }
    }

i am thinking about caching the hierarchical node indexes by coordinate (by cell), hierarchical nodes are not changing until the grid scanned or modified, right ?

also, if i set the hierarchical node indexes correctly, does the RVO system handle that ? i mean i am getting 30-40 fps without setting hierarchical node indexes, can i get same performance after i set the indexes ? @aron_granberg

i cached the ids and its seems working right now but i have some flickers, ill create other post about that