- Home /
Inconsistent Reflection/Ricochet of 2D Lines at Specific Angles via Raycasting and 2D Colliders
Hello!
I am currently implementing a rotating beam weapon for a 2D game which reflects/ricochets from its original point of contact with a 2D collider and continues until hitting another 2d collider.
Using the initial 2D raycast hit to begin another raycast with its direction obtained via reflection I have only had success when the direction of the initial raycast hits colliders at specific angles.
I've hit a roadblock finding an explanation for this behaviour, and would appreciate any insight into what is causing it and how it can be fixed.
My code is below, and here is a video of the problem in action.
public class Laser : MonoBehaviour
{
public Transform laserHit; // point where laser initially hits
public Transform laserRicochetHit; // point where laser ricochet hits
private LineRenderer lr;
void Awake ()
{
lr = GetComponent<LineRenderer>();
lr.enabled = true;
lr.useWorldSpace = true; // world origin instead of the parent object origin
lr.numPositions = 3; // 0 - origin, 1 - initial wall hit, 2 - ricochet hit
}
void Update()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, transform.parent.up);
laserHit.position = hit.point;
// create a ricochet line via raycasting
Vector2 ricoDir = Vector2.Reflect(transform.parent.up, hit.normal); // direction for ricochet raycast
RaycastHit2D ricochetHit = Physics2D.Raycast(hit.point, ricoDir);
laserRicochetHit.position = ricochetHit.point;
// set line positions
lr.SetPosition(0, transform.position); // origin
lr.SetPosition(1, laserHit.position); // initial wall hit
lr.SetPosition(2, laserRicochetHit.position); // ricochet hit
// draw debug lines
Debug.DrawLine(transform.position, hit.point);
Debug.DrawLine(hit.point, ricochetHit.point);
}
}
I am new to Unity and C#, and would appreciate any and all comments. Thanks!
That code looks good, especially for a beginner.
Can you post a screenshot of the scene view? What is the raycast shooting at? Are there colliders on the edges of the screen?
Sure thing @FortisVenaliter!
The "Walls" of the screen are 4 intersecting cubes with 2d box Collider components.
Answer by FortisVenaliter · Apr 07, 2017 at 04:29 PM
From the documentation for raycast:
Additionally, this will also detect Collider(s) at the start of the ray. In this case the ray is starting inside the Collider and doesn't intersect the Collider surface. This means that the collision normal cannot be calculated in which case the collision normal returned is set to the inverse of the ray vector being tested. This can easily be detected because such results are always at a RaycastHit2D fraction of zero.
So, what you probably need to do, is get the initial ray start point out of your starting collider. Try disabling the collider on the cube to confirm this is the problem.
No problems with the initial raycast exiting the cube which emitting the beam. This does explain the issue though. It appears the initial hit position is landing within the wall's collider when approaching from most directions. Therefore, my ricochet raycasts are unable to obtain the correct normal and go nowhere.
Thanks for this, I'll read the documentation more closely next time.
With this information I have a leg up on finding a solution, but would be welcome to anyone else's solutions of course! I'll post my own provided I'm able to come up with one myself.
You should just be able to offset the ricochet position by a small fraction of the collision surface normal. That should push it far enough to not collide.
Yep! The adding hit.normal to one line was enough to fix the behavior:
RaycastHit2D ricochetHit = Physics2D.Raycast(hit.point + hit.normal, ricoDir);
Thanks for all your help @FortisVenaliter
Here's the complete implemented fix in case anyone else is facing the same issue in the future.
public class Laser : $$anonymous$$onoBehaviour
{
public Transform laserHit; // point where laser is hits
public Transform laserRicochetHit; // point where laser ricochet
private LineRenderer lr;
void Awake ()
{
lr = GetComponent<LineRenderer>();
lr.enabled = true;
lr.useWorldSpace = true; // world origin ins$$anonymous$$d of the parent object origin
lr.numPositions = 3; // 0 - origin, 1 - initial wall hit, 2 - ricochet hit
}
void Update()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, transform.parent.up);
laserHit.position = hit.point;
// create a ricochet line via raycasting
Vector2 ricoDir = Vector2.Reflect(transform.parent.up, hit.normal); // direction for ricochet raycast
RaycastHit2D ricochetHit = Physics2D.Raycast(hit.point + hit.normal, ricoDir);
laserRicochetHit.position = ricochetHit.point;
// set line positions
lr.SetPosition(0, transform.position); // origin
lr.SetPosition(1, laserHit.position); // initial wall hit
lr.SetPosition(2, laserRicochetHit.position); // ricochet hit
// draw debug lines
Debug.DrawLine(transform.position, hit.point);
Debug.DrawLine(hit.point, ricochetHit.point);
}
}
Answer by imaginationrabbit · Jun 01, 2019 at 01:26 AM
The answer above is really useful but more specifically to solve this you need to modify the hit normal you are giving to the Reflect- all I did was create a new variable for the hitnormal I gave the Reflect by adding the hitnormal + hitpoint- now it works as it should.
Your answer
Follow this Question
Related Questions
2D Collider larger than specified 0 Answers
Top down shooter: Laser Sight 1 Answer
RaycastHit2D to hit an enemy when player rotates. (Using keyboard input only) 2 Answers
2D Raycast not working 1 Answer
LineRenderer (Laser Beam) is not following the ray it's going on the wrong direction when reflecting 1 Answer