Search Unity

Resolved I need help with some math (RayCast2D)

Discussion in '2D' started by EdoC-QWERTY, Sep 15, 2020.

  1. EdoC-QWERTY

    EdoC-QWERTY

    Joined:
    Feb 1, 2020
    Posts:
    75
    Hello,

    I need to find a point in space that is on the same line of a ray cast after it has collided with an object.

    I'll try to explain better. In my game there are lasers, that act kinda like a wall so it has an edge collider inside.
    To set the laser line i use a line renderer and a raycast, i create a ray and then i change the points of the collider and line rencerer based on where the laser collided with an object.

    I have also some special doors and the laser can pierce trough them.

    My problem was to find a way to ignore the collision between the laser and the door.
    At first i tried with "IgnoreCollision()" but it does not work. So i found out another way.

    If the laser hit the door, i cast a second ray from the laser hit point plus 0.5. It works! But i only do it for 4 directions.

    If the laser face LEFT the value is -0.5
    If the laser face RIGHT is 0.5

    Same for UP and Down.
    But the laser can rotate to any angle.

    How can i create a custom function that returns a Vector2 that would be the origin of the second raycast based on the laser rotation?

    I Made this is Paint, maybe it helps to better understand.

    UnityLaserHelpimg.png

    Thanks in advance
     
  2. TimmyTheTerrible

    TimmyTheTerrible

    Joined:
    Feb 18, 2017
    Posts:
    186
    you could use Physics.RaycastAll to find all of the objects the ray intersects. then sort them by distance, and if the first object hit can be pierced by the lazer, then continue onward to the next returned collision.
     
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    Please note that call is 3D physics not 2D physics. For 2D physics simply use the Physics2D.Raycast that accepts an array or list<T> for multiple results.

    If you perform a raycast that returns multiple results then the results are returned in order with closest hit as first result. This means the first result is your first red line. To get the second red line you can then perform a single raycast from the hit-point of the 2nd result back along the original ray direction to the start point. This will give you the exit point. You can then proceed to do this for the 3rd, 4th, 5th results etc. Noet that this process will only work for convex objects.
     
    SlimeProphet likes this.
  4. EdoC-QWERTY

    EdoC-QWERTY

    Joined:
    Feb 1, 2020
    Posts:
    75
    Thanks both for the help.

    It seems there is a RayCastAll for 2D https://docs.unity3d.com/ScriptReference/Physics2D.RaycastAll.html
    So i should use one of these functions:

    Code (CSharp):
    1.  
    2.  
    3. Physics2D.Raycast(Vector2 origin, Vector2 direction, ContactFilter2D contactFilter, List<RaycastHit2D> results, float distance = Mathf.Infinity);
    4.  
    5. Physics2D.RaycastAll(transform.position,transform.up,RaycastHit2D[] results);
    6.  
    7.  
    I tired to use the List function, but i can't manage to make it work, on visual studio i get an error.
    In the manual page (https://docs.unity3d.com/ScriptReference/Physics2D.Raycast.html) there is no example how to use the RayCast with a List.
    In visual studio i get the error "The use of generic type List<T> requires arguments of type 1".

    This is the line i wrote.

    Code (CSharp):
    1. Physics2D.Raycast(transform.position,transform.up, List<RaycastHit2D> hitList);
    Can you show me how to use this?

    Thanks
     
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    Yes but it creates a list each time you call it which then gets picked up by the garbage collector hence passing in an array/list to be reused stops that. If you don't care then just use that method.

    There doesn't need to be because that would mean every single method needed to show how to do basic C# stuff like create an instance of an object (like a list).

    Sure but this is C# so you need to create the list first and pass it in. What you did is invalid C#. I mean I can show you how to create something in C# but if you're struggling with that I would highly suggest looking up some C# tutorials too.

    This is how you create it but it's the same for anything you want to create. Pass in MyResults. If you don't reuse this same object instance then it's pointless and you might as well use RaycastAll.
    Code (CSharp):
    1. List<RaycastHit2D> myResults = new List<RaycastHit2D>();
    If you're completely confused over this then just use RaycastAll.
     
  6. EdoC-QWERTY

    EdoC-QWERTY

    Joined:
    Feb 1, 2020
    Posts:
    75

    I know that. I found out what the issue was.

    I forgot to add the contact filter parameter so the function was expecting a value for "distance" and not a List.


    Thanks
     
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    Good that you know it but at the same time you suggested you were passing "List<RaycastHit2D> hitList" which is why I told you why that was wrong. I did assume you could see the docs and the args and their order required which is why I didn't go over that part.
     
  8. EdoC-QWERTY

    EdoC-QWERTY

    Joined:
    Feb 1, 2020
    Posts:
    75
    Yeah,
    at first i created the list instance and passed the list name but since i forgot to put the contact filter parameter i got the error, so i started to try other things like that wrong line i posted because i was freaking out, it's better not to do things like this when you are tired.

    Anyway I have another question that is related to the lasers.

    I have also some laser receivers, that activate some objects if a laser hit them.

    My problem is that OnCollisionEnter() is not triggered if a laser hits them. I think it's related on how i move the edge collider points.
    As i said before my laser also act as a wall and it has an edge collider inside with a radius of 0.05.

    So my code is the following, after the raycast hits an object i call this function

    Code (CSharp):
    1.     void SetCollider()
    2.     {          
    3.         Vector2[] edgecollpoints;
    4.         edgecollpoints = _edgecollider.points;
    5.         edgecollpoints[0] = new Vector2(0f,0f);
    6.         Vector2 localtoworldpoints = transform.InverseTransformPoint(_laserhitpoint);  
    7.         edgecollpoints[1] = new Vector2(localtoworldpoints.x,localtoworldpoints.y);
    8.         _edgecollider.points = edgecollpoints;
    9.     }
    This basically moves the collier points instantly and therefore the OnCollisionEnter() is not working.
    I also tried the OnCollisionStay and it's the same behavior, the collision event is not registered somehow.

    It there a way i can solve this?

    I already thought of some alternatives like coroutines and raycasts but i would like to use the collision events because it's better and simpler.

    Thanks
     
  9. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    You've typed this several times now so I'll mention that these are the 3D physics callbacks not the 2D physics callbacks. 2D physics callbacks all have a "2D" suffix on them as shown in all the docs/tutortials i.e. "OnCollisionEnter2D"

    Contacts are not calculated by the physics system anytime you change any property on any collider. They're only calculated when the simulation runs; something which you can control. By default this is during the FixedUpdate.
     
  10. EdoC-QWERTY

    EdoC-QWERTY

    Joined:
    Feb 1, 2020
    Posts:
    75
    My bad(i will try to be more precise next time) i used the correct callbakcs "OnCollisionEnter2D" and it does not work, same thing with OnCollisionStay2D() .

    The collision is not registered.

    How can i force it to register the collision?
    I used OnCollisionStay because it should run on every call of FixedUpdate right?
    It still does not work, it's like the colliders do not touch eachother.


    You are suggesting to use a different type of check, like collider.isoverlapping, or collider.istouching during FixedUpdate?

    I was told that those calls in FixedUpdate are expensive, because are called every 0.02 seconds.

    I may be wrong but the OnCollisionEnter2D seems more performing since are only called once.

    Any alternatives to OnCollisionEnter2D/OnCollisionExit2D ?
     
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    Changing points on an edge collider doesn't stop it working so try NOT changing the points. Does it still work? When and how often are you setting these points?

    Yeah, there's a lot of free advice out there. Unfortunately a lot of it, like this, is nonsense.
     
    Last edited: Sep 18, 2020
  12. EdoC-QWERTY

    EdoC-QWERTY

    Joined:
    Feb 1, 2020
    Posts:
    75
    I need to change the points, to test it without moving the points i have to move the LaserReceiver instead and it's not supposed to move in the game.

    I solved it with a workaround, i check if the laser hits a laserreceiver and then execute a public function to activate it from the laser script.

    But since the collision problem is still there.

    I can post my function but it's pretty long (104 lines) and i was told my code is weird because i use a lot of spaces, if you don't struggle to read it here is the RayCast function.

    Code (CSharp):
    1. void RaycastStart()
    2.     {
    3.  
    4.         if(OnLaserRayCastEvent!= null)
    5.         {
    6.             OnLaserRayCastEvent();
    7.         }
    8.         _linerenderer.enabled =true;
    9.         _edgecollider.enabled =true;
    10.  
    11.         ContactFilter2D contactFilter = new ContactFilter2D().NoFilter();
    12.  
    13.      
    14.  
    15.         if(_boxSigns.Length ==0)
    16.         {
    17.             //hitresults = Physics2D.Raycast(transform.position,transform.up);
    18.  
    19.             hit = Physics2D.Raycast(transform.position,transform.up);
    20.  
    21.         }
    22.         else
    23.         {
    24.             if(BoxesStatusActive())
    25.             {
    26.             // _lasercollidList.Clear();
    27.             hit = Physics2D.Raycast(transform.position,transform.up);
    28.             }
    29.             else
    30.             {
    31.             hit = Physics2D.Raycast(transform.position,transform.up,0.5f);
    32.             _linerenderer.enabled =false;
    33.             _edgecollider.enabled =false;
    34.             }
    35.         }
    36.      
    37.      
    38.         if(hit.collider.gameObject.tag == "Barrier")
    39.         {
    40.             //Debug.Log("BARRIER HIT");
    41.             _lasercollidList.Clear();
    42.             Physics2D.Raycast(transform.position,transform.up, contactFilter, hitList );
    43.              
    44.              
    45.             for (int i = 0; i < hitList.Count; i++)
    46.             {
    47.                 _lasercollidList.Add( hitList[i].collider.gameObject);
    48.          
    49.  
    50.  
    51.  
    52.                 if(hitList[i].collider.gameObject.GetComponent<Barrier>()!=null)
    53.                 {
    54.                     if(_lasercolor ==_lasercollidList[i].GetComponent<Barrier>().barrierColor)
    55.                     {
    56.                         Debug.Log("BARRIER SAME COLOR");
    57.                         _laserhitpoint = hitList[i+1].point;
    58.                         _linerenderer.SetPosition(0, transform.position);
    59.                         _linerenderer.SetPosition(1, _laserhitpoint);
    60.                     // Physics2D.IgnoreCollision(_edgecollider,_collidBarrier._edgecollider,false);
    61.                      
    62.                     }
    63.                     else
    64.                     {
    65.                         Debug.Log("BARRIER COLOR NOO");
    66.                         _laserhitpoint = hit.point;
    67.                         _linerenderer.SetPosition(0, transform.position);
    68.                         _linerenderer.SetPosition(1, _laserhitpoint);    
    69.  
    70.                     }
    71.                 }
    72.             }
    73.              
    74.         }
    75. // This if statement is the one i added for the workaround, i can delete it and use the LaserReceiver collision instead
    76.         else if(hit.collider.gameObject.tag == "LaserReceivers")
    77.         {
    78.             hit.collider.gameObject.GetComponent<Laserreciver>()._laserwall = this;
    79.             hit.collider.gameObject.GetComponent<Laserreciver>().OnLaserCollision();
    80.              _laserhitpoint = hit.point;
    81.                 _linerenderer.SetPosition(0, transform.position);
    82.                  
    83.                 _linerenderer.SetPosition(1, _laserhitpoint);
    84.  
    85.         }
    86.         else // Any other object is hit so laser stops
    87.         {
    88.             //Debug.Log("SetLaserPoint");
    89.                 _laserhitpoint = hit.point;
    90.                 _linerenderer.SetPosition(0, transform.position);
    91.                  
    92.                 _linerenderer.SetPosition(1, _laserhitpoint);
    93.         }
    94.      
    95.  
    96.        // Debug.Log("hitobj   "+hit.collider.gameObject);
    97.  
    98.          
    99.         SetCollider();
    100.  
    101.         Debug.DrawRay(transform.position,transform.up, Color.red, Mathf.Infinity,false);      
    102.     }

    I change the points after the raycast hits an object.

    I used unity events system to avoid shooting rays continuously on fixed Update.

    So any time an object is activated, for example a door is opened or closed, an event is triggered and the RayCastStart function is executed, then it checks the object type it collide with, then checks if the object can be pierced, then it sets the LineRenderer points, then it moves the collider points.

    It works with well with the doors (Barriers in the code) but with the Laserreceiver it does not.


    I
     
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    Impossible to debug a code listing but when you change a collider, you can instantly perform any query on it. Contacts via collision callbacks need the simulation to run after the change which then produces callbacks. Your code doesn't relate to the initial subject which was you saying you change the collider and don't get physics callbacks. The code just shows queries which are unrelated.

    Can't really help further as it's difficult to untangle what you're doing TBH.
     
    Last edited: Sep 19, 2020
  14. SlimeProphet

    SlimeProphet

    Joined:
    Sep 30, 2019
    Posts:
    50
    Off topic, but it's so incredible to see actual UT devs helping so patiently in the forums like this. The generosity and patience is incredible, I don't know of any other software with engagement like this.
     
    EdoC-QWERTY and MelvMay like this.
  15. EdoC-QWERTY

    EdoC-QWERTY

    Joined:
    Feb 1, 2020
    Posts:
    75

    The code i showed you it's from the Laser script, where i change the collider points.

    The OnCollisionEnter2D() that does not work is on the LaserReciver script.

    Maybe I explained it wrong.

    In the Laser script:
    I use the Event system to cast a ray after an object has been activated.(So that i avoid to cast ray repeatedly)
    Object.Changestateevent += RaycastStart;
    The RaycastStart() function shoot a ray and sets the LineRenderer points on the laser.
    At the end of RaycastStart i call "SetCollider()" that moves the points of the collider to match the LineRenderer points.


    In the LaserReceiver script:
    I have OnCollisionEnter2D() that does not register the collision with the laser collider.
    I still don't know why it does not register the collision.


    I solved it with a custom function in the LaserReceiver script (that basically
    substitute OnCollision2DEnter )

    public void OnLaserCollision();
    That is executed from the Laser script after the ray hits a LaserReceiver object.

    hit.collider.gameObject.GetComponent<Laserreciver>().OnLaserCollision();


    The simulation should be always running, i don't get it.
    It's like if i increase a collider's size at runtime it does not register collision callbacks if it's not in the FixedUpdate method?


    Thanks

    Yeah i am surprised too.
     
  16. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    There's nothing special about fixed-update. It's just a point in time. After all the script fixed-updates have been called, Unity calls the internal system fixed-updates which includes 2D physics. This is when the simulation (by default) happens and at the end of that the callbacks occur. If you made a change during the "Update" callback then you'd get callbacks next time an internal fixed-update i.e. a simulation occurs. There's no rules like "if you don't do it in fixed-update, it doesn't work" so don't worry about hidden rules like that. :)

    That said, your description here still leaves some unknowns to me. My quesiton would be; if you DON'T change the collider size do you get callbacks? If so, are you saying that you change the size and then callbacks stop? Or are you saying that callbacks somehow don't happen when you expect them? I'm triying to narrow down exactly what is happening accurately.

    If you have a simple reproduction project or could produce one then feel free to host that and send me a link. Alternately if you want me to securely host it for you then DM me with your email address, I'd be happy to create a private workspace only we can see. At least that way I can see it for myself and advise you accordingly to save all this back/forth.
     
  17. EdoC-QWERTY

    EdoC-QWERTY

    Joined:
    Feb 1, 2020
    Posts:
    75
    I'll DM you.

    Btw thank you very much for your help and availability.
     
    MelvMay likes this.