0
\$\begingroup\$

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:

enter image description here

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:

enter image description here

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.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

The problem was that if you have a Raycast that originates within a Mesh, then there is basically no way to detect the backfaces, even if you have queriesHitBackfaces on.

From the Unity documentation:

Raycasts will not detect Colliders for which the Raycast origin is inside the Collider

Therefore, what I ended up doing was a different approach: Detecting if there was room above the ledge via a CapsuleCast. Now when two meshes are stacked on each other, CapsuleCast notices and flags the ledge grab as non-viable.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .