Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Raycast not detecting ground

Discussion in 'Scripting' started by Michael_93, Dec 24, 2015.

  1. Michael_93

    Michael_93

    Joined:
    Jan 12, 2014
    Posts:
    95
    I'm currently trying to create a 2d platform without the built in physics engine, so I'm using raycasts and colliders. I am fairly new to this, I tried to do the best I could on my own then I found a tutorial on youtube. I pretty much copied the code after mine didn't work but the copied code didn't work either.
    If anyone can help me understand why the raycast is ignoring the platform that would be greatly appreciated.

    Here is my code:
    the collisionMask is set up in the editor

    Code (CSharp):
    1.  void VerticalCollisions(ref Vector3 velocity)
    2.     {
    3.         float directionY = Mathf.Sign(velocity.y);
    4.         float rayLength = Mathf.Abs(velocity.y) + skinWidth;
    5.         Vector2 rayCastOrigin;
    6.         RaycastHit2D hit;
    7.         for (int i = 0; i < verticalRayCount; i++)
    8.         {
    9.             if (directionY <= 0)
    10.             rayCastOrigin = raycastOrigins.bottomLeft;
    11.             else
    12.             rayCastOrigin = raycastOrigins.topLeft;
    13.  
    14.             rayCastOrigin += (Vector2.right * (verticalRaySpacing * i+ velocity.x))+ Vector2.up*velocity.y;
    15.  
    16.             hit = Physics2D.Raycast(rayCastOrigin, Vector2.up * directionY, rayLength,  collisionMask);
    17.             Debug.DrawRay(rayCastOrigin, Vector2.up * directionY*rayLength, Color.red);
    18.  
    19.             Debug.Log("ray Length" + rayLength);
    20.             if (hit)
    21.             {                                
    22.                 Debug.Log("hit");            
    23.                 velocity.y = (rayLength - hit.distance - skinWidth);
    24.                 rayLength = hit.distance;    
    25.             }
    26.         }
    27.     }

    the code that I pretty much copied from is here: https://github.com/SebLague/2DPlatformer-Tutorial/blob/master/Episode 02/Controller2D.cs




    UPDATE
    So I figure out where I'm having the problem which is the layer mask which opened up a whole new bag of worms. I'm going to create a new thread for a more specific question.
     
    Last edited: Dec 26, 2015
  2. Afrodeity

    Afrodeity

    Joined:
    Dec 19, 2015
    Posts:
    12
    Your ground object is most likely being ignored because you haven't assigned a layer to the collision mask. You say you've already set up the collision mask, but maybe you're missing one of the two steps. First, select the ground object and assign it a layer in the inspector . Next, select your player object and, in the player controller script tab of the inspector, set the collision mask to the same layer you've selected for your ground.
     
  3. Michael_93

    Michael_93

    Joined:
    Jan 12, 2014
    Posts:
    95
    I did do that.
    The cube that it collides with is on the platformMask layer in the editor. And the collision mask layer in the controller script is set to the platformMask layer.
    One thing that I feel like I should mention is that when I don't set the ray length hit returns true. I have no idea why this is the case as it should hit regardless. It doesn't work even when it does hit but that doesn't bother me just yet.
     
  4. Afrodeity

    Afrodeity

    Joined:
    Dec 19, 2015
    Posts:
    12
    Hmm... In that case I took a second look at your code. Why are you setting velocity.y to rayLenght - hit.distance - skinWidth? First off, you want your player to stop when it touches the ground, which is equal to hit.distance - skinWidth. Secondly, hit.distance returns a positive value, but you want to account for collision below you as well, so it should look something like directionY*(hit.distance-skinWidth). However, despite those errors, your player still shouldnt be acting the way you described. If you're still stuck, please post your entire code for further examination.
     
  5. Michael_93

    Michael_93

    Joined:
    Jan 12, 2014
    Posts:
    95
    I really don't understand that part of the code, apparently it works (no idea why) but I plan to replace it with my own code.
    My current thoughts is that I may have misunderstood how layers work. My raycast took a layer mask as the parameter that I want collisions to occur on. Now I am getting mixed feedback on this from google searches. I removed it and there was no change in behavior. I also tried hit.collider in the if statement and it didn't work either

    Thanks for feedback.


    Here is my entire controller file:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. [RequireComponent(typeof(BoxCollider2D))]
    5.  
    6.  
    7. //This class detects collisions
    8. public class Controller2D : MonoBehaviour
    9. {
    10.  
    11.     public LayerMask collisionMask;
    12.  
    13.     const float skinWidth = .015f;
    14.     public int horizontalRayCount = 4;
    15.     public int verticalRayCount = 4;
    16.  
    17.     float horizontalRaySpacing;
    18.     float verticalRaySpacing;
    19.  
    20.     bool isGrounded;
    21.  
    22.     Collision2D collision;
    23.     BoxCollider2D collider;
    24.     RaycastOrigins raycastOrigins;
    25.  
    26.     void Start()
    27.     {
    28.         collider = GetComponent<BoxCollider2D>();
    29.         CalculateRaySpacing();
    30.     }
    31.  
    32.     public void Move(Vector3 velocity)
    33.     {
    34.         UpdateRaycastOrigins();
    35.         if (velocity.y != 0) {
    36.         VerticalCollisions(ref velocity);
    37.         }
    38.         transform.Translate(velocity);
    39.     }
    40.  
    41.  
    42.     //We want to only check collisions if the player
    43.     //is in the air falling or moving upwards
    44.     void VerticalCollisions(ref Vector3 velocity)
    45.     {
    46.      
    47.         float directionY = Mathf.Sign(velocity.y);
    48.         float rayLength = Mathf.Abs(velocity.y) + skinWidth;
    49.         Vector2 rayCastOrigin;
    50.         RaycastHit2D hit;
    51.         for (int i = 0; i < verticalRayCount; i++)
    52.         {
    53.             if (directionY <= 0)
    54.             rayCastOrigin = raycastOrigins.bottomLeft;
    55.             else
    56.             rayCastOrigin = raycastOrigins.topLeft;
    57.  
    58.             rayCastOrigin += (Vector2.right * (verticalRaySpacing * i+ velocity.x))+ Vector2.up*velocity.y;
    59.  
    60.             hit = Physics2D.Raycast(rayCastOrigin, Vector2.up * directionY, rayLength);
    61.             Debug.DrawRay(rayCastOrigin, Vector2.up * directionY*rayLength, Color.red);
    62.  
    63.             Debug.Log("ray Length" + rayLength);
    64.             if (hit.collider!=null)
    65.             {            
    66.        
    67.                 Debug.Log("hit");              
    68.                 velocity.y = (rayLength - hit.distance - skinWidth);
    69.                 rayLength = hit.distance;      
    70.             }
    71.         }
    72.     }
    73.  
    74.  
    75.     void UpdateRaycastOrigins()
    76.     {
    77.         Bounds bounds = collider.bounds;
    78.         bounds.Expand(skinWidth * -2);
    79.  
    80.         raycastOrigins.bottomLeft = new Vector2(bounds.min.x, bounds.min.y);
    81.         raycastOrigins.bottomRight = new Vector2(bounds.max.x, bounds.min.y);
    82.         raycastOrigins.topLeft = new Vector2(bounds.min.x, bounds.max.y);
    83.         raycastOrigins.topRight = new Vector2(bounds.max.x, bounds.max.y);
    84.     }
    85.  
    86.     void CalculateRaySpacing()
    87.     {
    88.         Bounds bounds = collider.bounds;
    89.         bounds.Expand(skinWidth * -2);
    90.  
    91.         horizontalRayCount = Mathf.Clamp(horizontalRayCount, 2, int.MaxValue);
    92.         verticalRayCount = Mathf.Clamp(verticalRayCount, 2, int.MaxValue);
    93.  
    94.         horizontalRaySpacing = bounds.size.y / (horizontalRayCount - 1);
    95.         verticalRaySpacing = bounds.size.x / (verticalRayCount - 1);
    96.     }
    97.  
    98.     struct RaycastOrigins
    99.     {
    100.         public Vector2 topLeft, topRight;
    101.         public Vector2 bottomLeft, bottomRight;
    102.     }
    103.  
    104. }

    Here is the player file:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. [RequireComponent(typeof(Controller2D))]
    5. public class Player : MonoBehaviour
    6. {
    7.     float moveSpeed = 6;
    8.     float gravity = -9.81F;
    9.     Vector3 velocity;
    10.  
    11.     Controller2D controller;
    12.  
    13.     void Start()
    14.     {
    15.         controller = GetComponent<Controller2D>();
    16.     }
    17.  
    18.     void Update()
    19.     {
    20.  
    21.         Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
    22.         velocity.x = input.x * moveSpeed;
    23.         velocity.y += gravity * Time.deltaTime;
    24.         controller.Move(velocity * Time.deltaTime);
    25.     }
    26. }
     
  6. Afrodeity

    Afrodeity

    Joined:
    Dec 19, 2015
    Posts:
    12
    The code for your controller doesn't specify the mask the rays can hit, so it'll only check the default layer. Simply add collisionMask as the last parameter on line 60 or leave it blank, but set your ground layer back to the default layer (I'd recommend the former).
    Code (CSharp):
    1. RaycastHit2D hit = Physics2D.Raycast(rayOrigin, Vector2.up * directionY, rayLength, collisionMask);
    You said you used to have this as a parameter. It should stay. If collision still doesn't work, maybe you forgot to add a boxcollider-component to your ground.
    And to help with your code comprehension issues:
    Basically, the maths behind it is that in your UpdateRaycastOrigins()-method, you offset the position from which the rays willl be shot, so that your player actually overlaps slightly with the ground. This is for visual flair, but now you'll have to account for it during your collision detection. And please take my advice from my previous comment: set velocity.y to (hit.distance-skinWidth)*directionY. hit.distance is the distance between player and obstacle and thats exactly how far we want to move. Your code however intends to move the player equal to the amount he would overlap with the obstacle in the next frame (rayLength-hit.distance), creating strange slow-downs.
    Correct those errors in line 60 and 68 and you should be good to go.
     
  7. Michael_93

    Michael_93

    Joined:
    Jan 12, 2014
    Posts:
    95
    It still doesn't enter the if conditional, I'll fix the math in the conditional once I begin to enter it. Anyways thanks for the clarification. I want to belive it has something to do with the ray length but I'm just not sure .


    I have no idea why this isn't working because it was working just fine with the tutorial that I was following. Here is an image of player and the ground in the editor:

    Gravity is set to one in these images that is why the ray cast is so small
     

    Attached Files:

  8. Afrodeity

    Afrodeity

    Joined:
    Dec 19, 2015
    Posts:
    12
    Sry for the late response. Since you're using Physics2D, your hit.collider is of the type Collider2D, but your ground has a 3-dimensional Box Collider. To fix this, simply replace your ground's collider with the 2D equivalent.
     
  9. Michael_93

    Michael_93

    Joined:
    Jan 12, 2014
    Posts:
    95
    probably should have updated this earlier but yeah I figured that out about a week and a bit ago. I'm just having issues with the corners but I think I can figure that out.