Search Unity

Bug with Circle Cast

Discussion in '2D' started by DaCookie, Feb 28, 2016.

  1. DaCookie

    DaCookie

    Joined:
    Nov 4, 2014
    Posts:
    44
    Hi everybody.
    I think there's a problem with the Physcis2D.CircleCast() method.

    I do a CircleCast from my Character to know if there's something forward it. But, if I hit a wall and go in the other direction, sometimes, the CircleCast hit my Character ! I disabled the option "Queries Start In Colliders" in Physics 2D, tried to use it in all kind of Updates, tried to use an offset for the origin of the CircleCast... nothing changes.

    You'll have to try more than once to make the bug happen, it's occasional.

    That's my script. I made a project just to show you what happens if you don't want to make it :
    https://drive.google.com/open?id=0BylYgxxY1FtpUWI1X05Rc282dm8

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Character : MonoBehaviour
    4. {
    5.  
    6.     private Rigidbody2D m_Rigidbody = null;
    7.  
    8.     [Header("Settings")]
    9.  
    10.     [SerializeField]
    11.     private float m_Acceleration = 1500.0f;
    12.  
    13.     [SerializeField]
    14.     private float m_ObstacleDetectionRange = 1.0f;
    15.  
    16.     [SerializeField]
    17.     private float m_ObstacleDetectionRadius = 0.3f;
    18.  
    19.     private void Awake()
    20.     {
    21.         m_Rigidbody = GetComponent<Rigidbody2D>();
    22.     }
    23.  
    24.     /*
    25.      * Nothing different if used in Update().
    26.      */
    27.  
    28.     private void FixedUpdate()
    29.     {
    30.         CheckMovement(Time.fixedDeltaTime);
    31.     }
    32.  
    33.     /// <summary>
    34.     /// Check the movement input
    35.     /// </summary>
    36.     private void CheckMovement(float _DeltaTime)
    37.     {
    38.         float horizontalMovement = Input.GetAxisRaw("Horizontal");
    39.  
    40.         Move(Vector3.right * horizontalMovement, _DeltaTime);
    41.     }
    42.  
    43.     /// <summary>
    44.     /// Make this Character move if it can.
    45.     /// </summary>
    46.     private void Move(Vector3 _Direction, float _DeltaTime)
    47.     {
    48.         if (_Direction != Vector3.zero && CanMove(_Direction))
    49.         {
    50.             if (m_Rigidbody != null)
    51.             {
    52.                 m_Rigidbody.AddForce(_Direction * m_Acceleration * _DeltaTime);
    53.             }
    54.  
    55.             else
    56.             {
    57.                 Debug.LogWarning("No Rigidbody 2D on Character.");
    58.             }
    59.         }
    60.     }
    61.  
    62.     /// <summary>
    63.     /// Check if there's no obstacles in the given direction.
    64.     /// </summary>
    65.     private bool CanMove(Vector3 _Direction)
    66.     {
    67.         _Direction.Normalize();
    68.  
    69.         RaycastHit2D hitResult = Physics2D.CircleCast(transform.position, m_ObstacleDetectionRadius, _Direction, m_ObstacleDetectionRange);
    70.  
    71.         Debug.Log("Hit = " + hitResult.collider);
    72.  
    73.         return (hitResult.collider == null);
    74.     }
    75.  
    76. }
    Thanks for any help ;)
     
  2. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    hi,

    I didn't read your code (well, I did read one line related to this question) but have you tried just using circleCast with specified layerMask ?

    That way you can completely avoid hitting your raycast source.
     
  3. DaCookie

    DaCookie

    Joined:
    Nov 4, 2014
    Posts:
    44
    Yeah, I tried to do it. A good solution is to ignore the collider of my character in its Awake, but there I don't want to work around the problem, but to solve it.

    I can't use a layer mask, because in my game my character has to avoid collision with itself, but not with other characters.
     
  4. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @DaCookie - maybe just make your layerMask configured in different manner, so that player collides with characters too?
     
  5. DaCookie

    DaCookie

    Joined:
    Nov 4, 2014
    Posts:
    44
    But I want all my characters can detect all characters but themselves... I don't think there's a way to do that with Layers and Physics settings :/
     
  6. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @DaCookie - sure you can:

    If you are controlling the only character that needs to raycast others:
    1. Place your player(s) in Layer "Player". Place your other characters in Layer "OtherCharacters".
    2. In Physics settings, un-tick Player-Player collision. But keep Player-OtherCharacters on.
    3. In your sphere cast, use "OtherCharacters" as layerMask.

    Then again, if this test is going to happen from perspective of any character, then maybe just change the layer of that character temporarily, runtime when that character does raycast?
     
  7. DaCookie

    DaCookie

    Joined:
    Nov 4, 2014
    Posts:
    44
    This solution is good but... Absolutely twisted !

    I just want to avoid the collision with myself at CircleCast, and that behaviour HAVE TO be the normal behaviour... So should I use a solution like this or be sure this is a bug (and will be resolved ;) )?
     
  8. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @DaCookie

    Well, I just mentioned these without thinking much more further - maybe they might help you... or not.

    Then there is Physics2D.CircleCastAll -> you could just use it and ignore raycaster itself in each case based on name/tag whatever.

    Or use transformPoint to offset start of the raycast in front of your character.

    Or use what you were already using, do you really have to capture each/every raycast, does it actually matter, if x is missed and one after it is used?
     
  9. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    CircleCast casts a circle from a point in a direction given a distance. It obviously doesn't know where your player is and can't distinguish it which is what the layer-mask filter is for i.e. asking only for things on specific layers.

    QueriesStartsInColliders does indeed control whether a collider overlapping the shape at the start point (transform.position in your case) should be returned or not so yes, can be relied upon to not return an initial overlap but can be dangerous if there's an overlap that you are interested in. If you're then saying this sometimes doesn't work then that is a bug I'd be interested in seeing reproduced in a simple project for sure.

    We have a test internally that performs checks on all the casts which also checks 'QueriesStartsInColliders' and that isn't showing any problems so I'd certainly be interesting in finding any problem there.
     
  10. DaCookie

    DaCookie

    Joined:
    Nov 4, 2014
    Posts:
    44
    Did you try to open my example project that I post ? That's the new version (with gizmos and settings in editor) :

    https://drive.google.com/open?id=0BylYgxxY1FtpeDZrS3ZUWWc5NjA

    I still have the same problem in. It's quite simple to reproduce the "bug". In the Testing scene, you'll see a Sphere and two Walls. You can move the Sphere using arrows.

    The sphere cast a CircleCast from it in Vector3.right direction (blue gizmo). The Sphere can't move in the direction where something is hit by the Circle cast (so if you're going in a wall that you're too close, the Sphere won't be able to move).

    Go against a wall. Then, press arrow in the opposite direction and quickly press the other direction. Now, the CircleCast must hit Character, the Sphere.
    I thought it's because I'm using FixedUpdate(), but it do the same with Update().

    If the Gizmo is blue, you can move, else, it's red. The GUI will say what is hit.
    Enjoy :p
     
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Today I tried your example and sure enough, CircleCast is not excluding your circle which it should do being as 'QueriesStartsInColliders' is off and the 'Character' circle and the starting point of the cast obviously overlap.

    For the casting we use Box2Ds 'b2TimeOfImpact' call which uses 'b2Distance' which returns a distance/time until impact and a state of either 'b2TOIOutput::e_touching' with a distance > 0 or 'b2TOIOutput::e_overlapped' with a distance <= 0. In this case, we'd expect a 'b2TOIOutput::e_overlapped' but Box2D is returning 'b2TOIOutput::e_touching' with a fractionally-above-zero distance which is obviously wrong.

    We changed from using an explicit overlap check at the start point as it produced inconsistent results from the cast for various reasons and now rely on the state returned above. Seems like there's a flaw in Box2Ds calculation here.

    The obvious workaround is to use the mask as highlighted above and as stated, 'QueriesStartsInColliders' doesn't mean don't hit the character, only anything that initially overlaps the start point which might end-up being something you *do* want i.e. a wall or enemy if you are using discrete collision-detection. You must use layers to ensure you get the correct results. Just taking the 'character' layer out of the mask will get everything but the character. There's zero reason not to do that.

    I will however investigate where exactly the Box2D 'b2TimeOfImpact' is going wrong here and if it only affects circle-shapes or not.
     
    Last edited: Mar 6, 2016
  12. DaCookie

    DaCookie

    Joined:
    Nov 4, 2014
    Posts:
    44
    Thanks a lot to take care of your users :p

    If you don't use this post, where can I know the results of that investigation ?
     
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    If a fix because of this becomes available, I'll post back here as this thread is linked to the issue.

    EDIT: I've found what the problem is and will try to resolve the issue next week.
     
    Last edited: Mar 6, 2016
  14. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    I've fixed this issue. I'll get it into trunk and then into the back-port release stream ASAP. Normally takes a few weeks before it goes public.
     
    eses likes this.
  15. DaCookie

    DaCookie

    Joined:
    Nov 4, 2014
    Posts:
    44
    ... Too efficient for me :p
     
  16. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    It was a quiet Sunday. ;)

    Note that with the fix in-place, the test project still has a flaw in its movement behaviour in that it will detect a wall and stop up to the distance you're casting. To allow the character to get right next to the wall, you need to check that the distance is above a fairly small value (or zero) so in your 'CanMove' method:

    Instead of:
    Code (CSharp):
    1. return (m_HitCollider == null);
    ... you should use ...
    Code (CSharp):
    1. return !hitResult || hitResult.distance > 0.0f;
     
  17. DaCookie

    DaCookie

    Joined:
    Nov 4, 2014
    Posts:
    44
    You're right, I'm doing it in my real project, but I missed it in my example, sorry.
     
  18. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Ah, that makes sense. I just wanted to check.