//Checks our vertical collisions, and either stops us from moving or enables us to continue moving private void CheckVerticalCollisions(float velocityToCheck, bool isTranslatingDirectly = false) { if (!willStickToMovingPlatforms) { movingPlatform = null; } box = GetRectAtPosition(properties.position); Direction.Vertical direction = (velocityToCheck <= 0.0f) ? Direction.Vertical.Down : Direction.Vertical.Up; if (velocityToCheck == 0.0f) { return; } //The edge buffer prevents you from standing on a ledge with the very corner of your hitbox //This fixes problems where you jump straight up *next to* a ledge and the end up resting on the ledge itself on the way down float edgeBuffer = 0.025f; Vector2 startPoint = new Vector2(box.xMin + edgeBuffer, box.center.y); Vector2 endPoint = new Vector2(box.xMax - edgeBuffer, box.center.y); RaycastHit2D[] raycastHits = new RaycastHit2D[raycastAmounts.vertical]; float rayLength = box.height / 2.0f + (((properties.isGrounded && direction == Direction.Vertical.Down) || (properties.isAgainstCeiling && direction == Direction.Vertical.Up)) ? slopeDetectionMargin : Mathf.Abs(velocityToCheck)); float lowestFraction = Mathf.Infinity; int savedIndex = 0; bool didCollide = false; for (int i = 0; i < raycastAmounts.vertical; i++) { float raycastSpacing = (float)i / (float)(raycastAmounts.vertical - 1); Vector2 rayOrigin = Vector2.Lerp(startPoint, endPoint, raycastSpacing); Vector2 rayDirection = (direction == Direction.Vertical.Down) ? -Vector2.up : Vector2.up; //Debug.DrawRay(rayOrigin, rayDirection * rayLength, Color.red); int mask = ((direction == Direction.Vertical.Down && gravitySettings.gravityScale > 0.0f) || (direction == Direction.Vertical.Up && gravitySettings.gravityScale <= 0.0f)) ? collisionLayerMaskDown : collisionLayerMask; raycastHits[i] = Physics2D.Raycast(rayOrigin, rayDirection, rayLength, mask); if (raycastHits[i].fraction > 0) { didCollide = true; if (raycastHits[i].fraction < lowestFraction) { savedIndex = i; lowestFraction = raycastHits[i].fraction; } } } //This prevents you from walking off a one-way platform, holding left or right to be back inside it as you fall, and clipping back up on top of it if (didCollide && raycastHits[savedIndex].collider.gameObject.layer == LayerMask.NameToLayer("PassThroughBottom")) { float buffer = 0.1f; if ((gravitySettings.gravityScale > 0.0f && box.yMin < raycastHits[savedIndex].collider.bounds.max.y - buffer) || (gravitySettings.gravityScale <= 0.0f && box.yMax > raycastHits[savedIndex].collider.bounds.min.y + buffer)) { didCollide = false; } } //In rare instances, if the actor is already on top of a PassThroughBottom collider when they land on the ground, the above raycast can fail to detect the terrain; this is a failsafe if (!didCollide && raycastAmounts.enableRedundantVerticalCollisions) { int adjustedVerticalRaycasts = Mathf.CeilToInt(raycastAmounts.vertical * 0.5f); for (int i = 0; i < adjustedVerticalRaycasts; i++) { float raycastSpacing = (float)i / (float)(adjustedVerticalRaycasts - 1); Vector2 rayOrigin = Vector2.Lerp(startPoint, endPoint, raycastSpacing); Vector2 rayDirection = (direction == Direction.Vertical.Down) ? -Vector2.up : Vector2.up; int mask = collisionLayerMask; raycastHits[i] = Physics2D.Raycast(rayOrigin, rayDirection, rayLength, mask); if (raycastHits[i].fraction > 0) { didCollide = true; if (raycastHits[i].fraction < lowestFraction) { savedIndex = i; lowestFraction = raycastHits[i].fraction; } } } } if (didCollide) { properties.velocity = new Vector2(properties.velocity.x, 0); properties.externalVelocity = new Vector2(properties.externalVelocity.x, 0); if (direction == Direction.Vertical.Down) { properties.isGrounded = true; willSnapToFloorOnStart = false; properties.isFalling = false; properties.isAgainstCeiling = false; properties.position += Vector2.down * (raycastHits[savedIndex].fraction * rayLength - box.height / 2); MovingPlatform platform = raycastHits[savedIndex].collider.GetComponent <MovingPlatform>(); if (platform != null) { movingPlatform = platform; movingPlatform.NotifyOfObjectOnTop(); } else { movingPlatform = null; } } else { if (willStickToMovingPlatforms) { MovingPlatform platform = raycastHits[savedIndex].collider.GetComponent <MovingPlatform>(); if (platform != null) { movingPlatform = platform; } else { movingPlatform = null; } } properties.isAgainstCeiling = true; if (gravitySettings.gravityScale > 0.0f) //Hit your head on the ceiling; stop your jump { properties.isFalling = true; } else { properties.isFalling = false; } properties.position += Vector2.up * (raycastHits[savedIndex].fraction * rayLength - box.height / 2); } if (DidLandThisFrame()) { if (gravitySettings.gravityScale >= 0.0f) { NotifyOfCollision(raycastHits[savedIndex].collider, RexObject.Side.Bottom, RexObject.CollisionType.Enter); } else { NotifyOfCollision(raycastHits[savedIndex].collider, RexObject.Side.Top, RexObject.CollisionType.Enter); } } if (DidHitCeilingThisFrame()) { if (gravitySettings.gravityScale >= 0.0f) { NotifyOfCollision(raycastHits[savedIndex].collider, RexObject.Side.Top, RexObject.CollisionType.Enter); } else { NotifyOfCollision(raycastHits[savedIndex].collider, RexObject.Side.Bottom, RexObject.CollisionType.Enter); } } } else { properties.isAgainstCeiling = false; properties.isGrounded = false; if (isTranslatingDirectly) { properties.position = new Vector2(transform.position.x, properties.position.y + velocityToCheck); } } }
//Checks our horizontal collisions, and either stops us from moving or enables us to continue moving private void CheckHorizontalCollisions(float velocityToCheck, bool isTranslatingDirectly = false) { properties.isAgainstLeftWall = false; properties.isAgainstRightWall = false; box = GetRectAtPosition(properties.position); if (velocityToCheck != 0) { //edgebuffer prevents unintended horizontal collisions with a MovingPlatform you're riding on top of float edgeBuffer = (isTranslatingDirectly) ? 0.05f : 0.0f; Vector2 startPoint = new Vector2(box.center.x, box.center.y - box.height * 0.5f + edgeBuffer); Vector2 endPoint = new Vector2(box.center.x, box.center.y + box.height * 0.5f); RaycastHit2D[] raycastHits = new RaycastHit2D[raycastAmounts.horizontal]; int numberOfCollisions = 0; float fraction = 0; float sideRayLength = box.width / 2 + Mathf.Abs(velocityToCheck); Vector2 direction = (velocityToCheck) > 0 ? Vector2.right : -Vector2.right; bool didCollide = false; for (int i = 0; i < raycastAmounts.horizontal; i++) { float raycastSpacing = (float)i / (float)(raycastAmounts.horizontal - 1); Vector2 rayOrigin; if (i == 1 && !willStickToMovingPlatforms) { rayOrigin = Vector2.Lerp(startPoint, endPoint, ((float)(i - 1) / (float)(raycastAmounts.horizontal - 1)) + 0.005f); } else if (i == raycastAmounts.horizontal - 2 && !willStickToMovingPlatforms) { rayOrigin = Vector2.Lerp(startPoint, endPoint, ((float)(i + 1) / (float)(raycastAmounts.horizontal - 1)) - 0.005f); } else { rayOrigin = Vector2.Lerp(startPoint, endPoint, raycastSpacing); } raycastHits[i] = Physics2D.Raycast(rayOrigin, direction, sideRayLength, collisionLayerMask); //Debug.DrawRay(rayOrigin, direction * sideRayLength, Color.red); if (raycastHits[i].fraction > 0) { didCollide = true; if (fraction > 0) { float slopeAngle = Vector2.Angle(raycastHits[i].point - raycastHits[i - 1].point, Vector2.right); if (Mathf.Abs(slopeAngle - 90) < slopeAngleBuffer) //If the slope is too steep, treat it as a wall { if (direction == Vector2.right) { properties.isAgainstRightWall = true; } else { properties.isAgainstLeftWall = true; } if (willStickToMovingPlatforms) { MovingPlatform platform = raycastHits[i].collider.GetComponent <MovingPlatform>(); if (platform != null) { movingPlatform = platform; } else { movingPlatform = null; } } properties.position += (direction * (raycastHits[i].fraction * sideRayLength - box.width / 2.0f)); properties.velocity = new Vector2(0, properties.velocity.y); properties.externalVelocity = new Vector2(0, properties.externalVelocity.y); RexObject.Side side = (direction == Vector2.right) ? RexObject.Side.Right : RexObject.Side.Left; if (side == RexObject.Side.Left && DidHitLeftWallThisFrame() && velocityToCheck < 0.0f) { NotifyOfCollision(raycastHits[i].collider, RexObject.Side.Left, RexObject.CollisionType.Enter); } else if (side == RexObject.Side.Right && DidHitRightWallThisFrame() && velocityToCheck > 0.0f) { NotifyOfCollision(raycastHits[i].collider, RexObject.Side.Right, RexObject.CollisionType.Enter); } break; } } numberOfCollisions++; fraction = raycastHits[i].fraction; } if (!didCollide && isTranslatingDirectly) { properties.position = new Vector2(transform.position.x + velocityToCheck, properties.position.y); } } } else { CheckForWallContact(Direction.Horizontal.Left); CheckForWallContact(Direction.Horizontal.Right); } }