public Ground(RaycastHit hit, RaycastHit nearHit, RaycastHit farHit, RaycastHit secondaryHit, SuperCollisionType superCollisionType, Transform hitTransform) { this.hit = hit; this.nearHit = nearHit; this.farHit = farHit; this.secondaryHit = secondaryHit; this.collisionType = superCollisionType; this.transform = hitTransform; }
void Awake() { collisionData = new List<SuperCollision>(); TemporaryLayerIndex = LayerMask.NameToLayer(TemporaryLayer); ignoredColliders = new List<Collider>(); ignoredColliderStack = new List<IgnoredCollider>(); currentlyClampedTo = null; fixedDeltaTime = 1.0f / fixedUpdatesPerSecond; heightScale = 1.0f; if (ownCollider) IgnoreCollider(ownCollider); foreach (var sphere in spheres) { if (sphere.isFeet) feet = sphere; if (sphere.isHead) head = sphere; } if (feet == null) Debug.LogError("[SuperCharacterController] Feet not found on controller"); if (head == null) Debug.LogError("[SuperCharacterController] Head not found on controller"); if (defaultCollisionType == null) defaultCollisionType = new GameObject("DefaultSuperCollisionType", typeof(SuperCollisionType)).GetComponent<SuperCollisionType>(); currentGround = new SuperGround(Walkable, this); manualUpdateOnly = false; gameObject.SendMessage("SuperStart", SendMessageOptions.DontRequireReceiver); }
/// <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)) { if (col.isTrigger) { continue; } Vector3 position = SpherePosition(sphere); Vector3 contactPoint = SuperCollider.ClosestPointOnSurface(col, position, radius); if (contactPoint != Vector3.zero) { 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); } }
public Ground(RaycastHit hit, RaycastHit nearHit, RaycastHit farHit, SuperCollisionType superCollisionType, Transform hitTransform) { Hit = hit; NearHit = nearHit; FarHit = farHit; CollisionType = superCollisionType; Transform = hitTransform; }
/// <summary> /// Scan the surface below us for ground. Follow up the initial scan with subsequent scans /// designed to test what kind of surface we are standing above and handle different edge cases. /// </summary> /// <param name="origin">Center of the sphere for the initial SphereCast.</param> /// <param name="iter">Debug tool to print out which ProbeGround iteration is being run. (3 are run each frame for the controller)</param> public void ProbeGround(Vector3 origin, int iter) { ResetGrounds(); Vector3 up = controller.up; Vector3 down = -up; Vector3 o = origin + (up * Tolerance); // Reduce our radius by Tolerance squared to avoid failing the SphereCast due to clipping with walls. float smallerRadius = controller.radius - (Tolerance * Tolerance); RaycastHit hit; if (Physics.SphereCast(o, smallerRadius, down, out hit, Mathf.Infinity, walkable, triggerInteraction)) { var superColType = hit.collider.gameObject.GetComponent <SuperCollisionType>(); if (superColType == null) { superColType = defaultCollisionType; } superCollisionType = superColType; transform = hit.transform; // By reducing the initial SphereCast's radius by Tolerance, our casted sphere no longer fits with // our controller's shape. Reconstruct the sphere cast with the proper radius. SimulateSphereCast(hit.normal, out hit); primaryGround = new GroundHit(hit.point, hit.normal, hit.distance); // If we are standing on a perfectly flat surface, we cannot be either on an edge, // On a slope or stepping off a ledge. if (Vector3.Distance(Math3d.ProjectPointOnPlane(controller.up, controller.transform.position, hit.point), controller.transform.position) < TinyTolerance) { return; } // As we are standing on an edge, we need to retrieve the normals of the two // faces on either side of the edge and store them in nearHit and farHit. Vector3 toCenter = Math3d.ProjectVectorOnPlane(up, (controller.transform.position - hit.point).normalized * TinyTolerance); Vector3 awayFromCenter = Quaternion.AngleAxis(-80.0f, Vector3.Cross(toCenter, up)) * -toCenter; Vector3 nearPoint = hit.point + toCenter + (up * TinyTolerance); Vector3 farPoint = hit.point + (awayFromCenter * 3); RaycastHit nearHit; RaycastHit farHit; Physics.Raycast(nearPoint, down, out nearHit, Mathf.Infinity, walkable, triggerInteraction); Physics.Raycast(farPoint, down, out farHit, Mathf.Infinity, walkable, triggerInteraction); nearGround = new GroundHit(nearHit.point, nearHit.normal, nearHit.distance); farGround = new GroundHit(farHit.point, farHit.normal, farHit.distance); // If we are currently standing on ground that should be counted as a wall, // we are likely flush against it on the ground. Retrieve what we are standing on. if (Vector3.Angle(hit.normal, up) > superColType.StandAngle) { // Retrieve a vector pointing down the slope. Vector3 r = Vector3.Cross(hit.normal, down); Vector3 v = Vector3.Cross(r, hit.normal); Vector3 flushOrigin = hit.point + hit.normal * TinyTolerance; RaycastHit flushHit; if (Physics.Raycast(flushOrigin, v, out flushHit, Mathf.Infinity, walkable, triggerInteraction)) { RaycastHit sphereCastHit; if (SimulateSphereCast(flushHit.normal, out sphereCastHit)) { flushGround = new GroundHit(sphereCastHit.point, sphereCastHit.normal, sphereCastHit.distance); } else { // Uh oh. } } } // If we are currently standing on a ledge then the face nearest the center of the // controller should be steep enough to be counted as a wall. Retrieve the ground // it is connected to at it's base, if there exists any. if (Vector3.Angle(nearHit.normal, up) > superColType.StandAngle || nearHit.distance > Tolerance) { SuperCollisionType col = null; if (nearHit.collider != null) { col = nearHit.collider.gameObject.GetComponent <SuperCollisionType>(); } if (col == null) { col = defaultCollisionType; } // We contacted the wall of the ledge, rather than the landing. Raycast down // the wall to retrieve the proper landing. if (Vector3.Angle(nearHit.normal, up) > col.StandAngle) { // Retrieve a vector pointing down the slope. Vector3 r = Vector3.Cross(nearHit.normal, down); Vector3 v = Vector3.Cross(r, nearHit.normal); RaycastHit stepHit; if (Physics.Raycast(nearPoint, v, out stepHit, Mathf.Infinity, walkable, triggerInteraction)) { stepGround = new GroundHit(stepHit.point, stepHit.normal, stepHit.distance); } } else { stepGround = new GroundHit(nearHit.point, nearHit.normal, nearHit.distance); } } } // If the initial SphereCast fails, likely due to the controller clipping a wall, // fallback to a raycast simulated to SphereCast data. else if (Physics.Raycast(o, down, out hit, Mathf.Infinity, walkable, triggerInteraction)) { var superColType = hit.collider.gameObject.GetComponent <SuperCollisionType>(); if (superColType == null) { superColType = defaultCollisionType; } superCollisionType = superColType; transform = hit.transform; RaycastHit sphereCastHit; if (SimulateSphereCast(hit.normal, out sphereCastHit)) { primaryGround = new GroundHit(sphereCastHit.point, sphereCastHit.normal, sphereCastHit.distance); } else { primaryGround = new GroundHit(hit.point, hit.normal, hit.distance); } } else { Debug.LogWarning("[SuperCharacterComponent]: WALKABLE LAYER NOT PROPERLY SET. SEE README FILE."); } }
/// <summary> /// Scan the surface below us for ground. Follow up the initial scan with subsequent scans /// designed to test what kind of surface we are standing above and handle different edge cases /// 扫描地面下方的表面。 通过随后的扫描进行初始扫描,以便测试我们站在上面的哪种表面,并处理不同的边缘情况 /// </summary> /// <param name="origin">Center of the sphere for the initial SphereCast</param> /// <param name="iter">Debug tool to print out which ProbeGround iteration is being run (3 are run each frame for the controller)</param> public void ProbeGround(Vector3 origin, int iter) { ResetGrounds(); Vector3 up = controller.up; Vector3 down = -up; //略微向上偏移 Vector3 o = origin + (up * Tolerance); // Reduce our radius by Tolerance squared to avoid failing the SphereCast due to clipping with walls // 通过公差平方减少我们的半径,避免由于墙壁剪切而导致SphereCast失败 float smallerRadius = controller.radius - (Tolerance * Tolerance); RaycastHit hit; //圆形匹配 单一 if (Physics.SphereCast(o, smallerRadius, down, out hit, Mathf.Infinity, walkable, triggerInteraction)) { var superColType = hit.collider.gameObject.GetComponent <SuperCollisionType>(); if (superColType == null) { superColType = defaultCollisionType; } superCollisionType = superColType; transform = hit.transform; // By reducing the initial SphereCast's radius by Tolerance, our casted sphere no longer fits with // our controller's shape. Reconstruct the sphere cast with the proper radius //通过容差减少初始SphereCast的半径,我们的铸造球体不再符合我们控制器的形状。 重建以适当半径投射的球体 //软件的翻译,我不太明白 //Simulate:模仿----》实际上对hit进行了微微的调整,具体为什么我还得看看文档 SimulateSphereCast(hit.normal, out hit); //整理地面信息,当前位置的,进行描述 primaryGround = new GroundHit(hit.point, hit.normal, hit.distance); // If we are standing on a perfectly flat surface, we cannot be either on an edge, // On a slope or stepping off a ledge if (Vector3.Distance(Math3d.ProjectPointOnPlane(controller.up, controller.transform.position, hit.point), controller.transform.position) < TinyTolerance) { //为什么在投影点和位置很近的时候 //就什么什么的了? //如果我们站在一个完美平坦的表面上 return; } // As we are standing on an edge, we need to retrieve the normals of the two // faces on either side of the edge and store them in nearHit and farHit // 当我们站在边缘时,我们需要检索边缘两侧的两个面的法线并将它们存储在nearHit和farHit //中心 算的一个平面的投影 Vector3 toCenter = Math3d.ProjectVectorOnPlane(up, (controller.transform.position - hit.point).normalized * TinyTolerance); //完全不知道什么原理的样子 Vector3 awayFromCenter = Quaternion.AngleAxis(-80.0f, Vector3.Cross(toCenter, up)) * -toCenter; Vector3 nearPoint = hit.point + toCenter + (up * TinyTolerance); Vector3 farPoint = hit.point + (awayFromCenter * 3); RaycastHit nearHit; RaycastHit farHit; Physics.Raycast(nearPoint, down, out nearHit, Mathf.Infinity, walkable, triggerInteraction); Physics.Raycast(farPoint, down, out farHit, Mathf.Infinity, walkable, triggerInteraction); nearGround = new GroundHit(nearHit.point, nearHit.normal, nearHit.distance); farGround = new GroundHit(farHit.point, farHit.normal, farHit.distance); // If we are currently standing on ground that should be counted as a wall, // we are likely flush against it on the ground. Retrieve what we are standing on if (Vector3.Angle(hit.normal, up) > superColType.StandAngle) { // Retrieve a vector pointing down the slope Vector3 r = Vector3.Cross(hit.normal, down); Vector3 v = Vector3.Cross(r, hit.normal); Vector3 flushOrigin = hit.point + hit.normal * TinyTolerance; RaycastHit flushHit; if (Physics.Raycast(flushOrigin, v, out flushHit, Mathf.Infinity, walkable, triggerInteraction)) { RaycastHit sphereCastHit; if (SimulateSphereCast(flushHit.normal, out sphereCastHit)) { flushGround = new GroundHit(sphereCastHit.point, sphereCastHit.normal, sphereCastHit.distance); } else { // Uh oh } } } // If we are currently standing on a ledge then the face nearest the center of the // controller should be steep enough to be counted as a wall. Retrieve the ground // it is connected to at it's base, if there exists any if (Vector3.Angle(nearHit.normal, up) > superColType.StandAngle || nearHit.distance > Tolerance) { SuperCollisionType col = null; if (nearHit.collider != null) { col = nearHit.collider.gameObject.GetComponent <SuperCollisionType>(); } if (col == null) { col = defaultCollisionType; } // We contacted the wall of the ledge, rather than the landing. Raycast down // the wall to retrieve the proper landing if (Vector3.Angle(nearHit.normal, up) > col.StandAngle) { // Retrieve a vector pointing down the slope Vector3 r = Vector3.Cross(nearHit.normal, down); Vector3 v = Vector3.Cross(r, nearHit.normal); RaycastHit stepHit; if (Physics.Raycast(nearPoint, v, out stepHit, Mathf.Infinity, walkable, triggerInteraction)) { stepGround = new GroundHit(stepHit.point, stepHit.normal, stepHit.distance); } } else { stepGround = new GroundHit(nearHit.point, nearHit.normal, nearHit.distance); } } } // If the initial SphereCast fails, likely due to the controller clipping a wall, // fallback to a raycast simulated to SphereCast data else if (Physics.Raycast(o, down, out hit, Mathf.Infinity, walkable, triggerInteraction)) { var superColType = hit.collider.gameObject.GetComponent <SuperCollisionType>(); if (superColType == null) { superColType = defaultCollisionType; } superCollisionType = superColType; transform = hit.transform; RaycastHit sphereCastHit; if (SimulateSphereCast(hit.normal, out sphereCastHit)) { primaryGround = new GroundHit(sphereCastHit.point, sphereCastHit.normal, sphereCastHit.distance); } else { primaryGround = new GroundHit(hit.point, hit.normal, hit.distance); } } else { Debug.LogError("[SuperCharacterComponent]: No ground was found below the player; player has escaped level"); } }