Setting up my 2D Character Colliders & Ground Check (The Right Way)

Hi everyone,

I’m new here, and I’m loving unity and the community so far. I’ve been looking around here and learning a lot from you all, however some concepts are just very confusing to me and trying to understand through other users code is making it even harder. I want to setup my character the ‘right’ way and not use code that I’m just going to have to completely scrap/redo later on down the line.

As of now, my tiles use polygon colliders (through the map creator ‘Tiled’) and for the ground check I’m using the OverLapCircle method as follows:

void FixedUpdate()
  {
  grounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
  }

However, what happens is if I set the groundCheckRadius too low, the character believes he is not grounded on the edges. To solve this, I set the groundCheckRadius higher, but then you can jump forever up walls because the character believes he is grounded. Example here:

I’ve read from other users that RayCast could be a way to solve this, but I just don’t understand how to set them up. I attempted to use this code:

    //your variables at top of script
        const int maxReturnedIntersections = 1;
        private RaycastHit2D[] hits = new RaycastHit2D[maxReturnedIntersections];
            private int groundedR;
            private int groundedM;
            private int groundedL;
            private bool isGrounded;
    
    //Then in Update(), or you can create a custom function for better organization, called like, CheckCollision() and call CheckCollision(); in your update. But anyway:
    void Update()
    {
    //Check Collisions
    //We use the NonAlloc version of Linecast, since we're calling it every frame and this version won't allocate memory each time, it doesn't even use 0.01ms of processing time for one of these.
            groundedR = Physics2D.LinecastNonAlloc(gndChkR_s.position, gndChkR_e.position,hits,(1 << LayerMask.NameToLayer("Ground")) | (1 << LayerMask.NameToLayer("PassthroughPlatform")));
            groundedM = Physics2D.LinecastNonAlloc(gndChkM_s.position, gndChkM_e.position,hits,(1 << LayerMask.NameToLayer("Ground")) | (1 << LayerMask.NameToLayer("PassthroughPlatform")));
            groundedL = Physics2D.LinecastNonAlloc(gndChkL_s.position, gndChkL_e.position,hits,(1 << LayerMask.NameToLayer("Ground")) | (1 << LayerMask.NameToLayer("PassthroughPlatform")));
    
            if ((groundedL > 0) || (groundedM > 0) || (groundedR > 0))
            {
                isGrounded = true;
                anim.SetBool ("Grounded", true);
            }
            else
            {
                isGrounded = false;
                anim.SetBool ("Grounded", false);
            }
    }

However, I just couldn’t get this method to work properly. I would be very grateful for any help and assistance in solving this issue.

Thanks!

Instead of LinecastNonAlloc try using RaycastNonAlloc and see if that helps.

The layers Ground and PassthroughPlatform → are they to be neglected or are those the only ones being checked ??

Hi there, I’ll give that a try. I’m not sure about your question, since this is not my code but code that I had found from another post. I only have the layer Ground assigned, which is to all my walls, ground and ceiling.

Yes , which would certainly mean that ground layers and passthroughplatform layers are the only ones being checked . So did you try with RayCastNonAlloc ??

Okay, I seem to have got the Raycast working, except now my groundcheck always returns true. So I can now jump forever wherever I am, I’ve probably done something simple and silly here? Code is as follows:

    public class PlayerControl : MonoBehaviour {
    
        public float moveSpeed;
        public float jumpHeight;
        private float moveVelocity;
        //public Transform groundCheck;
        //public Transform groundCheck2;
        //public float groundCheckRadius;
        public LayerMask whatIsGround;
        //private bool grounded;
        private bool doubleJumped;
        private Animator anim;
        public Transform firePoint;
        public GameObject ninjaStar;
        public float knockback;
        public float knockbackCount;
        public float knockbackLength;
        public bool knockFromRight;
        public float shotDelay;
        private float shotDelayCounter;
        public bool onLadder;
        public float climbSpeed;
        private float climbVelocity;
        private float gravityStore;
        private Rigidbody2D myrigidbody2D;
    
        //your variables at top of script
        const int maxReturnedIntersections = 1;
        private RaycastHit2D[] hits = new RaycastHit2D[maxReturnedIntersections];
        private int groundedR;
        private int groundedM;
        private int groundedL;
        public bool grounded;
    
        public Transform gndChkR_s;
        public Transform gndChkL_s;
        public Transform gndChkM_s;
    
        public Transform gndChkR_e;
        public Transform gndChkL_e;
        public Transform gndChkM_e;
    
    
    
        // Use this for initialization
        void Start () {
            anim = GetComponent<Animator>();
            myrigidbody2D = GetComponent<Rigidbody2D>();
            gravityStore = myrigidbody2D.gravityScale;
    
      
        }
    
        void FixedUpdate()
        {
            //grounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
            //grounded = Physics2D.OverlapArea(groundCheck.position, groundCheck2.position, whatIsGround)
    
            //Check Collisions
            //We use the NonAlloc version of Linecast, since we're calling it every frame and this version won't allocate memory each time, it doesn't even use 0.01ms of processing time for one of these.
            groundedR = Physics2D.RaycastNonAlloc(gndChkR_s.position, gndChkR_e.position, hits, (1 << LayerMask.NameToLayer("Ground")) | (1 << LayerMask.NameToLayer("PassthroughPlatform")));
            groundedM = Physics2D.RaycastNonAlloc(gndChkM_s.position, gndChkM_e.position, hits, (1 << LayerMask.NameToLayer("Ground")) | (1 << LayerMask.NameToLayer("PassthroughPlatform")));
            groundedL = Physics2D.RaycastNonAlloc(gndChkL_s.position, gndChkL_e.position, hits, (1 << LayerMask.NameToLayer("Ground")) | (1 << LayerMask.NameToLayer("PassthroughPlatform")));
    
            if ((groundedL > 0) || (groundedM > 0) || (groundedR > 0))
            {
                grounded = true;
                anim.SetBool ("Grounded", true);
                doubleJumped = false;
    
            }
            else
            {
                grounded = false;
                anim.SetBool ("Grounded", false);
            }

Try doing this:-
Play with distance value(only decrease it but not 0) until you find the right spot.

float distance=3f;
groundedR = Physics2D.RaycastNonAlloc(gndChkR_s.position, -Vector3.up, hits,distance, (1 << LayerMask.NameToLayer("Ground")) | (1 << LayerMask.NameToLayer("PassthroughPlatform")));

--------> (No distance applied)
Normal Ray

---------------> (distance applied is just scaling the vector by that amount)
Scaled Ray