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

Checking for Grounded State with BoxCast - Platforms

Discussion in '2D' started by nabrown, Apr 29, 2020.

  1. nabrown

    nabrown

    Joined:
    Jun 27, 2019
    Posts:
    27
    (Repost from Physics)

    I'm creating a 2d game with some platformer elements. I have a lot of one-way edge colliders with platform effectors, both horizontal and diagonal (for stairs). For checking if the character is grounded I use a box-cast against the terrain layer and log any hits.

    The issue I'm running into is when the character is jumping through one of the one-way edge colliders there are a few brief frames where my boxcast detects the collider and registers as grounded.

    My first fix was to measure the angle between the characters velocity and the normal of the collider my box-cast is detecting. If the angle is small (ex. when a player is jumping up vertically through a horizontal platform), I ignore the hit. I also make sure the velocity.y is greater than 0 because the issue only occurs for characters moving upwards.

    This fixed all the cases except one. When the player's jump peaks, it's velocity becomes 0. If that happens to occur at the same time the box-cast detects a platform, it counts as being grounded... Any ideas on how to fix this? Or just a better approach?

    Code (CSharp):
    1.    
    2. void FixedUpdate()
    3.     {
    4.         Rigidbody2D body = GetComponent<Rigidbody2D>();
    5.         groundHits.Clear();
    6.  
    7.         // Are we grounded?
    8.         RaycastHit2D[] potentialGroundHits = Physics2D.BoxCastAll(
    9.             new Vector2(transform.position.x, transform.position.y + 0.01f),
    10.             new Vector2(0.1f, 0.02f),
    11.             0,
    12.             Vector2.down,
    13.             0.1f,
    14.             LayerMask.GetMask(new string[] { "Terrain" })
    15.         );
    16.  
    17.         foreach (RaycastHit2D hit in potentialGroundHits)
    18.         {
    19.             TerrainTile tileHit = hit.collider.GetComponent<TerrainTile>();
    20.  
    21.             // Get the normal vector associated with this tile type
    22.             Vector2 normal = Vector2.up;    // Default is flat
    23.             if(tileHit.type == TerrainTile.Type.StairsAsc)
    24.             {
    25.                 normal = new Vector2(-0.7f, 0.7f);
    26.             }
    27.             else if(tileHit.type == TerrainTile.Type.StairsDesc)
    28.             {
    29.                 normal = new Vector2(0.7f, 0.7f);
    30.             }
    31.  
    32.             Vector2 normVelocity = Vector2.ClampMagnitude(body.velocity, 1.0f);
    33.             float deltaAngle = Vector2.Angle(normVelocity, normal);
    34.  
    35.             // If our velocity is close to the normal vector of this surface, ignore it
    36.             // (This is to handle when your jumping through a platform)
    37.             if (normVelocity.y > 0 && deltaAngle < 60)
    38.             {
    39.                 Debug.Log("V=" + normVelocity + " N=" + normal + " DA=" + deltaAngle);
    40.                 continue;
    41.             }
    42.  
    43.             groundHits.Add(hit);
    44.         }
    45.     }
    46.  
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    Take a look at my PhysicsExample2D GitHub project. This scene shows you how to query existing contacts to check for grounded state but you can use this for anything.

    In the end, after defining a ContactFilter2D the actual ground check is just this. Checking exsiting contacts is always accurate and requires no spatial queries. For a ground check all you really want to know is if you're in contact with a specific layer(s). A filter allows you to specify a range of angles for the normal so you can filter out contacts that are not underneath you for instance. You can combine multiple queries like this for below, left, right etc if you want.

    Querying existing contacts is far quicker than perform spatial queries as the contact work has already happened.

    Here's a video of that particular scene:
     
    samooniyakk likes this.
  3. nabrown

    nabrown

    Joined:
    Jun 27, 2019
    Posts:
    27
    Perfecto! Many thanks.