Raycasts not behaving correctly

I’ve been porting over a script to ecs that handles some rudimentary pathfinding. Everything worked great until I switched to the new Unity physics package. The ray-casts are firing and colliding; however, they’re behaving unexpectedly. (i.e. hitting when they shouldn’t be, and not hitting when they should).

  1. I’d typically do a Debug.DrawRay to ensure they’re aimed how I want. Is there an equivalent I could use?

  2. Here’s a trimmed down version of the code, there may be an issue I’m not picking up on.

public class PathfindingSystem : JobComponentSystem {

    BuildPhysicsWorld physicsWorld;

    protected override void OnCreate() {
        physicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
    }

    [BurstCompile]
    struct PathfindingSystemJob : IJobForEach<Translation, PathfindingComponent, Rotation, LocalToWorld> {
        [ReadOnly] public float deltaTime;
        [ReadOnly] public CollisionWorld collisionWorld;

        public void Execute(ref Translation translation, ref PathfindingComponent pathfindingComponent, ref Rotation rotation, ref LocalToWorld localToWorld) {

            RaycastInput up = new RaycastInput() {
                Start = translation.Value + localToWorld.Up * pathfindingComponent.rayCastOffset,
                End = localToWorld.Forward * pathfindingComponent.detectionDistance,
                Filter = CollisionFilter.Default
            };

            var offset = Vector3.zero;

            // Emit raycasts
            if (collisionWorld.CastRay(up)) {
                offset -= Vector3.up;
                //Debug.Log("hit up");
            }

            if (offset != Vector3.zero) {
                // Avoid obstacle
            } else {
                // No obstacle, turn towards target
            }
        }
    }

    protected override JobHandle OnUpdate(JobHandle inputDependencies) {
        var job = new PathfindingSystemJob();
        job.deltaTime = Time.deltaTime;
        job.collisionWorld = physicsWorld.PhysicsWorld.CollisionWorld;

        return job.Schedule(this, inputDependencies);
    }
}

Looks like your RaycastInput.End is a direction not an endpoint.

@Rory_Havok right. Why you using Direction in End if it’s even named as point :slight_smile: And if you look at sources you’ll see it converts it to Direction, and comments describes that Start nad End is points :slight_smile:

// The input to ray cast queries consists of
    // the Start & End positions of a line segment,
    // and a CollisionFilter used to cull potential hits
    public struct RaycastInput
    {
        public float3 Start
        {
            get => Ray.Origin;
            set
            {
                float3 end = Ray.Origin + Ray.Displacement;
                Ray.Origin = value;
                Ray.Displacement = end - value;
            }
        }
        public float3 End
        {
            get => Ray.Origin + Ray.Displacement;
            set => Ray.Displacement = value - Ray.Origin;
        }

        public CollisionFilter Filter;

        internal Ray Ray;
    }

I knew I was missing something! Thanks for the help.

Sharing is caring. Here’s where I ended up for anyone interested.

using Unity.Burst;
using Unity.Entities;
using Unity.Jobs;
using UnityEngine;
using Unity.Transforms;
using Unity.Mathematics;
using Unity.Physics.Systems;
using Unity.Collections;
using Unity.Physics;

public class PathfindingSystem : JobComponentSystem {

    BuildPhysicsWorld physicsWorld;

    protected override void OnCreate() {
        physicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
    }

    [BurstCompile]
    struct PathfindingSystemJob : IJobForEach<Translation, PathfindingComponent, Rotation, LocalToWorld> {
        [ReadOnly] public float deltaTime;
        [ReadOnly] public float3 target;
        [ReadOnly] public CollisionWorld collisionWorld;

        public void Execute(ref Translation translation, ref PathfindingComponent pathfindingComponent, ref Rotation rotation, ref LocalToWorld localToWorld) {

            Vector3 end = math.forward(rotation.Value) * pathfindingComponent.detectionDistance;

            NativeArray<PathfindingRay> rays = new NativeArray<PathfindingRay>(4, Allocator.Temp);
            rays[0] = new PathfindingRay(translation.Value - localToWorld.Right * pathfindingComponent.rayCastOffset, end, Vector3.right);    // Left
            rays[1] = new PathfindingRay(translation.Value + localToWorld.Right * pathfindingComponent.rayCastOffset, end, Vector3.left);    // Right
            rays[2] = new PathfindingRay(translation.Value + localToWorld.Up * pathfindingComponent.rayCastOffset, end, Vector3.down);        // Up
            rays[3] = new PathfindingRay(translation.Value - localToWorld.Up * pathfindingComponent.rayCastOffset, end, Vector3.up);        // Down

            var offset = Vector3.zero;

            // Emit raycasts
            for (var i = 0; i < rays.Length; i++) {
                if (collisionWorld.CastRay(rays[i].ray)) {
                    // Apply counter adjustment to avoid
                    offset += rays[i].counterAdjustment;
                    break;
                }
            }

            if (offset != Vector3.zero) {
                // Avoid obstacle
                var newVal = math.mul(math.normalize(rotation.Value), quaternion.EulerXYZ(offset));
                rotation.Value = Quaternion.Slerp(rotation.Value, newVal, pathfindingComponent.rotationalDamp * deltaTime);
            } else {
                // No obstacle, turn towards target
                Vector3 pos = target - translation.Value;
                Quaternion newRotation = Quaternion.LookRotation(pos);
                rotation.Value = Quaternion.Slerp(rotation.Value, newRotation, pathfindingComponent.rotationalDamp * deltaTime);
            }

            // Move forward
            translation.Value += math.forward(rotation.Value) * deltaTime * pathfindingComponent.movementSpeed;

            rays.Dispose();
        }
    }

    protected override JobHandle OnUpdate(JobHandle inputDependencies) {
        var job = new PathfindingSystemJob();
        job.deltaTime = Time.deltaTime;
        job.collisionWorld = physicsWorld.PhysicsWorld.CollisionWorld;

        var targets = GetEntityQuery(typeof(TargetComponent), typeof(Translation)).ToComponentDataArray<Translation>(Allocator.Persistent);

        if(targets.Length > 0) {
            job.target = targets[0].Value;
        }

        targets.Dispose();

        return job.Schedule(this, inputDependencies);
    }
}

public struct PathfindingRay {
    public RaycastInput ray;
    public Vector3 counterAdjustment;

    public PathfindingRay(Vector3 start, Vector3 end, Vector3 counterAdjustment) {
        ray = new RaycastInput() {
            Start = start,
            End = start + end,
            Filter = CollisionFilter.Default
        };
        this.counterAdjustment = counterAdjustment;
    }
}