static void HandleCompletePath(ComponentDataFromEntity <LocalToWorld> localToWorldFromEntity, Entity entity, Rotation rotation, ref NavAgent agent, Parent surface, Translation translation, PhysicsWorld physicsWorld, float elapsedSeconds, EntityCommandBuffer.ParallelWriter commandBuffer, int entityInQueryIndex, NavSettings settings)
        {
            var rayInput = new RaycastInput
            {
                Start  = localToWorldFromEntity[entity].Position + agent.Offset,
                End    = math.forward(rotation.Value) * settings.ObstacleRaycastDistanceMax,
                Filter = new CollisionFilter
                {
                    BelongsTo    = NavUtil.ToBitMask(settings.ColliderLayer),
                    CollidesWith = NavUtil.ToBitMask(settings.ObstacleLayer)
                }
            };

            if (
                !surface.Value.Equals(agent.DestinationSurface) &&
                !NavUtil.ApproxEquals(translation.Value, agent.LocalDestination, settings.StoppingDistance) &&
                !physicsWorld.CastRay(rayInput, out _)
                )
            {
                agent.JumpSeconds = elapsedSeconds;

                commandBuffer.RemoveComponent <NavWalking>(entityInQueryIndex, entity);
                commandBuffer.RemoveComponent <NavSteering>(entityInQueryIndex, entity);
                commandBuffer.AddComponent <NavJumping>(entityInQueryIndex, entity);
                commandBuffer.AddComponent <NavPlanning>(entityInQueryIndex, entity);

                return;
            }

            commandBuffer.RemoveComponent <NavWalking>(entityInQueryIndex, entity);
            commandBuffer.RemoveComponent <NavSteering>(entityInQueryIndex, entity);
            commandBuffer.RemoveComponent <NavDestination>(entityInQueryIndex, entity);
        }
        protected override void OnUpdate()
        {
            var commandBuffer        = barrier.CreateCommandBuffer().AsParallelWriter();
            var elapsedSeconds       = (float)Time.ElapsedTime;
            var deltaSeconds         = Time.DeltaTime;
            var physicsWorld         = buildPhysicsWorld.PhysicsWorld;
            var settings             = navSystem.Settings;
            var pathBufferFromEntity = GetBufferFromEntity <NavPathBufferElement>();

            var localToWorldFromEntity = GetComponentDataFromEntity <LocalToWorld>(true);
            var fallingFromEntity      = GetComponentDataFromEntity <NavFalling>(true);
            var jumpingFromEntity      = GetComponentDataFromEntity <NavJumping>(true);
            var flockingFromEntity     = GetComponentDataFromEntity <NavFlocking>(true);

            Entities
            .WithNone <NavProblem, NavPlanning>()
            .WithAll <NavWalking, LocalToParent>()
            .WithReadOnly(localToWorldFromEntity)
            .WithReadOnly(physicsWorld)
            .WithReadOnly(jumpingFromEntity)
            .WithReadOnly(fallingFromEntity)
            .WithReadOnly(flockingFromEntity)
            .WithNativeDisableParallelForRestriction(pathBufferFromEntity)
            .ForEach((Entity entity, int entityInQueryIndex, ref NavAgent agent, ref Translation translation, ref NavSteering navSteering, ref Rotation rotation, in Parent surface) =>
            {
                if (!pathBufferFromEntity.HasComponent(entity) || agent.DestinationSurface.Equals(Entity.Null))
                {
                    return;
                }

                var pathBuffer = pathBufferFromEntity[entity];

                if (pathBuffer.Length == 0)
                {
                    HandleCompletePath(localToWorldFromEntity, entity, rotation, ref agent, surface, translation, physicsWorld, elapsedSeconds, commandBuffer, entityInQueryIndex, settings);
                    return;
                }

                var pathBufferIndex = pathBuffer.Length - 1;

                if (NavUtil.ApproxEquals(translation.Value, pathBuffer[pathBufferIndex].Value, settings.StoppingDistance))
                {
                    pathBuffer.RemoveAt(pathBufferIndex);
                }

                if (pathBuffer.Length == 0)
                {
                    return;
                }

                pathBufferIndex = pathBuffer.Length - 1;

                var heading = math.normalizesafe(pathBuffer[pathBufferIndex].Value - translation.Value);

                if (
                    !jumpingFromEntity.HasComponent(entity) &&
                    !fallingFromEntity.HasComponent(entity) &&
                    flockingFromEntity.HasComponent(entity)
                    )
                {
                    navSteering.AgentAvoidanceSteering.y = navSteering.SeparationSteering.y = navSteering.AlignmentSteering.y = navSteering.CohesionSteering.y = 0;

                    heading = math.normalizesafe(
                        heading +
                        navSteering.AgentAvoidanceSteering +
                        navSteering.SeparationSteering +
                        navSteering.AlignmentSteering +
                        navSteering.CohesionSteering
                        );

                    if (!navSteering.CollisionAvoidanceSteering.Equals(float3.zero))
                    {
                        heading = math.normalizesafe(heading + navSteering.CollisionAvoidanceSteering);
                    }
                }

                navSteering.CurrentHeading = heading;
            })
            .WithName("NavSteeringJob")
            .ScheduleParallel();

            barrier.AddJobHandleForProducer(Dependency);
            buildPhysicsWorld.AddInputDependencyToComplete(Dependency);

            var jumpBufferFromEntity = GetBufferFromEntity <NavJumpBufferElement>();

            Entities
            .WithNone <NavProblem>()
            .WithAny <NavFalling, NavJumping>()
            .WithAll <LocalToParent>()
            .WithReadOnly(fallingFromEntity)
            .WithReadOnly(jumpBufferFromEntity)
            .WithReadOnly(localToWorldFromEntity)
            .ForEach((Entity entity, int entityInQueryIndex, ref Translation translation, in NavAgent agent, in Parent surface) =>
            {
                if (agent.DestinationSurface.Equals(Entity.Null))
                {
                    return;
                }

                commandBuffer.AddComponent <NavPlanning>(entityInQueryIndex, entity);

                if (!jumpBufferFromEntity.HasComponent(entity))
                {
                    return;
                }
                var jumpBuffer = jumpBufferFromEntity[entity];
                if (jumpBuffer.Length == 0 && !fallingFromEntity.HasComponent(entity))
                {
                    return;
                }

                var destinationSurfaceLocalToWorld = localToWorldFromEntity[agent.DestinationSurface];
                var worldDestination = agent.LocalDestination.ToWorld(destinationSurfaceLocalToWorld);
                var velocity         = math.distance(translation.Value, worldDestination) / (math.sin(2 * math.radians(agent.JumpDegrees)) / agent.JumpGravity);
                var yVelocity        = math.sqrt(velocity) * math.sin(math.radians(agent.JumpDegrees));
                var waypoint         = translation.Value + math.up() * float.NegativeInfinity;

                if (!fallingFromEntity.HasComponent(entity))
                {
                    var xVelocity           = math.sqrt(velocity) * math.cos(math.radians(agent.JumpDegrees)) * agent.JumpSpeedMultiplierX;
                    var surfaceLocalToWorld = localToWorldFromEntity[surface.Value];

                    waypoint = jumpBuffer[0].Value
                               .ToWorld(destinationSurfaceLocalToWorld)
                               .ToLocal(surfaceLocalToWorld);

                    translation.Value.MoveTowards(waypoint, xVelocity * deltaSeconds);
                }

                translation.Value.y += (yVelocity - (elapsedSeconds - agent.JumpSeconds) * agent.JumpGravity) * deltaSeconds * agent.JumpSpeedMultiplierY;

                if (elapsedSeconds - agent.JumpSeconds >= settings.JumpSecondsMax)
                {
                    commandBuffer.RemoveComponent <NavJumping>(entityInQueryIndex, entity);
                    commandBuffer.AddComponent <NavFalling>(entityInQueryIndex, entity);
                }

                if (!NavUtil.ApproxEquals(translation.Value, waypoint, 1))
                {
                    return;
                }

                commandBuffer.AddComponent <NavNeedsSurface>(entityInQueryIndex, entity);
                commandBuffer.RemoveComponent <NavJumping>(entityInQueryIndex, entity);
            })
Exemplo n.º 3
0
        protected override void OnUpdate()
        {
            var commandBuffer          = barrier.CreateCommandBuffer().AsParallelWriter();
            var elapsedSeconds         = (float)Time.ElapsedTime;
            var deltaSeconds           = Time.DeltaTime;
            var physicsWorld           = buildPhysicsWorld.PhysicsWorld;
            var settings               = navSystem.Settings;
            var pathBufferFromEntity   = GetBufferFromEntity <NavPathBufferElement>();
            var localToWorldFromEntity = GetComponentDataFromEntity <LocalToWorld>(true);

            Entities
            .WithNone <NavProblem, NavPlanning>()
            .WithAll <NavWalking, LocalToParent>()
            .WithReadOnly(localToWorldFromEntity)
            .WithReadOnly(physicsWorld)
            .WithNativeDisableParallelForRestriction(pathBufferFromEntity)
            .ForEach((Entity entity, int entityInQueryIndex, ref NavAgent agent, ref Translation translation, ref Rotation rotation, in Parent surface) =>
            {
                if (!pathBufferFromEntity.HasComponent(entity) || agent.DestinationSurface.Equals(Entity.Null))
                {
                    return;
                }

                var pathBuffer = pathBufferFromEntity[entity];

                if (pathBuffer.Length == 0)
                {
                    HandleCompletePath(localToWorldFromEntity, entity, rotation, ref agent, ref pathBuffer, surface, translation, physicsWorld, elapsedSeconds, commandBuffer, entityInQueryIndex, settings);
                    return;
                }

                var pathBufferIndex = pathBuffer.Length - 1;

                if (NavUtil.ApproxEquals(translation.Value, pathBuffer[pathBufferIndex].Value, settings.StoppingDistance))
                {
                    pathBuffer.RemoveAt(pathBufferIndex);
                }

                if (pathBuffer.Length == 0)
                {
                    return;
                }

                pathBufferIndex = pathBuffer.Length - 1;

                translation.Value = Vector3.MoveTowards(translation.Value, pathBuffer[pathBufferIndex].Value, agent.TranslationSpeed * deltaSeconds);

                var lookAt = NavUtil.MultiplyPoint3x4(     // To world (from local in terms of destination surface).
                    localToWorldFromEntity[agent.DestinationSurface].Value,
                    pathBuffer[pathBufferIndex].Value
                    );

                lookAt = NavUtil.MultiplyPoint3x4(     // To local (in terms of agent's current surface).
                    math.inverse(localToWorldFromEntity[surface.Value].Value),
                    lookAt
                    );

                lookAt.y = translation.Value.y;

                var lookRotation = quaternion.LookRotationSafe(lookAt - translation.Value, math.up());

                if (math.length(agent.SurfacePointNormal) > 0.01f)
                {
                    lookRotation = Quaternion.FromToRotation(math.up(), agent.SurfacePointNormal) * lookRotation;
                }

                rotation.Value = math.slerp(rotation.Value, lookRotation, deltaSeconds / agent.RotationSpeed);
            })
            .WithName("NavWalkJob")
            .ScheduleParallel();

            barrier.AddJobHandleForProducer(Dependency);
            buildPhysicsWorld.AddInputDependency(Dependency);

            var jumpBufferFromEntity = GetBufferFromEntity <NavJumpBufferElement>();
            var fallingFromEntity    = GetComponentDataFromEntity <NavFalling>();

            Entities
            .WithNone <NavProblem>()
            .WithAny <NavFalling, NavJumping>()
            .WithAll <LocalToParent>()
            .WithReadOnly(fallingFromEntity)
            .WithReadOnly(jumpBufferFromEntity)
            .WithReadOnly(localToWorldFromEntity)
            .ForEach((Entity entity, int entityInQueryIndex, ref Translation translation, in NavAgent agent, in Parent surface) =>
            {
                if (agent.DestinationSurface.Equals(Entity.Null))
                {
                    return;
                }

                commandBuffer.AddComponent <NavPlanning>(entityInQueryIndex, entity);

                if (!jumpBufferFromEntity.HasComponent(entity))
                {
                    return;
                }
                var jumpBuffer = jumpBufferFromEntity[entity];

                if (jumpBuffer.Length == 0 && !fallingFromEntity.HasComponent(entity))
                {
                    return;
                }

                var destination = NavUtil.MultiplyPoint3x4(
                    localToWorldFromEntity[agent.DestinationSurface].Value,
                    agent.LocalDestination
                    );

                var velocity  = Vector3.Distance(translation.Value, destination) / (math.sin(2 * math.radians(agent.JumpDegrees)) / agent.JumpGravity);
                var yVelocity = math.sqrt(velocity) * math.sin(math.radians(agent.JumpDegrees));
                var waypoint  = translation.Value + math.up() * float.NegativeInfinity;

                if (!fallingFromEntity.HasComponent(entity))
                {
                    var xVelocity = math.sqrt(velocity) * math.cos(math.radians(agent.JumpDegrees)) * agent.JumpSpeedMultiplierX;

                    waypoint = NavUtil.MultiplyPoint3x4(     // To world (from local in terms of destination surface).
                        localToWorldFromEntity[agent.DestinationSurface].Value,
                        jumpBuffer[0].Value
                        );

                    waypoint = NavUtil.MultiplyPoint3x4(     // To local (in terms of agent's current surface).
                        math.inverse(localToWorldFromEntity[surface.Value].Value),
                        waypoint
                        );

                    translation.Value = Vector3.MoveTowards(translation.Value, waypoint, xVelocity * deltaSeconds);
                }

                translation.Value.y += (yVelocity - (elapsedSeconds - agent.JumpSeconds) * agent.JumpGravity) * deltaSeconds * agent.JumpSpeedMultiplierY;

                if (elapsedSeconds - agent.JumpSeconds >= settings.JumpSecondsMax)
                {
                    commandBuffer.RemoveComponent <NavJumping>(entityInQueryIndex, entity);
                    commandBuffer.AddComponent <NavFalling>(entityInQueryIndex, entity);
                }

                if (!NavUtil.ApproxEquals(translation.Value, waypoint, 1))
                {
                    return;
                }

                commandBuffer.AddComponent <NavNeedsSurface>(entityInQueryIndex, entity);
                commandBuffer.RemoveComponent <NavJumping>(entityInQueryIndex, entity);
            })
        protected override void OnUpdate()
        {
            var commandBuffer          = barrier.CreateCommandBuffer().AsParallelWriter();
            var elapsedSeconds         = (float)Time.ElapsedTime;
            var deltaSeconds           = Time.DeltaTime;
            var physicsWorld           = buildPhysicsWorld.PhysicsWorld;
            var pathBufferFromEntity   = GetBufferFromEntity <NavPathBufferElement>(true);
            var localToWorldFromEntity = GetComponentDataFromEntity <LocalToWorld>(true);

            Dependency = JobHandle.CombineDependencies(Dependency, buildPhysicsWorld.GetOutputDependency());

            Entities
            .WithNone <NavHasProblem, NavPlanning, NavJumping>()
            .WithAll <NavLerping, LocalToParent>()
            .WithReadOnly(pathBufferFromEntity)
            .WithReadOnly(localToWorldFromEntity)
            .WithReadOnly(physicsWorld)
            .ForEach((Entity entity, int entityInQueryIndex, ref NavAgent agent, ref Translation translation, ref Rotation rotation, in Parent surface) =>
            {
                if (!pathBufferFromEntity.HasComponent(entity) || agent.DestinationSurface.Equals(Entity.Null))
                {
                    return;
                }

                var pathBuffer = pathBufferFromEntity[entity];

                if (pathBuffer.Length == 0)
                {
                    return;
                }

                if (agent.PathBufferIndex >= pathBuffer.Length)
                {
                    --agent.PathBufferIndex;
                    return;
                }

                var localDestination = agent.LocalDestination;
                var localWaypoint    = pathBuffer[agent.PathBufferIndex].Value;

                if (
                    NavUtil.ApproxEquals(translation.Value, localWaypoint, 1) &&
                    ++agent.PathBufferIndex > pathBuffer.Length - 1
                    )
                {
                    var rayInput = new RaycastInput
                    {
                        Start  = localToWorldFromEntity[entity].Position + agent.Offset,
                        End    = math.forward(rotation.Value) * NavConstants.OBSTACLE_RAYCAST_DISTANCE_MAX,
                        Filter = new CollisionFilter
                        {
                            BelongsTo    = NavUtil.ToBitMask(NavConstants.COLLIDER_LAYER),
                            CollidesWith = NavUtil.ToBitMask(NavConstants.OBSTACLE_LAYER)
                        }
                    };

                    if (
                        !surface.Value.Equals(agent.DestinationSurface) &&
                        !NavUtil.ApproxEquals(translation.Value, localDestination, 1) &&
                        !physicsWorld.CastRay(rayInput, out RaycastHit hit)
                        )
                    {
                        agent.JumpSeconds = elapsedSeconds;
                        commandBuffer.AddComponent <NavJumping>(entityInQueryIndex, entity);
                        commandBuffer.AddComponent <NavPlanning>(entityInQueryIndex, entity);
                        return;
                    }

                    commandBuffer.RemoveComponent <NavLerping>(entityInQueryIndex, entity);
                    commandBuffer.RemoveComponent <NavNeedsDestination>(entityInQueryIndex, entity);
                    agent.PathBufferIndex = 0;
                    return;
                }

                translation.Value = Vector3.MoveTowards(translation.Value, localWaypoint, agent.TranslationSpeed * deltaSeconds);

                var lookAt = NavUtil.MultiplyPoint3x4(     // To world (from local in terms of destination surface).
                    localToWorldFromEntity[agent.DestinationSurface].Value,
                    localWaypoint
                    );

                lookAt = NavUtil.MultiplyPoint3x4(     // To local (in terms of agent's current surface).
                    math.inverse(localToWorldFromEntity[surface.Value].Value),
                    lookAt
                    );

                lookAt.y = translation.Value.y;

                var lookRotation = quaternion.LookRotationSafe(lookAt - translation.Value, math.up());

                if (math.length(agent.SurfacePointNormal) > 0.01f)
                {
                    lookRotation = Quaternion.FromToRotation(math.up(), agent.SurfacePointNormal) * lookRotation;
                }

                rotation.Value = math.slerp(rotation.Value, lookRotation, deltaSeconds / agent.RotationSpeed);
            })
            .WithName("NavWalkJob")
            .ScheduleParallel();

            barrier.AddJobHandleForProducer(Dependency);

            var jumpBufferFromEntity = GetBufferFromEntity <NavJumpBufferElement>();
            var fallingFromEntity    = GetComponentDataFromEntity <NavFalling>();

            Entities
            .WithNone <NavHasProblem>()
            .WithAny <NavFalling, NavJumping>()
            .WithAll <LocalToParent>()
            .WithReadOnly(fallingFromEntity)
            .WithReadOnly(jumpBufferFromEntity)
            .WithReadOnly(localToWorldFromEntity)
            .ForEach((Entity entity, int entityInQueryIndex, ref Translation translation, in NavAgent agent, in Parent surface) =>
            {
                if (agent.DestinationSurface.Equals(Entity.Null))
                {
                    return;
                }

                commandBuffer.AddComponent <NavPlanning>(entityInQueryIndex, entity);

                if (!jumpBufferFromEntity.HasComponent(entity))
                {
                    return;
                }
                var jumpBuffer = jumpBufferFromEntity[entity];

                if (jumpBuffer.Length == 0 && !fallingFromEntity.HasComponent(entity))
                {
                    return;
                }

                var destination = NavUtil.MultiplyPoint3x4(
                    localToWorldFromEntity[agent.DestinationSurface].Value,
                    agent.LocalDestination
                    );

                var velocity  = Vector3.Distance(translation.Value, destination) / (math.sin(2 * math.radians(agent.JumpDegrees)) / agent.JumpGravity);
                var yVelocity = math.sqrt(velocity) * math.sin(math.radians(agent.JumpDegrees));
                var waypoint  = translation.Value + math.up() * float.NegativeInfinity;

                if (!fallingFromEntity.HasComponent(entity))
                {
                    var xVelocity = math.sqrt(velocity) * math.cos(math.radians(agent.JumpDegrees)) * agent.JumpSpeedMultiplierX;

                    waypoint = NavUtil.MultiplyPoint3x4(     // To world (from local in terms of destination surface).
                        localToWorldFromEntity[agent.DestinationSurface].Value,
                        jumpBuffer[0].Value
                        );

                    waypoint = NavUtil.MultiplyPoint3x4(     // To local (in terms of agent's current surface).
                        math.inverse(localToWorldFromEntity[surface.Value].Value),
                        waypoint
                        );

                    translation.Value = Vector3.MoveTowards(translation.Value, waypoint, xVelocity * deltaSeconds);
                }

                translation.Value.y += (yVelocity - (elapsedSeconds - agent.JumpSeconds) * agent.JumpGravity) * deltaSeconds * agent.JumpSpeedMultiplierY;

                if (elapsedSeconds - agent.JumpSeconds >= NavConstants.JUMP_SECONDS_MAX)
                {
                    commandBuffer.RemoveComponent <NavJumping>(entityInQueryIndex, entity);
                    commandBuffer.AddComponent <NavFalling>(entityInQueryIndex, entity);
                }

                if (!NavUtil.ApproxEquals(translation.Value, waypoint, 1))
                {
                    return;
                }

                commandBuffer.AddComponent <NavNeedsSurface>(entityInQueryIndex, entity);
                commandBuffer.RemoveComponent <NavJumping>(entityInQueryIndex, entity);
            })
Exemplo n.º 5
0
        protected override void OnUpdate()
        {
            var commandBuffer          = barrier.CreateCommandBuffer().AsParallelWriter();
            var elapsedSeconds         = (float)Time.ElapsedTime;
            var localToWorldFromEntity = GetComponentDataFromEntity <LocalToWorld>(true);
            var physicsWorld           = buildPhysicsWorld.PhysicsWorld;
            var settings = navSystem.Settings;

            Entities
            .WithNone <NavProblem>()
            .WithChangeFilter <NavDestination>()
            .WithReadOnly(localToWorldFromEntity)
            .WithReadOnly(physicsWorld)
            .ForEach((Entity entity, int entityInQueryIndex, ref NavAgent agent, in NavDestination destination) =>
            {
                if (elapsedSeconds - agent.DestinationSeconds < settings.DestinationRateLimitSeconds)
                {
                    commandBuffer.AddComponent <NavDestination>(entityInQueryIndex, entity, destination);    // So that the change filter applies next frame.
                    return;
                }

                var collider = SphereCollider.Create(
                    new SphereGeometry()
                {
                    Center = destination.WorldPoint,
                    Radius = settings.DestinationSurfaceColliderRadius
                },
                    new CollisionFilter()
                {
                    BelongsTo    = NavUtil.ToBitMask(settings.ColliderLayer),
                    CollidesWith = NavUtil.ToBitMask(settings.SurfaceLayer),
                }
                    );

                unsafe
                {
                    var castInput = new ColliderCastInput()
                    {
                        Collider    = (Collider *)collider.GetUnsafePtr(),
                        Orientation = quaternion.identity
                    };

                    if (!physicsWorld.CastCollider(castInput, out ColliderCastHit hit))
                    {
                        commandBuffer.RemoveComponent <NavDestination>(entityInQueryIndex, entity);    // Ignore invalid destinations.
                        return;
                    }

                    var localDestination = destination.WorldPoint.ToLocal(localToWorldFromEntity[hit.Entity]) + agent.Offset;

                    if (NavUtil.ApproxEquals(localDestination, agent.LocalDestination, destination.Tolerance))
                    {
                        return;
                    }

                    if (destination.Teleport)
                    {
                        commandBuffer.SetComponent <Parent>(entityInQueryIndex, entity, new Parent
                        {
                            Value = hit.Entity
                        });

                        commandBuffer.SetComponent <Translation>(entityInQueryIndex, entity, new Translation
                        {
                            Value = localDestination
                        });

                        commandBuffer.RemoveComponent <NavDestination>(entityInQueryIndex, entity);

                        return;
                    }

                    agent.DestinationSurface = physicsWorld.Bodies[hit.RigidBodyIndex].Entity;
                    agent.LocalDestination   = localDestination;
                    agent.DestinationSeconds = elapsedSeconds;

                    commandBuffer.AddComponent <NavPlanning>(entityInQueryIndex, entity);
                }
            })
            .WithName("CreateDestinationJob")
            .ScheduleParallel();

            barrier.AddJobHandleForProducer(Dependency);
            buildPhysicsWorld.AddInputDependencyToComplete(Dependency);
        }
        protected override JobHandle OnUpdate(JobHandle inputDeps)
        {
            var commandBuffer          = barrier.CreateCommandBuffer().ToConcurrent();
            var elapsedSeconds         = (float)Time.ElapsedTime;
            var deltaSeconds           = Time.DeltaTime;
            var physicsWorld           = buildPhysicsWorldSystem.PhysicsWorld;
            var pathBufferFromEntity   = GetBufferFromEntity <NavPathBufferElement>(true);
            var localToWorldFromEntity = GetComponentDataFromEntity <LocalToWorld>(true);
            var avoidantFromEntity     = GetComponentDataFromEntity <NavAvoidant>(true);

            var walkJob = Entities
                          .WithNone <NavPlanning, NavJumping>()
                          .WithAll <NavLerping, Parent, LocalToParent>()
                          .WithReadOnly(pathBufferFromEntity)
                          .WithReadOnly(localToWorldFromEntity)
                          .WithReadOnly(avoidantFromEntity)
                          .ForEach((Entity entity, int entityInQueryIndex, ref NavAgent agent, ref Translation translation, ref Rotation rotation) =>
            {
                var pathBuffer = pathBufferFromEntity[entity];

                if (pathBuffer.Length == 0)
                {
                    return;
                }

                if (agent.PathBufferIndex >= pathBuffer.Length)
                {
                    --agent.PathBufferIndex;
                    return;
                }

                var destination = pathBuffer[agent.PathBufferIndex].Value;

                if (!agent.DestinationSurface.Equals(Entity.Null))
                {
                    agent.WorldDestination = NavUtil.MultiplyPoint3x4(
                        localToWorldFromEntity[agent.DestinationSurface].Value,
                        agent.LocalDestination
                        );
                }

                var avoidant         = avoidantFromEntity.Exists(entity);
                var worldDestination = avoidant ? agent.AvoidanceDestination : agent.WorldDestination;
                var worldPosition    = localToWorldFromEntity[entity].Position;

                if (
                    NavUtil.ApproxEquals(translation.Value, destination, 1) &&
                    ++agent.PathBufferIndex > pathBuffer.Length - 1
                    )
                {
                    if (!avoidant)
                    {
                        var rayInput = new RaycastInput
                        {
                            Start  = translation.Value,
                            End    = math.forward(rotation.Value) * NavConstants.OBSTACLE_RAYCAST_DISTANCE_MAX,
                            Filter = CollisionFilter.Default
                        };

                        if (
                            !physicsWorld.CastRay(rayInput, out RaycastHit hit) &&
                            !NavUtil.ApproxEquals(worldPosition, worldDestination, 1)
                            )
                        {
                            agent.JumpSeconds = elapsedSeconds;
                            commandBuffer.AddComponent <NavJumping>(entityInQueryIndex, entity);
                            commandBuffer.AddComponent <NavPlanning>(entityInQueryIndex, entity);
                            return;
                        }
                    }

                    commandBuffer.RemoveComponent <NavLerping>(entityInQueryIndex, entity);

                    if (avoidant)
                    {
                        commandBuffer.RemoveComponent <NavAvoidant>(entityInQueryIndex, entity);
                        commandBuffer.AddComponent <NavPlanning>(entityInQueryIndex, entity);
                    }

                    agent.PathBufferIndex = 0;

                    return;
                }

                var lookAt     = worldDestination;
                lookAt.y       = worldPosition.y;
                rotation.Value = quaternion.LookRotationSafe(lookAt - worldPosition, math.up());

                translation.Value = Vector3.MoveTowards(translation.Value, destination, agent.TranslationSpeed * deltaSeconds);

                agent.LastDestination = agent.WorldDestination;
            })
                          .WithName("NavWalkJob")
                          .Schedule(
                JobHandle.CombineDependencies(
                    inputDeps,
                    buildPhysicsWorldSystem.FinalJobHandle
                    )
                );

            var jumpBufferFromEntity = GetBufferFromEntity <NavJumpBufferElement>();
            var fallingFromEntity    = GetComponentDataFromEntity <NavFalling>();

            var artificialGravityJob = Entities
                                       .WithAny <NavFalling, NavJumping>()
                                       .WithAll <Parent, LocalToParent>()
                                       .WithReadOnly(fallingFromEntity)
                                       .WithNativeDisableParallelForRestriction(jumpBufferFromEntity)
                                       .ForEach((Entity entity, int entityInQueryIndex, ref NavAgent agent, ref Translation translation) =>
            {
                commandBuffer.AddComponent <NavPlanning>(entityInQueryIndex, entity);    // For sticking the landing.

                var jumpBuffer = jumpBufferFromEntity[entity];

                if (jumpBuffer.Length == 0 && !fallingFromEntity.Exists(entity))
                {
                    return;
                }

                var velocity    = Vector3.Distance(translation.Value, agent.WorldDestination) / (math.sin(2 * math.radians(agent.JumpDegrees)) / agent.JumpGravity);
                var yVelocity   = math.sqrt(velocity) * math.sin(math.radians(agent.JumpDegrees));
                var destination = translation.Value + math.up() * float.NegativeInfinity;

                if (!fallingFromEntity.Exists(entity))
                {
                    var xVelocity     = math.sqrt(velocity) * math.cos(math.radians(agent.JumpDegrees));
                    destination       = jumpBuffer[0].Value;
                    translation.Value = Vector3.MoveTowards(translation.Value, destination, xVelocity * deltaSeconds);
                }

                translation.Value.y += (yVelocity - (elapsedSeconds - agent.JumpSeconds) * agent.JumpGravity) * deltaSeconds;

                if (elapsedSeconds - agent.JumpSeconds >= NavConstants.JUMP_SECONDS_MAX)
                {
                    commandBuffer.RemoveComponent <NavJumping>(entityInQueryIndex, entity);
                    commandBuffer.AddComponent <NavFalling>(entityInQueryIndex, entity);
                }

                if (!NavUtil.ApproxEquals(translation.Value, destination, 1))
                {
                    return;
                }

                commandBuffer.AddComponent <NavNeedsSurface>(entityInQueryIndex, entity);
                commandBuffer.RemoveComponent <NavJumping>(entityInQueryIndex, entity);

                jumpBuffer.Clear();
            })
                                       .WithName("NavArtificialGravityJob")
                                       .Schedule(walkJob);

            barrier.AddJobHandleForProducer(artificialGravityJob);

            return(artificialGravityJob);
        }