It appears that ComputePenetration doesn’t return the correct decollision direction between a CapsuleCollider and a convex MeshCollider.
Notice the white debug line representing the decollision direction. Here I move a convex cube MeshCollider to make it intersect with a sphere and a capsule. With the sphere, the decoll direction is accurate and moves when moving the cube, but with the capsule, the direction seems to “snap” at 90 degree angles. The problem doesn’t happen if I switch my cube MeshCollider to non-convex
Unity 2202.1.2
Code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ComputePenetrationCapsuleTester : MonoBehaviour
{
public CapsuleCollider CapsuleCollider;
private Transform _transform;
private Collider[] tmpColliders = new Collider[300];
public Vector3 CharacterTransformToCapsuleCenter { get; private set; }
public Vector3 CharacterTransformToCapsuleBottom { get; private set; }
public Vector3 CharacterTransformToCapsuleTop { get; private set; }
public Vector3 CharacterTransformToCapsuleBottomHemi { get; private set; }
public Vector3 CharacterTransformToCapsuleTopHemi { get; private set; }
public Vector3 CapsuleDirection
{
get
{
switch(CapsuleCollider.direction)
{
case 0:
return _transform.right;
case 1:
return _transform.up;
case 2:
return _transform.forward;
}
return _transform.up;
}
}
void Awake()
{
_transform = this.transform;
SetCapsuleDimensions(CapsuleCollider.radius, CapsuleCollider.height, CapsuleCollider.center.y);
}
public void SetCapsuleDimensions(float radius, float height, float yOffset)
{
height = Mathf.Max(height, (radius * 2f) + 0.01f); // Safety to prevent invalid capsule geometries
CapsuleCollider.radius = radius;
CapsuleCollider.height = Mathf.Clamp(height, CapsuleCollider.radius * 2f, height);
CapsuleCollider.center = new Vector3(0f, yOffset, 0f);
Vector3 capsuleDirection = CapsuleDirection;
float halfHeight = CapsuleCollider.height * 0.5f;
CharacterTransformToCapsuleCenter = CapsuleCollider.center;
CharacterTransformToCapsuleBottom = CapsuleCollider.center + (-capsuleDirection * halfHeight);
CharacterTransformToCapsuleTop = CapsuleCollider.center + (capsuleDirection * halfHeight);
CharacterTransformToCapsuleBottomHemi = CapsuleCollider.center + (-capsuleDirection * halfHeight) + (capsuleDirection * CapsuleCollider.radius);
CharacterTransformToCapsuleTopHemi = CapsuleCollider.center + (capsuleDirection * halfHeight) + (-capsuleDirection * CapsuleCollider.radius);
}
void Update()
{
SetCapsuleDimensions(CapsuleCollider.radius, CapsuleCollider.height, CapsuleCollider.center.y);
int nbHits = Physics.OverlapCapsuleNonAlloc(
_transform.position + (_transform.rotation * CharacterTransformToCapsuleBottomHemi),
_transform.position + (_transform.rotation * CharacterTransformToCapsuleTopHemi),
CapsuleCollider.radius,
tmpColliders,
-1,
QueryTriggerInteraction.Collide);
for (int i = 0; i < nbHits; i++)
{
Collider otherCollider = tmpColliders[i];
if (otherCollider != CapsuleCollider)
{
if (Physics.ComputePenetration(
CapsuleCollider, _transform.position, _transform.rotation,
otherCollider, otherCollider.transform.position, otherCollider.transform.rotation,
out Vector3 decollideDirection, out float overlapDistance))
{
Debug.DrawRay(_transform.position, decollideDirection * overlapDistance);
}
else
{
Debug.Log("ComputePenetration failed");
}
}
}
}
private void OnDrawGizmos()
{
_transform = this.transform;
Gizmos.color = Color.yellow;
Gizmos.DrawSphere(_transform.position + (_transform.rotation * CharacterTransformToCapsuleBottomHemi), CapsuleCollider.radius);
Gizmos.DrawSphere(_transform.position + (_transform.rotation * CharacterTransformToCapsuleTopHemi), CapsuleCollider.radius);
}
}