Impact position for sphere overlap collision is at objects origin

Zaddo

Active member
I have a problem when using Sphere Overlap Collision for body melee. It appears that the position of the impact is calculated as the origin of the object impacted? As best I can tell, this is what the SphereCastNonAlloc returns. This post seems to indicate same: https://answers.unity.com/questions/1471278/physicsspherecastnonalloc-returning-incorrect-info.html

This isn't a big issue for small objects. But for large objects like a building, the impact location can be some distance away. So when audio is played in the collision module, the volume isn't correct due to the distance.

I might be missing something, does this sound correct? If this is wrong, what might the issue be?

If this is correct, then the only solution I can think of, is to do a secondary check to locate the point of impact using a raycast and update the m_collisionHit array with this detail?

1682372076191.png

 
Last edited:
If the point is 0 then the hit point is contained within the cast. I'm tagging @Sangemdoko to look into this further. If the distance is 0 then Physics.ComputePenetration should be used.
 
Thank you for bringing this to my attention I was unaware of this.

@Justin I can't use Physic.ComputePenetration because that requires 2 colliders.
In this use case we have single collider and a sphere cast.

I came up with this "solution":

Code:
/// <summary>
/// Do a cast when hitting a collider to get the raycast hit data.
/// </summary>
/// <param name="dataStream">The data stream.</param>
/// <param name="hitboxIndex">The index of the hitbox that caused the collision.</param>
/// <param name="hitCollider">The collider that was hit.</param>
/// <returns>A list of raycast hits.</returns>
protected override ListSlice<RaycastHit> DoCollisionCast(MeleeUseDataStream dataStream, int hitboxIndex, Collider hitCollider)
{
    var hitCount = 0;
    Vector3 direction;
    float distance;
    var characterTransform = Character.transform;
    var center = characterTransform.TransformPoint(m_SphereCenter);
    // A RaycastHit should be retrieved based off of the collision.
    if (((hitCollider is BoxCollider) || (hitCollider is SphereCollider) || (hitCollider is CapsuleCollider) ||
         ((hitCollider is MeshCollider) && (hitCollider as MeshCollider).convex))) {
        var hitColliderTransform = hitCollider.transform;
        
        direction = characterTransform.position - center;
        distance = direction.magnitude;
        direction = direction.normalized;
        
        var offset = direction * (distance + CharacterLocomotion.ColliderSpacing * 2);
        var closestPoint = Physics.ClosestPoint(center + offset, hitCollider, hitColliderTransform.position, hitColliderTransform.rotation);
        hitCount = Physics.SphereCastNonAlloc(closestPoint + offset, CharacterLocomotion.ColliderSpacing, -direction,
            m_CollisionsHit,
            distance + CharacterLocomotion.ColliderSpacing * 2, 1 << hitCollider.gameObject.layer, m_TriggerInteraction);
        // Sphere Cast Non Alloc returns point Vector3.zero when the sphere overlaps, so lets just get the closest point.
        for (int i = 0; i < hitCount; i++) {
            var hit = m_CollisionsHit[i];
            if (hit.point == Vector3.zero) {
                hit.point = closestPoint;
                hit.distance = (closestPoint - (center + offset)).magnitude;
            }
            m_CollisionsHit[i] = hit;
        }
        
    } else {
        // Convert the collider's position to be relative to the character's z location.
        var position = center;
        var localPosition = CharacterTransform.InverseTransformPoint(position);
        distance = localPosition.z + CharacterLocomotion.ColliderSpacing;
        localPosition.z = 0;
        position = CharacterTransform.TransformPoint(localPosition);
        // Perform the raycast from the character's local z position. This will prevent the cast from overlapping the object.
        var rotation = CharacterTransform.rotation;
        var originPoint = MathUtility.TransformPoint(position, rotation, m_SphereCenter);
        hitCount = Physics.SphereCastNonAlloc(originPoint,
            m_SphereRadius, CharacterTransform.forward, m_CollisionsHit, distance, 1 << hitCollider.gameObject.layer,
            m_TriggerInteraction);
        
        // Sphere Cast Non Alloc returns point Vector3.zero when the sphere overlaps, so lets just get the closest point.
        for (int i = 0; i < hitCount; i++) {
            var hit = m_CollisionsHit[i];
            if (hit.point == Vector3.zero) {
                hit.point = originPoint;
                hit.distance = (originPoint - center).magnitude;
            }
            m_CollisionsHit[i] = hit;
        }
    }
    return new ListSlice<RaycastHit>(m_CollisionsHit,0,hitCount);
}

When the collider is convex I can use the closest point. When it is not convex then I have to approximate, which I do by getting the attack point.

Let me know if that helps with your issue
 
Top