Search Unity

Vector3.Reflection not working as it should

Discussion in 'Physics' started by dccoo, Feb 15, 2018.

  1. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    I'm throwing a ball so that it's going to hit 2 walls.

    When it hits the walls far from the corner, it behaves correctly, but when it's thrown near the corner (with a relatively fast speed), it behaves wrongly, like in the image below.

    The blue and red arrows tell where the ball go, the green arrow at the WRONG cases tells where it should go.

    I guess the problem occurs because the ball hits 2 walls very fast so Unity gets confused with the wall's normal, but I don't know how to solve it.

    upload_2018-2-15_16-17-9.png

    This is the code I use for reflection:
    Code (csharp):
    1. void FixedUpdate () {
    2.     lastVelocity = myRigidbody.velocity;
    3. }
    4.  
    5. void OnCollisionEnter(Collision col){
    6.     if (col.gameObject.tag == "wall") {
    7.         ContactPoint cp = col.contacts [0];
    8.         myRigidbody.velocity = Vector3.Reflect (lastVelocity, cp.normal);
    9.     }
    10. }
     
    Last edited: Feb 15, 2018
  2. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,518
    Might be due to the fact that you only set the velocity in FixedUpdate.

    Try calculating it into a cached vector, then applying that vector in FixedUpdate also.
     
  3. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    Could you explain? What do you mean by "cached vector" and "applying that vector in FixedUpdate" ?
     
  4. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,518
    Code (csharp):
    1.  
    2.         private Vector3 lastVelocity;
    3.         private Vector3 goalVelocity;
    4.         private Rigidbody myRigidbody;
    5.  
    6.         void FixedUpdate()
    7.         {
    8.             lastVelocity = myRigidbody.velocity;
    9.             myRigidbody.velocity = goalVelocity;
    10.         }
    11.  
    12.         void OnCollisionEnter(Collision col)
    13.         {
    14.             if (col.gameObject.tag == "wall")
    15.             {
    16.                 ContactPoint cp = col.contacts[0];
    17.                 goalVelocity = Vector3.Reflect(lastVelocity, cp.normal);
    18.             }
    19.         }
    something like that... Not sure if lastVelocity is necessary or if it even will work this way. I would probably just use myRb.velocity directly instead.
     
  5. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    didn't work, same problem...
     
  6. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,518
    Well, I can tell you that Vector3.Reflect works fine, so it's something else. Likely something with the way velocity is being used.
     
  7. newjerseyrunner

    newjerseyrunner

    Joined:
    Jul 20, 2017
    Posts:
    966
    I would think the issue would be with your speed. If it hits both walls of the corner in the same frame, Unity may get confused. I would recommend for fast moving objects such as bullets, that you raytrace them.
     
  8. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,154
    I am also only guessing here...

    I would debug.log in the oncollisionenter
    - number of contact points
    - debug.break

    This way you can step through what happens at collision time. It could be the case that you have multiple contact points but only use the first one or a sequence of collisions. Hard to tell what is happening based on the info shared so far
     
  9. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    the OnCollisionEnter is called twice, I debugged to verify, even the angles seem to be correcty, but instead of coming back correctly, it follows the wall just like the figure, I'll try to debug the velocity...
     
  10. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,154
    Sounds like you are on the right track...

    I would still look at the number of contact points and given it is called twice velocity is changed after 1st collision (so not the one visible in your schematic drawing)

    A fix is possibly to ensure separation after 1st collision (for a quick n dirty test you could simply dismiss a follow up collision for a few ms)
     
  11. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    I did this now, I debugged which walls were touched (2 walls touched, correctly done) and the contact points.
    There was only one contact point for each hit, but it was the same for both hits, it seems strange...
     
  12. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    OnCollisionEnter and friends will occur after a fixed update and after the velocity has already died, so by the time you get it, FixedUpdate should have already happened according to the PhysicsMaterial that's been set (bounce, slide etc). So querying rigidbody.velocity should not actually be correct, or rather will give undesired results. You can probably get around it by using inverse relativeVelocity in OnCollisionEnter for reflect.

    There are better ways to deal with this with ComputePenetration or raycasts and so on, rather than allowing the collision response to alter the velocity to begin with.
     
  13. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    I tried this:
    Code (csharp):
    1. void OnCollisionEnter(Collision col){
    2.     ContactPoint cp = col.contacts [0];
    3.     rb.velocity = Vector3.Reflect (-col.relativeVelocity, cp.normal);
    4. }
    but I got the same mistake, when the ball hits the corner, it runs parallel to the the wall, it works just like the image I posted.
     
    hms0589 likes this.
  14. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    If I would work with raycast, what would I do? Suppose I throw a ray and get the vector3 V that the ball will follow after the collision, I would only be able to do rigidbody.velocity = V in OnCollisionEnter, otherwise where would I call it?
     
  15. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    I was able to solve by doing that:
    Code (csharp):
    1. void FixedUpdate(){
    2.     lastVelocity = myRigidbody.velocity;
    3. }
    4.  
    5. void OnCollisionEnter(Collision col){
    6.     if (col.gameObject.tag == "wall") {
    7.         ContactPoint cp = col.contacts [0];
    8.         lastVelocity = Vector3.Reflect (lastVelocity, cp.normal);
    9.         myRigidbody.velocity = lastVelocity;
    10.     }
    11. }
     
    KenjxZ, maliceCO and cychoi like this.
  16. unity_96aman96

    unity_96aman96

    Joined:
    Nov 3, 2018
    Posts:
    2
    Did you find a fix. Please share it if you did.
     
  17. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    read the full post.
     
  18. MitchStan

    MitchStan

    Joined:
    Feb 26, 2007
    Posts:
    568
    Came across this today because I was having a similar issue. Sorry about necro'ing an old post. Is 2018 old enough to qualify as a necro?

    Anyway, this works for me with no need to get the lastVelocty = myRigidbody.velocity from FixedUpdate. You can get the relativeVelocity of the collision from within OnCollisionEnter --

    Code (CSharp):
    1. private void OnCollisionEnter(Collision collision)
    2.     {
    3.         if (collision.gameObject.CompareTag("Wall"))
    4.         {
    5.             ContactPoint contactPoint = collision.contacts[0];
    6.             Vector3 newVelocity = (Vector3.Reflect(-collision.relativeVelocity, contactPoint.normal));
    7.             myRigidbody.AddForce(newVelocity);
    8.         }
    9.     }
    Make sure to negate the value of collision.relativeVelocity.
     
    Last edited: Jan 5, 2022
    dzozulya3377 and Foksnor like this.
  19. humadi2001

    humadi2001

    Joined:
    Feb 28, 2020
    Posts:
    2
    I've got the same problem and not sure what exactly was the problem but this code worked just fine with me

    private void OnCollisionEnter2D(Collision2D col)
    {
    Reflect();

    void Reflect()//local method
    {
    var reflect = Vector2.Reflect(lastVelocity, col.contacts[0].normal);
    rb.velocity = reflect.normalized * speed;

    lastVelocity = rb.velocity;//assigned right after reflection
    }
    }