public void CheckBlocking(ref Vector2 original, HashSet <Tile> tilesToMove) { Vector2 largestValidMoveAmount = original; Vector2 norm = original.normalized; RaycastHit2D[] hits = Physics2D.BoxCastAll( transform.position, size - Vector2.one * 2 * skinWidth, transform.eulerAngles.z, original.normalized, original.magnitude + skinWidth, passengerMask ); DebugExtensions.DrawBoxCast2D( transform.position, (size - Vector2.one * 2 * skinWidth), transform.eulerAngles.z, original.normalized, original.magnitude + skinWidth, Color.cyan ); foreach (RaycastHit2D hit in hits) { IPlatformMoveBlocker pass = hit.collider.GetComponent <IPlatformMoveBlocker>(); if (pass != null) { Vector2 passMoveAmount = original - norm * (hit.distance - skinWidth); pass.CheckBlocking(ref passMoveAmount, tilesToMove); original = passMoveAmount + norm * (hit.distance - skinWidth); } } }
void CalculatePassengerMovement(Vector3 velocity) { Dictionary <Transform, IPlatformMoveBlocker> movedPassengers = new Dictionary <Transform, IPlatformMoveBlocker> (); passengerMovement = new List <PassengerMovement> (); System.Func <Vector2, RaycastHit2D, PassengerMovement> GetMovement = (dir, hit) => { IPlatformMoveBlocker blocker = movedPassengers[hit.transform]; // add 90 to convert from Vector2.down being default direction to Vector2.right Vector2 gravityAngle = Utils.AngleToVector(blocker.GravityAngle - 90); Vector2 normalizedVelocity = velocity.normalized; var rotatedVelocity = velocity.Rotate(-blocker.GravityAngle); float dot = Vector2.Dot(gravityAngle, normalizedVelocity); bool movingWithDirection = rotatedVelocity.y > 0; // we need to check the direction cast in comparison to gravity to determine // players position relative to the platform (i.e. player on top) float positionDot = Vector2.Dot(gravityAngle, dir); bool onTop = positionDot < -0.9f; float velocityDot = Vector2.Dot(normalizedVelocity, dir); if (dot > 0.5f) { // gravity and platform moving in same direction if (onTop) { // passenger is on top of the platform and should be moved with it float pushX = velocity.x; float pushY = velocity.y; return(new PassengerMovement(hit.transform, new Vector3(pushX, pushY), true, false, false)); } else { // passenger is below the platform and will be pushed float pushX = 0f; float pushY = rotatedVelocity.y - (hit.distance - skinWidth) * Mathf.Sign(rotatedVelocity.y); return(new PassengerMovement(hit.transform, new Vector3(pushX, pushY), false, true, true)); } } else if (dot < -0.5f) { // gravity and platform moving in opposite directions float pushX = movingWithDirection ? rotatedVelocity.x : 0; float pushY = rotatedVelocity.y - (hit.distance - skinWidth) * Mathf.Sign(rotatedVelocity.y); return(new PassengerMovement(hit.transform, new Vector3(pushX, pushY), onTop, true, false)); } else if (positionDot < 0.9f) { // platform is moving side-to-side relative to gravity // only disallow movement is player is below platform float pushX = rotatedVelocity.x - (hit.distance - skinWidth) * Mathf.Sign(rotatedVelocity.x); float pushY = 0f; return(new PassengerMovement(hit.transform, new Vector3(pushX, pushY), onTop, false, false)); } return(null); }; System.Action <Vector2, Vector2, float> CastForPoint = (pt, dir, dist) => { //Debug.DrawRay(pt, dir*dist, new Color(dir.y*0.5f+0.5f, dir.x*0.5f+0.5f, 0), 0.1f); RaycastHit2D hit = Physics2D.Raycast(pt, dir, dist, passengerMask); if (hit) { if (!movedPassengers.ContainsKey(hit.transform)) { movedPassengers.Add(hit.transform, hit.transform.GetComponent <IPlatformMoveBlocker>()); PassengerMovement m = GetMovement(dir, hit); if (m != null) { passengerMovement.Add(m); } } else { PassengerMovement m = GetMovement(dir, hit); int index = passengerMovement.FindIndex(pm => pm.transform == hit.transform); if (m != null && index >= 0 && m.velocity.sqrMagnitude > passengerMovement[index].velocity.sqrMagnitude) { passengerMovement[index] = m; } } } }; var pts = GeneratePoints(); float topDistance = skinWidth * 2f; float bottomDistance = skinWidth * 2f; Vector2 rotatedPlatformVelocity = velocity.Rotate(-transform.eulerAngles.z); if (rotatedPlatformVelocity.y > 0) { topDistance = Mathf.Max(topDistance, Mathf.Abs(rotatedPlatformVelocity.y) + skinWidth); } else { bottomDistance = Mathf.Max(bottomDistance, Mathf.Abs(rotatedPlatformVelocity.y) + skinWidth); } Vector2 up = Vector2.up.Rotate(transform.eulerAngles.z); Vector2 right = Vector2.right.Rotate(transform.eulerAngles.z); foreach (var pt in pts.Top) { CastForPoint(pt, up, topDistance); } foreach (var pt in pts.Bottom) { CastForPoint(pt, -up, bottomDistance); } float leftDistance = skinWidth * 2f; float rightDistance = skinWidth * 2f; if (rotatedPlatformVelocity.x > 0) { rightDistance = Mathf.Max(rightDistance, Mathf.Abs(rotatedPlatformVelocity.x) + skinWidth); } else { leftDistance = Mathf.Max(leftDistance, Mathf.Abs(rotatedPlatformVelocity.x) + skinWidth); } foreach (var pt in pts.Right) { CastForPoint(pt, right, rightDistance); } foreach (var pt in pts.Left) { CastForPoint(pt, -right, leftDistance); } }