0

I have a very basic player in Unity that I coded to collide against objects using Physics.ComputePenetration(...) and it works relatively well. The problem is that I would like more detailed information about where the player hit the collider.

Some things to note:

First of all, the idea of this project is to see if I can replicate the CharacterController's functionality from scratch using ComputePenetration. I am currently trying to replicate the built-in OnControllerColliderHit(...) function, which allows me to get the location of where the hit took place.

Second of all, I am not using a SphereCollider. If I was, then all I would have to do is get the returned direction vector and multiply it by the sphere's radius (along with a couple of other things). But sadly, I am using a CapsuleCollider (maybe there's a way to do the same thing but for a CapsuleCollider, but I have not been able to find one).

What I've tried:

My initial idea was to take the collider that the player hit, hitCollider, and execute this snippet of code:

Vector3 point = hitCollider.ClosestPoint(player.transform.position);

to get the closest point on the hit collider. From there, I could call

Vector3 finalPoint = player.collider.ClosestPoint(point);

on the player to get its closest point to the hit collider's closest point. It's very confusing, but it works pretty well. There's only one drawback, however: I plan on using ProBuilder to do a lot of my level design, and you cannot call ClosestPoint on a MeshCollider that does not have convex set to true, which is a majority of meshes in ProBuilder.

I thought of a couple of other things, like maybe I could invert the direction and then do some (not-so) hacky math to get the contact point, etc. but nothing seemed to work as a full-blown solution.

Closing thoughts:

I'm beginning to think that it's not technically possible. Which is true if you think about it, because when two objects intersect, there's no single point that defines it, it's an entire volume that does so. But getting the average position of that volume would (maybe?) be the solution.

Any ideas?

Thank you in advance!

  • I thought the collision included the extract hit point. – BugFinder Jun 06 '22 at 18:28
  • @BugFinder It doesn't, sadly. Only a direction and a distance needed to depenetrate the colliders – Chris Bannoura Jun 06 '22 at 18:36
  • Really? So what about the contact points that it gives which are the points on the collider. When using oncollision it has the points. Are you not reinventing the wheel? – BugFinder Jun 06 '22 at 19:29
  • Its seems that you are trying to build your own custom physics calculation while still using Unity's out of the box capabilities. Unity already provides this functionality using [collision contacts](https://docs.unity3d.com/ScriptReference/Collision-contacts.html) – Erik Overflow Jun 06 '22 at 19:41
  • @BugFinder I'm trying to, yeah, but OnCollisionEnter is only called by objects that come in contact with Rigidbodies and Rigidbodies themselves. And since Unity is closed source, I can't see how they do it – Chris Bannoura Jun 06 '22 at 19:49
  • @ErikOverflow It's not that simple though. My player doesn't call OnCollisionEnter unless it comes across a Rigidbody. Otherwise, collisions are pretty much ignored. That's why Physics.ComputePenetration is needed to depenetrate the character, because Unity won't do it for me – Chris Bannoura Jun 06 '22 at 19:51
  • Why aren't you just assigning a kinematic RigidBody to your player? The literal purpose of a RigidBody is to enable collision detection. – Erik Overflow Jun 06 '22 at 20:01
  • @ErikOverflow Kinematic Rigidbodies don't detect collision by default. The only way to do that is to click "Enable All Contacts" in the Physics settings, but I don't want that because that would mean that ALL Kinematic Rigidbodies have collisions now – Chris Bannoura Jun 06 '22 at 21:12

1 Answers1

2

For anyone else who was curious, I found the solution. When Physics.ComputePenetration(...) between ourCollider and selectedCollider detects a collision, make sure to de-penetrate FIRST, then do the following:

Call a Physics.CapsuleCast(...), but make sure that point1, point2, and radius are slightly smaller values (maybe 0.001) than the original capsule's size. This is because the CapsuleCast will think that it's intersecting with the selectedCollider and will therefore just go straight through it, which you don't want. Then just make your direction the negative direction that was output from Physics.ComputePenetration(...).

So your code ends up looking like this:

var origin = this.transform.position + this.center;
var offset = new Vector3(0.0f, this.height / 2.0f - this.radius - someTinyValue, 0.0f);
if (Physics.CapsuleCast(
        origin + offset,
        origin - offset,
        this.radius - someTinyValue,
        -direction,
        out var hit))
{
    // Do whatever with the `hit` variable
}