Search Unity

Distance to Ground not working?

Discussion in '2D' started by Dollique, Oct 2, 2016.

  1. Dollique

    Dollique

    Joined:
    Oct 2, 2016
    Posts:
    7
    Hi Everyone

    I tried to implement this Solution for jumping and I'm stuck and the distance to the ground.

    The code on my PlayerControls script I used is this:
    Code (CSharp):
    1. public Collider2D ground;
    2. void Start () {
    3.    distToGround = ground.GetComponent<BoxCollider2D>().bounds.extents.y;
    4. }
    In my game I attached the GameObject ground to the script on my player:


    When I 'Debug.Log(distToGround);' I get always the same number: 0.1679988

    Info: my player starts in the air and falls slowly to the ground so there should be a different number.

    Thanks for you help.
    Best Regards
    Dollique
     
  2. WinCisky

    WinCisky

    Joined:
    Sep 21, 2015
    Posts:
    8
    You get always the same number because bounds.extents returns the extents of the box (that's always the half of the size).
    The guide's function creates a raycast that checks if you are grounded at position distToGround +0.1
    Code (csharp):
    1.  
    2. functionIsGrounded():boolean{
    3. returnPhysics.Raycast(transform.position,-Vector3.up, distToGround +0.1);
    4. }
    5.  
     
  3. Dollique

    Dollique

    Joined:
    Oct 2, 2016
    Posts:
    7
    Okay, so there is something else that doesn't work.

    I have the function like this:

    Code (CSharp):
    1. bool IsGrounded() {
    2.         var hit = Physics.Raycast(transform.position, -Vector3.up, distToGround + 0.1f);
    3.  
    4.         Debug.Log(hit);
    5.  
    6.         if (hit) {
    7.             Debug.Log("grounded");
    8.             return true;
    9.         }
    10.         else return false;
    11.     }
    In the debugger I always get 'false'.

    What did I do wrong?
     
  4. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    What is the value of distToGround? Try Mathf.Infinity to be sure your distance isn't too small.

    You can also use Debug.DrawRay to visualize what's happening.
     
  5. Dollique

    Dollique

    Joined:
    Oct 2, 2016
    Posts:
    7
    I visualized it with

    Code (CSharp):
    1. Debug.DrawRay(transform.position, -Vector3.up);
    and it shows me only a line from my players center to the lowest point of my player. The line stops there and doesn't go any further.
    This seems logic as I was not able to use the DistToGround variable in the DrawRay debug code.

    Can you explain me how I can debug.DrawRay the full 'hit' variable including the DistToGround + 0.1f?
     
  6. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Code (CSharp):
    1. Debug.DrawRay(transform.position, -Vector3.up * (distToGround + 0.1f));
    Try something like this, to show green or red depending on if you got a hit result.

    Code (CSharp):
    1. bool IsGrounded() {
    2.     RaycastHit hit;
    3.     if(Physics.Raycast(transform.position, -Vector3.up, out hit, distToGround + 0.1f)) {
    4.         Debug.Log(hit);
    5.         Debug.Log("grounded");
    6.         Debug.DrawLine(transform.position, hit.point, Color.green);
    7.         return true;
    8.     } else {
    9.         Debug.DrawRay(transform.position, -Vector3.up * (distToGround + 0.1f), Color.red);
    10.         return false;
    11.     }
    12. }
     
  7. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    I've seen a couple of ways to do this, I've not done a platformer as of yet, the version I remember the most, is create another gameobject of a child of the player but move this below the player and put a collider on it, if this contacts(collided with it) the ground then your grounded else your not.

    it's simple, but it might not be as dynamic as you want, just an idea.
     
  8. WinCisky

    WinCisky

    Joined:
    Sep 21, 2015
    Posts:
    8
    If you want to know the distance to the ground maybe this is what you were looking for:
    Code (csharp):
    1. using UnityEngine;
    2. public class floor_distance : MonoBehaviour {
    3.     RaycastHit2D hit;
    4.     void FixedUpdate () {
    5.         hit = Physics2D.Raycast(new Vector2(0, transform.position.y - 0.5f), -Vector2.up);
    6.         if (hit.collider != null)
    7.         {
    8.             Debug.Log(transform.position.y - hit.point.y - 0.5f);
    9.         }
    10.     }
    11. }
     
  9. gabriellpweb

    gabriellpweb

    Joined:
    Oct 25, 2015
    Posts:
    17
    Try this:

    bool grounded = false;
    float halfHeight = GetComponent<BoxCollider2D>().size.y / 2;
    var hit = Physics2D.Raycast(transform.position, Vector2.down);
    if (hit && hit.collider)
    {
    grounded = Vector2.distance(transform.position, hit.collider.point) >= halfHeight;
    }
     
  10. Dollique

    Dollique

    Joined:
    Oct 2, 2016
    Posts:
    7
    I'm pretty sure the code of jeffreyschoch should work.
    I see a red line going from the center of the player down to the bottom of the player (like before).

    Could it be that the issue is not on my script but on the parameters in my game?

    I'm a beginner in unity so it could be I forgot something.

    One important thing I forgot: My game is a 2D platformer (like WinCisky guessed correctly).
     
  11. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    For 2D colliders, you need to use the 2D physics system for raycasts.

    You should also define a ground layer for anything that should be considered ground, and use a layermask with the raycast so that it will only collide with colliders on the ground layers. This will also keep the raycast from hitting your player.

    Code (CSharp):
    1. public LayerMask groundLayers;
    2. bool IsGrounded() {
    3.     RaycastHit2D hit = Physics2D.Raycast(transform.position, -Vector2.up, distToGround + 0.1f, groundLayers);
    4.     if(hit) {
    5.         Debug.DrawLine(transform.position, hit.point, Color.green);
    6.         return true;
    7.     } else {
    8.         Debug.DrawRay(transform.position, -Vector3.up * (distToGround + 0.1f), Color.red);
    9.         return false;
    10.     }
    11. }
     
    Last edited: Oct 4, 2016
  12. Dollique

    Dollique

    Joined:
    Oct 2, 2016
    Posts:
    7
    Yes, with the groundLayer defined it worked.
    But now I'm a little confused about how I defined 'DistToGround' because I had to define the actual collider as ground for it to work as expected:

    Code (CSharp):
    1. public LayerMask groundLayers;
    2.   public Collider2D ground;
    3.  
    4. void Start () {
    5.         player = GetComponent<Rigidbody2D>();
    6.         distToGround = ground.GetComponent<BoxCollider2D>().bounds.extents.y; // GROUND - distance to the ground
    7.     }
    Like you see in the screenshot on my first post here, I defined the ground (Box Collider 2D) as 'Ground'.

    Can I get the 'DistToGround' using the groundLayers variable somehow, so I could add multiple grounds to the layer and it would still work?
     
  13. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    You can put every gameobject that should be considered ground onto the ground layer. If Raycast is given a LayerMask, it will only detect colliders that are on the layers in the mask.

    That distance you're getting is the distance from your player's center to his feet. It's the half-height of the collider. You then add 0.1 so that you check a little bit below the player's feet.

    That will work fine for the raycast, but first make sure your raycast will work at all.

    This will fire a ray downward forever and let you know if it hit something. Once this is working, then replace Infinity with your original distance.
    Code (CSharp):
    1. public LayerMask groundLayers;
    2. bool IsGrounded() {
    3.     RaycastHit2D hit = Physics2D.Raycast(transform.position, -Vector2.up, Mathf.Infinity, groundLayers);
    4.     if(hit) {
    5.         Debug.DrawLine(transform.position, hit.point, Color.green);
    6.  
    7.         float distanceToGround = Vector2.Distance(transform.position, hit.point);
    8.         Debug.Log("Distance to ground: " + distanceToGround);
    9.  
    10.         return true;
    11.     } else {
    12.         Debug.DrawRay(transform.position, -Vector3.up * Mathf.Infinity, Color.red);
    13.         return false;
    14.     }
    15. }
    This should do what you expect:
    Code (CSharp):
    1. public LayerMask groundLayers;
    2. private float groundCheckDistance;
    3.  
    4. private void Start() {
    5.     // get the distance from the player's collider center, to the bottom of the collider, plus a little bit more
    6.     groundCheckDistance = GetComponent<Collider2D>().bounds.extents.y + 0.1f;
    7. }
    8.  
    9. private bool IsGrounded() {
    10.     Ray2D ray = new Ray2D(transform.position, Vector2.down);
    11.     RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction, groundCheckDistance, groundLayers);
    12.     if(hit) {
    13.         Debug.DrawLine(ray.origin, hit.point, Color.green);
    14.         return true;
    15.     } else {
    16.         Debug.DrawRay(ray.origin, ray.direction * groundCheckDistance, Color.red);
    17.         return false;
    18.     }
    19. }
     
    Last edited: Oct 4, 2016
  14. Dollique

    Dollique

    Joined:
    Oct 2, 2016
    Posts:
    7
    Ok, I see now how I can achieve the distance to the ground, but this somehow doesn't help me.
    I thought I need the distance to the ground to be able to tell when my player isGrounded().

    I could now define something like 'if distance to ground is less than 0.814' I am grounded but this doesn't seem very accurate.

    To keep it short, I want my portion of code to run without to specify the 'ground' collider but only the groundLayer.
    Is there any other possibility to achieve this than with distanceToGround < 0.814 ?

    If I understand correctly a layer doesn't have a collider. Maybe is there a way to merge multiple colliders into one? Like this I could use I currently have (because with just one collider it works).
     
  15. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    The layer is set on the GameObject, at the top of the inspector. You can have as many gameobjects per layer as you want. So one ground object would be a gameobject with a collider2D, and the gameobject is set to the Ground layer.

    Your player raycasts downward a specific distance. That distance is what you're talking about when you say "0.814". The guide you were reading is telling you how to calculate a good distance for that raycast.

    The second bit of code in my last post will do all of that for you, and "IsGrounded" will return true only if the player's feet are within 0.1 of a ground object. You don't have to specify any colliders here. The player's collider is the only one referenced directly in order to calculate an appropriate raycast distance.
     
  16. Dollique

    Dollique

    Joined:
    Oct 2, 2016
    Posts:
    7
    Ah, I didn't see your Edit.
    Perfect this is exactly what I needed.

    I will tomorrow try to really understand all this code and then continue with the jumping code :)

    Thanks for the help, I'll write back here, if I have any further question!
     
    LiterallyJeff likes this.