public static bool ClosestPointOnSurface(Collider collider, Vector3 to, float radius, out Vector3 closestPointOnSurface) { if (collider is BoxCollider) { closestPointOnSurface = SuperCollider.ClosestPointOnSurface((BoxCollider)collider, to); return(true); } else if (collider is SphereCollider) { closestPointOnSurface = SuperCollider.ClosestPointOnSurface((SphereCollider)collider, to); return(true); } else if (collider is CapsuleCollider) { closestPointOnSurface = SuperCollider.ClosestPointOnSurface((CapsuleCollider)collider, to); return(true); } else if (collider is MeshCollider) { BSPTree bsp = collider.GetComponent <BSPTree>(); if (bsp != null) { closestPointOnSurface = bsp.ClosestPointOn(to, radius); return(true); } BruteForceMesh bfm = collider.GetComponent <BruteForceMesh>(); if (bfm != null) { closestPointOnSurface = bfm.ClosestPointOn(to); return(true); } } else if (collider is TerrainCollider) { closestPointOnSurface = SuperCollider.ClosestPointOnSurface((TerrainCollider)collider, to, radius, false); return(true); } Debug.LogError(string.Format("{0} does not have an implementation for ClosestPointOnSurface; GameObject.Name='{1}'", collider.GetType(), collider.gameObject.name)); closestPointOnSurface = Vector3.zero; return(false); }
/// <summary> /// Check if any of the CollisionSpheres are colliding with any walkable objects in the world. /// If they are, apply a proper pushback and retrieve the collision data /// </summary> void RecursivePushback(int depth, int maxDepth) { PushIgnoredColliders(); bool contact = false; foreach (var sphere in spheres) { foreach (Collider col in Physics.OverlapSphere((SpherePosition(sphere)), radius, Walkable, triggerInteraction)) { Vector3 position = SpherePosition(sphere); Vector3 contactPoint; bool contactPointSuccess = SuperCollider.ClosestPointOnSurface(col, position, radius, out contactPoint); if (!contactPointSuccess) { return; } if (debugPushbackMesssages) { DebugDraw.DrawMarker(contactPoint, 2.0f, Color.cyan, 0.0f, false); } Vector3 v = contactPoint - position; if (v != Vector3.zero) { // Cache the collider's layer so that we can cast against it int layer = col.gameObject.layer; col.gameObject.layer = TemporaryLayerIndex; // Check which side of the normal we are on bool facingNormal = Physics.SphereCast(new Ray(position, v.normalized), TinyTolerance, v.magnitude + TinyTolerance, 1 << TemporaryLayerIndex); col.gameObject.layer = layer; // Orient and scale our vector based on which side of the normal we are situated if (facingNormal) { if (Vector3.Distance(position, contactPoint) < radius) { v = v.normalized * (radius - v.magnitude) * -1; } else { // A previously resolved collision has had a side effect that moved us outside this collider continue; } } else { v = v.normalized * (radius + v.magnitude); } contact = true; transform.position += v; col.gameObject.layer = TemporaryLayerIndex; // Retrieve the surface normal of the collided point RaycastHit normalHit; Physics.SphereCast(new Ray(position + v, contactPoint - (position + v)), TinyTolerance, out normalHit, 1 << TemporaryLayerIndex); col.gameObject.layer = layer; SuperCollisionType superColType = col.gameObject.GetComponent <SuperCollisionType>(); if (superColType == null) { superColType = defaultCollisionType; } // Our collision affected the collider; add it to the collision data var collision = new SuperCollision() { collisionSphere = sphere, superCollisionType = superColType, gameObject = col.gameObject, point = contactPoint, normal = normalHit.normal }; collisionData.Add(collision); } } } PopIgnoredColliders(); if (depth < maxDepth && contact) { RecursivePushback(depth + 1, maxDepth); } }