I created a script to climb ledges. It shoots 4 raycasts total:
-2 shooting straight forward from player (origins left and right of player)
-2 shooting down from forward&above player (origins left and right of player)
Like so:
If these all hit, it calculates the midpoint of the Upper Raycasts to determine what Vector3 position to AddForce towards. This Vector3 is called targetPointToJumpTo.
This ledge grabbing technique seems to work well enough in most cases. However, when it encounters a situation of meshes stacked on top of each other, it returns targetPointToJumpTo as a point between those meshes, as in inside of them where there isn't actually a ledge.
Screenshot of problem:
I'm quite confused as to the cause of this behavior. Would anyone be able to assess possible solutions? Below is my full ledgegrab script.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class LedgeGrab : MonoBehaviour { public LayerMask layerMask; //lower raycasts public float offsetRight; public float offsetLeft; public float offsetUp; public float raycastLength; //upper raycasts public RaycastHit upleftRayHit; public RaycastHit uprightRayHit; public float upraycastLength; //empty game objects that upper raycasts spawn from public Transform upperLeftTransform; public Transform upperRightTransform; //misc PlayerControllerScript sf1; [SerializeField] private float angleBetweenRaycasts = 0f; public float distanceToGrabLedge; public bool jumpingToLedge; public bool holdingOnToLedge; [SerializeField] Vector3 targetPointToJumpTo; [SerializeField] float timeSinceLastOnLedge = 0; private void Awake() { sf1 = GetComponent<PlayerControllerScript>(); } private void Update() { if (timeSinceLastOnLedge > 1 && holdingOnToLedge == false && angleBetweenRaycasts != 0 && Vector3.Distance(transform.position, MidPoint(uprightRayHit.point, upleftRayHit.point)) < distanceToGrabLedge) { timeSinceLastOnLedge = 0; //print("Grab"); targetPointToJumpTo = MidPoint(uprightRayHit.point, upleftRayHit.point); jumpingToLedge = true; } if (Input.GetKeyDown(KeyCode.X) && holdingOnToLedge == true) { //print("Release"); holdingOnToLedge = false; sf1.r.useGravity = true; sf1.r.velocity = sf1.r.velocity; sf1.ignoreInput = false; } } private void FixedUpdate() { LowCasts(); UpCasts(); angleBetweenRaycasts = Vector3.Angle(upleftRayHit.point, uprightRayHit.point); if(jumpingToLedge == true) { //addforce towards targetPointToJumpTo every frame sf1.r.AddForce(targetPointToJumpTo - transform.position); //if reached close enough to point if (Vector3.Distance(transform.position, targetPointToJumpTo) < 1.5) { jumpingToLedge = false; sf1.ignoreInput = true; holdingOnToLedge = true; } } if (holdingOnToLedge == true) { sf1.r.useGravity = false; sf1.r.velocity = Vector3.zero; } else { timeSinceLastOnLedge = timeSinceLastOnLedge + 1 * Time.deltaTime; } } void LowCasts() { RaycastHit hit; //Right Raycast if (Physics.Raycast(transform.position + transform.right * offsetRight + transform.up * offsetUp, transform.TransformDirection(Vector3.forward), out hit, raycastLength, layerMask)) { //Debug.Log("Right Did Hit"); } else { //Debug.Log("Right Did not Hit"); } //Left Raycast if (Physics.Raycast(transform.position - transform.right * offsetLeft + transform.up * offsetUp, transform.TransformDirection(Vector3.forward), out hit, raycastLength, layerMask)) { //Debug.Log("Left Did Hit"); } else { //Debug.Log("Left Did not Hit"); } } void UpCasts() { //UpperLeft Raycast if (Physics.Raycast(upperLeftTransform.position, transform.TransformDirection(Vector3.down), out upleftRayHit, upraycastLength, layerMask)) { } else { //print("Didnt hit"); } //UpperRight Raycast if (Physics.Raycast(upperRightTransform.position, transform.TransformDirection(Vector3.down), out uprightRayHit, upraycastLength, layerMask)) { } else { } } public static Vector3 MidPoint(Vector3 start, Vector3 end) { return new Vector3( (start.x + end.x) / 2, (start.y + end.y) / 2, (start.z + end.z) / 2 ); } } Please note the actual Jump method is in PlayerControllerScript. This script though is what Adds Force to Player rigidbody towards viable ledges. Theoretically player does not even need to jump to grab onto ledge with this current code.
Thank you for any insight here.

