Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Resolved I need help with some math (RayCast2D)

Discussion in '2D' started by EdoC93-QWERTY, Sep 15, 2020 at 9:23 PM.

  1. EdoC93-QWERTY


    Feb 1, 2020

    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.


    Thanks in advance
  2. TimmyTheTerrible


    Feb 18, 2017
    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


    Unity Technologies

    May 24, 2013
    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.
  4. EdoC93-QWERTY


    Feb 1, 2020
    Thanks both for the help.

    It seems there is a RayCastAll for 2D
    So i should use one of these functions:

    Code (CSharp):
    3. Physics2D.Raycast(Vector2 origin, Vector2 direction, ContactFilter2D contactFilter, List<RaycastHit2D> results, float distance = Mathf.Infinity);
    5. Physics2D.RaycastAll(transform.position,transform.up,RaycastHit2D[] results);
    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 ( 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?

  5. MelvMay


    Unity Technologies

    May 24, 2013
    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. EdoC93-QWERTY


    Feb 1, 2020

    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.

  7. MelvMay


    Unity Technologies

    May 24, 2013
    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. EdoC93-QWERTY


    Feb 1, 2020
    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.

  9. MelvMay


    Unity Technologies

    May 24, 2013
    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. EdoC93-QWERTY


    Feb 1, 2020
    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


    Unity Technologies

    May 24, 2013
    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 at 2:17 PM
  12. EdoC93-QWERTY


    Feb 1, 2020
    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.     {
    4.         if(OnLaserRayCastEvent!= null)
    5.         {
    6.             OnLaserRayCastEvent();
    7.         }
    8.         _linerenderer.enabled =true;
    9.         _edgecollider.enabled =true;
    11.         ContactFilter2D contactFilter = new ContactFilter2D().NoFilter();
    15.         if(_boxSigns.Length ==0)
    16.         {
    17.             //hitresults = Physics2D.Raycast(transform.position,transform.up);
    19.             hit = Physics2D.Raycast(transform.position,transform.up);
    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.         }
    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 );
    45.             for (int i = 0; i < hitList.Count; i++)
    46.             {
    47.                 _lasercollidList.Add( hitList[i].collider.gameObject);
    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);
    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);    
    70.                     }
    71.                 }
    72.             }
    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);
    83.                 _linerenderer.SetPosition(1, _laserhitpoint);
    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);
    92.                 _linerenderer.SetPosition(1, _laserhitpoint);
    93.         }
    96.        // Debug.Log("hitobj   "+hit.collider.gameObject);
    99.         SetCollider();
    101.         Debug.DrawRay(transform.position,transform.up,, 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.