Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Question on Raycasting In a Unity Tutorial...

Discussion in '2D' started by sieks-, Nov 7, 2019.

  1. sieks-

    sieks-

    Joined:
    Aug 29, 2019
    Posts:
    11
    Hello,

    I have a question regarding the raycasting code found within the tutorial:
    Recorded Video Session: 2D Platformer Character Controller, at
    https://learn.unity.com/tutorial/li...character-controller#5c7f8528edbc2a002053b691.

    Specifically, this section, the Movement function:

    Code (CSharp):
    1. void Movement(Vector2 move, bool yMovement)
    2.     {
    3.         float distance = move.magnitude;
    4.  
    5.         if (distance > minMoveDistance)
    6.         {
    7.             int count = rb2d.Cast (move, contactFilter, hitBuffer, distance + shellRadius);
    8.             hitBufferList.Clear ();
    9.             for (int i = 0; i < count; i++) {
    10.                 hitBufferList.Add (hitBuffer [i]);
    11.             }
    12.  
    13.             for (int i = 0; i < hitBufferList.Count; i++)
    14.             {
    15.                 Vector2 currentNormal = hitBufferList [i].normal;
    16.                 if (currentNormal.y > minGroundNormalY)
    17.                 {
    18.                     grounded = true;
    19.                     if (yMovement)
    20.                     {
    21.                         groundNormal = currentNormal;
    22.                         currentNormal.x = 0;
    23.                     }
    24.                 }
    25.  
    26.                 float projection = Vector2.Dot (velocity, currentNormal);
    27.                 if (projection < 0)
    28.                 {
    29.                     velocity = velocity - projection * currentNormal;
    30.                 }
    31.  
    32.                 float modifiedDistance = hitBufferList [i].distance - shellRadius;
    33.                 distance = modifiedDistance < distance ? modifiedDistance : distance;
    34.             }
    35.  
    36.  
    37.         }
    38.  
    39.         rb2d.position = rb2d.position + move.normalized * distance;
    40.     }
    My question perhaps come from my misunderstanding of the Raycast...why is he looping through each member of the list? The 'distance' value upon which he relies on to move is reset each iteration, therefore only the last one is important, so why bother looping through all of them? Are they sorted in reverse order, ie closest contact last? It would still make looping through them pointless, but at least make choosing the last value appear rational.

    Thanks for any help clearing this up.
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    2,018
    I've not been through that tutorial myself but I can hopefully help a little.
    First thing and sorry for being nitpicky but it's not a raycast, it's a shape cast. Specifially it's casting all attached Collider2D on the Rigidbody2D and returning all results. That said, it does return the standard results type of RaycastHit2D which would seem contradictory to what I just said. :)

    All 2D physics queries that return results with distance (RaycastHit2D) are sorted by ascending distance i.e. first result is first hit.

    Not being familiar with this tutorial I don't know where this "velocity" things is coming from. I don't see it defined. I also don't follow the logic of what it's trying to achieve.

    It may not have been the case when this tutorial was written however now, all 2D physics queries allow results to be passed into a List<T> whose capacity is automatically increased if required. This saves having to reuse oversized arrays.

    The use of "rb2d.position" is bad as it instantly teleports the body to the new position which also bypasses interpolation and can cause collision tunnelling. It should use Rigidbody2D.MovePosition instread.
     
    Last edited: Nov 8, 2019
    sieks- and eses like this.
  3. sieks-

    sieks-

    Joined:
    Aug 29, 2019
    Posts:
    11
    1] The logic is used within a 2D sidescroller. The remaining code can be found at the link I posted in the original comment. It is done so that the player can move on slanted surfaces without moving 'into' the surface. A shape cast (thank for you the distinction!) is cast at the non-modified moving distance and a collision check is performed. The normal from the contact point defines the vector from which the character will move.

    2] Yes, I am aware that directly modifying the rigidbody's position, or rigidbody.transform.position is bad practice as this avoids proper collision checking. I am surprised both that I didn't notice that, and that this is used within an official Unity tutorial...


    But yes, as you said and I thought was most natural ordering, the RaycastHit array stores each object collided within the ray length in ascending order. This makes my original question more puzzling. Why bother looping through EACH object in that array when it is known the first object is the closest?
     
  4. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    1,361
    Hi @sieks-

    "Why bother looping through EACH object in that array when it is known the first object is the closest?"

    I did watch that tutorial at some point I think, IIRC it was pretty puzzling how the collision and sliding was created.

    If I read the code correctly...

    One reason for looping might be this part, not because of distance but for setting the grounded state:
    Code (CSharp):
    1. if (currentNormal.y > minGroundNormalY)
    2. {
    3.      grounded = true;
    4.      //...
    5. }
    Grounded doesn't get reset in each iteration of the loop. So think about a case where character is falling down.

    Player is close to the ground and allowed technically to be "grounded" based on the distance to ground... but the first hit is to a diagonal wall. This wall is too steep to allow player to be grounded. So only the second closest hit will qualify for setting grounded state true.

    In this kind of case grounded would be set only when the second closest hit's normal is checked:

    rbcast_collision.PNG
     
    sieks- likes this.
  5. sieks-

    sieks-

    Joined:
    Aug 29, 2019
    Posts:
    11
    @eses Yes! Good catch, this makes sense. After reading your comment it clicked for me. Thank you for the explanation :)