Sphere Cast not detecting less than radius

Hi, so I’m trying to make a combat lock on system, where the player uses the right joystick, points in the rough direction of an enemy, and then the player can lock on to them.

The video below demonstrates the current state, along with the problem I face:

The black gizmo shows the right stick input, while the blue gizmo line shows the target that it’s locked on to currently. (Red gizmo is irrelevant as it shows current player input).

The problem is, when the player is close, the SphereCast does not detect the enemy, as demonstrated in the video.

This is my code:

           lockInput = new Vector3(aimInput.x, 0, aimInput.y);

            var matrix = Matrix4x4.Rotate(Quaternion.Euler(0, 45, 0));
            skewedLock = matrix.MultiplyPoint3x4(lockInput);

            Vector3 lockHitPoint = skewedLock.normalized;

            RaycastHit info;
            if (Physics.SphereCast(lockDetectionOffset.position, lockDetectionRadius, lockHitPoint, out info, maximumLockRange, enemyLayer))
            {
                if (info.collider.CompareTag("Enemy"))
                {
                    lockOnTarget = info.collider.gameObject.transform;
                }
            }

Looking into it further, I see this on the unity documentations, which seems to somewhat explain my error
SphereCast will not detect colliders for which the sphere overlaps the collider.

What can I do to remedy this? Are there any alternatives? Most videos I’ve seen regarding such systems say to use SphereCast(mainly Mix&Jam’s one).

There are ‘Overlap’ versions of all the different ray-cast shapes that can be used to check if something exists within an specific area. For example, OverlapSphere: https://docs.unity3d.com/ScriptReference/Physics.OverlapSphere.html

And you don’t need to tie yourself to one check, too. If necessary do a number of different checks. Check if something is right next to the player with an Overlay check. If not, check further away with a Cast check. And then maybe more!

1 Like
1 Like

If you’d prefer to prioritize angle instead of distance, you could use Physics.SphereCastAll or Physics.SphereCastNonAlloc then manually check the angle between the spherecast ray and a ray from the spherecast start to an appropriate center for the object, like Transform.position, and select the object with the smallest such angle. Those methods also include objects that intersect with the starting sphere, so no need for a second query with this.

1 Like

You can use Vector3.Dot to check if an enemy is in a general direction and then do a ray cast to check if there’s no obstacles between the player and enemy.

    foreach (Transform enemy in enemies)
        if (Vector3.Dot(lockInput,(enemy.position-transform.position).normalized)>0.5f) // enemy in the general input direction?
            if (Physics.Linecast(transform.position,enemy.position, out RaycastHit hit))
                if (hit.transform==enemy) // enemy visible?
                    lockOnTarget=enemy;
2 Likes

Thanks guys, ended up using a hybrid of the answers provided here! I’ll upload the code when I’m able for anyone else who struggles.