private bool FootStrike(out GameObject struckObject) { struckObject = null; float radius = controller.radius; Vector3 o = controller.OffsetPosition(controller.feet.Offset); Collider[] colliders = Physics.OverlapSphere(o, radius, EnemyLayerMask); foreach (var col in colliders) { var machine = col.gameObject.GetComponent <GoombaMachine>(); if (machine != null) { if (!machine.Alive) { continue; } } Vector3 closestPoint = SuperCollider.ClosestPointOnSurface(col, o, radius); if (SuperMath.PointAbovePlane(controller.down, o, closestPoint)) { struckObject = col.gameObject; return(true); } } return(false); }
bool PushBack() { bool isPushedBack = false; for (int i = collisionSpheres.Count - 1; i >= 0; i--) { overlappingColliders = Physics.OverlapSphere(SpherePosition(collisionSpheres[i]), radius, LayersManager.colLayers); for (int j = 0; j < overlappingColliders.Length; j++) { if (overlappingColliders[j] != myCollider) { isPushedBack = true; Vector3 position = SpherePosition(collisionSpheres[i]); // The position of the sphere in world coordinates Vector3 contactPoint = SuperCollider.ClosestPointOnSurface(overlappingColliders[j], position, radius); // The closest point on the collider, named contact point if (contactPoint != Vector3.zero) // Contact point was found { Vector3 v = contactPoint - position; //The direction from the position of the sphere to the contact point if (v != Vector3.zero) { // Cache the collider's layer so that we can cast against it int layer = overlappingColliders[j].gameObject.layer; overlappingColliders[j].gameObject.layer = _temporaryLayerIndex; // Check which side of the normal we are on bool facingNormal = Physics.Raycast(new Ray(position, v.normalized), v.magnitude + _tinyTolerance, 1 << _temporaryLayerIndex); //Set col layer back to its previous layer overlappingColliders[j].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; //The distance needed to move the controller in order to push it out of the collider } else { // A previously resolved collision has had a side effect that moved us outside this collider continue; } } else { v = v.normalized * (radius + v.magnitude); //The distance needed to move the controller in order to push it out of the collider } transform.position += v; //Push back controller overlappingColliders[j].gameObject.layer = layer; } } } } } return(isPushedBack); }
public static Vector3 ClosestPointOnSurface(Collider collider, Vector3 to, float radius) { if (collider is BoxCollider) { return(SuperCollider.ClosestPointOnSurface((BoxCollider)collider, to)); } else if (collider is SphereCollider) { return(SuperCollider.ClosestPointOnSurface((SphereCollider)collider, to)); } else if (collider is CapsuleCollider) { return(SuperCollider.ClosestPointOnSurface((CapsuleCollider)collider, to)); } else if (collider is MeshCollider) { BSPTree bsp = collider.GetComponent <BSPTree>(); if (bsp != null) { return(bsp.ClosestPointOn(to, radius)); } BruteForceMesh bfm = collider.GetComponent <BruteForceMesh>(); if (bfm != null) { return(bfm.ClosestPointOn(to)); } } return(Vector3.zero); }
// Main collision check public GameObject collides(SuperCollider collider, float x, float y, float width, float height) { if (x + width / 2 > collider.transform.position.x - collider.width / 2 && y + height / 2 > collider.transform.position.y - collider.height / 2 && x - width / 2 <= collider.transform.position.x + collider.width / 2 && y - height / 2 <= collider.transform.position.y + collider.height / 2) { return(collider.gameObject); } return(null); }
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 bspTree = collider.GetComponent <BSPTree>(); if (bspTree != null) { closestPointOnSurface = bspTree.ClosestPointOn(to, radius); return(true); } 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> /// Checks if there exists a wall in front of us as well as a flat surface to finish the climb on /// </summary> private bool CanGrabLedge(out Vector3 hitPosition, out GameObject grabbedObject) { hitPosition = Vector3.zero; grabbedObject = null; Vector3 o = controller.OffsetPosition(controller.head.Offset); Collider[] colliders = Physics.OverlapSphere(o, controller.radius + 0.2f, controller.Walkable); if (colliders.Length > 0) { foreach (var col in colliders) { SuperCollisionType type = col.GetComponent <SuperCollisionType>(); Vector3 closestPoint = SuperCollider.ClosestPointOnSurface(col, o, controller.radius); RaycastHit hit; col.Raycast(new Ray(o, closestPoint - o), out hit, Mathf.Infinity); if (Vector3.Angle(hit.normal, controller.up) < type.StandAngle) { continue; } if (Vector3.Angle(-hit.normal, lookDirection) < 60.0f) { Vector3 topOfHead = o + controller.up * controller.radius; Vector3 direction = Math3d.ProjectVectorOnPlane(controller.up, closestPoint - o); Vector3 rayOrigin = topOfHead + Math3d.AddVectorLength(direction, 0.02f); col.Raycast(new Ray(rayOrigin, controller.down), out hit, 0.5f); if (Vector3.Angle(hit.normal, controller.up) < 20.0f) { hitPosition = Math3d.ProjectPointOnPlane(controller.up, hit.point, topOfHead + direction); grabbedObject = col.gameObject; return(true); } } } } return(false); }
public static Vector3 ClosestPointOnSurface(Collider collider, Vector3 to, float radius) { if (collider is BoxCollider) { return(SuperCollider.ClosestPointOnSurface((BoxCollider)collider, to)); } else if (collider is SphereCollider) { return(SuperCollider.ClosestPointOnSurface((SphereCollider)collider, to)); } else if (collider is CapsuleCollider) { return(SuperCollider.ClosestPointOnSurface((CapsuleCollider)collider, to)); } else if (collider is MeshCollider) { RPGMesh rpgMesh = collider.GetComponent <RPGMesh>(); if (rpgMesh != null) { return(rpgMesh.ClosestPointOn(to, radius, false, false)); } BSPTree bsp = collider.GetComponent <BSPTree>(); if (bsp != null) { return(bsp.ClosestPointOn(to, radius)); } BruteForceMesh bfm = collider.GetComponent <BruteForceMesh>(); if (bfm != null) { return(bfm.ClosestPointOn(to)); } } else if (collider is TerrainCollider) { return(SuperCollider.ClosestPointOnSurface((TerrainCollider)collider, to, radius, false)); } Debug.LogError(string.Format("{0} does not have an implementation for ClosestPointOnSurface", collider.GetType())); return(Vector3.zero); }
public static Vector3 ClosestPointOnSurface(Collider collider, Vector3 to, float radius) { if (collider is BoxCollider) { return(SuperCollider.ClosestPointOnSurface((BoxCollider)collider, to)); } else if (collider is SphereCollider) { return(SuperCollider.ClosestPointOnSurface((SphereCollider)collider, to)); } else if (collider is MeshCollider) { RPGMesh rpgMesh = collider.GetComponent <RPGMesh>(); if (rpgMesh != null) { return(rpgMesh.ClosestPointOn(to, radius, false, false)); } } return(Vector3.zero); }
private bool Strike(out GameObject struckObject, Vector3 origin, Vector3 offset, float radius = 0) { struckObject = null; if (radius == 0) { radius = controller.radius; } // Vector3 o = transform.position + (controller.Up * controller.Height * 0.5f); Vector3 center = origin + offset; Collider[] colliders = Physics.OverlapSphere(center, radius, controller.Walkable | EnemyLayerMask); foreach (var col in colliders) { SuperCollisionType type = col.GetComponent <SuperCollisionType>(); Vector3 closestPoint = SuperCollider.ClosestPointOnSurface(col, center, radius); RaycastHit hit; col.Raycast(new Ray(origin, closestPoint - origin), out hit, Mathf.Infinity); if (type != null && Vector3.Angle(hit.normal, controller.up) < type.StandAngle) { continue; } if (Vector3.Angle(-hit.normal, lookDirection) < 60.0f) { struckObject = col.gameObject; return(true); } } 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); } }
private void RecursivePushback(int depth) { PushIgnoredColliders(); bool hasContact = false; foreach (CollisionSphere collisionSphere in collisionSpheres) { Collider[] colliders = Physics.OverlapSphere( collisionSphere.Position, collisionSphere.Radius, settings.walkableLayerMask ); foreach (Collider collider in colliders) { if (collider.isTrigger) { continue; } Vector3 contactPoint = SuperCollider.ClosestPointOnSurface( collider, collisionSphere.Position, collisionSphere.Radius ); if (contactPoint != Vector3.zero) { if (settings.Debug.showPushbackMessages) { DebugDraw.DrawMarker(contactPoint, 2.0f, Color.cyan, 0.0f, false); } Vector3 contactDirection = contactPoint - collisionSphere.Position; if (contactDirection != Vector3.zero) { int cachedLayer = collider.gameObject.layer; collider.gameObject.layer = ignoreCollisionLayer; Ray ray = new Ray(collisionSphere.Position, contactDirection.normalized); bool facingNormal = Physics.SphereCast( ray, settings.epsilon, contactDirection.magnitude + settings.epsilon, 1 << ignoreCollisionLayer ); collider.gameObject.layer = cachedLayer; if (facingNormal) { if (Vector3.Distance(collisionSphere.Position, contactPoint) < collisionSphere.Radius) { contactDirection = -contactDirection.normalized * (collisionSphere.Radius - contactDirection.magnitude); } else { // A previously resolved collision has had a // side effect that moved us outside the // collider continue; } } else { contactDirection = contactDirection.normalized * (collisionSphere.Radius + contactDirection.magnitude); } hasContact = true; playerView.Position += contactDirection; collider.gameObject.layer = ignoreCollisionLayer; RaycastHit normalHit; Ray normalRay = new Ray( collisionSphere.Position + contactDirection, contactPoint - collisionSphere.Position - contactDirection ); Physics.SphereCast( normalRay, settings.epsilon, out normalHit, 1 << ignoreCollisionLayer ); collider.gameObject.layer = cachedLayer; Collidable collidable = collider.gameObject.GetComponent <Collidable>(); if (collidable == null) { collidable = defaultCollidable; } Collision collision = new Collision() { collisionSphere = collisionSphere, collidable = collidable, gameObject = collider.gameObject, point = contactPoint, normal = normalHit.normal }; collisions.Add(collision); } } } } PopIgnoredColliders(); if (depth < settings.maxPushbackIterations && hasContact) { RecursivePushback(depth + 1); } }
// This function makes sure we don't phase through other colliders. (Since character controller doesn't provide this functionality lmao). // I copied it from https://github.com/IronWarrior/SuperCharacterController // I changed it a bit, but SuperCharacterController is under the MIT license, meaning we can't use it without making our game also under the MIT license. // so TODO: Change this enough that we don't have to use the MIT license if we don't want to. private void RecursivePushback(int depth, int maxDepth) { bool contact = false; foreach (var sphere in spheres) { foreach (Collider col in Physics.OverlapSphere(SpherePosition(sphere), sphere.radius, layerMask, QueryTriggerInteraction.Ignore)) { if (col.isTrigger) { continue; } Vector3 position = SpherePosition(sphere); Vector3 contactPoint; bool contactPointSuccess = SuperCollider.ClosestPointOnSurface(col, position, radius, out contactPoint); if (!contactPointSuccess) { return; } 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, QueryTriggerInteraction.Ignore); 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; HandleCollision(col.gameObject, normalHit.normal, normalHit.point); } } } if (depth < maxDepth && contact) { RecursivePushback(depth + 1, maxDepth); } }