Search Unity

What is executed before OnCollisionEnter? Physic evaluation bug ?

Discussion in 'Scripting' started by Craze74, Nov 20, 2019.

  1. Craze74

    Craze74

    Joined:
    Nov 19, 2012
    Posts:
    83
    Hello folks!

    I have an issue with a rigidbody these days, an arrow to be exact.

    When I shoot the arrow, I wanted it to freeze whenever it collides with a piece of environment, however I noticed that on the collision, the arrow was sometimes ( depending on the angle it has been fired I guess ) being rotated right after hitting the collider.

    What I tried to do is in my method void OnCollisionEnter(), freezing the timeScale and put it to 0.

    Problem is : this weird rotation happening all of a sudden at collision is still there... and still happening before the time freezes, meaning something is happening even before the OnCollisionEnter() is called... but what ?

    Any thoughts ?

    Thanks !
     
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    My first thought would be that the arrow is being deflected by the collision itself (physical objects that collide with each other tend to change path). Could you turn one of the colliders into a trigger, so that regular collision physics don't apply?
     
  3. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    What's happening is basically physics. The exact same physics that are being used to call OnCollisionEnter, must obviously be calculated before OnCollisionEnter is called (otherwise how would it know to call it?)

    Actually it depends on when in the physics frame (which lasts 0.02 seconds by default) it actually hits the wall.

    The physics frame is calculated all at the same time. And so, in that 0.02 seconds, the arrow would not only hit the wall (say, 0.05s into the physics frame), but then it bounces, and the remaining 0.15s of the physics frame it's rotating and moving away. Then, it calls OnCollisionEnter.

    The best way to handle this is:
    A) in FixedUpdate, store the velocity of the arrow.
    B) in OnCollisionEnter, set the position of the arrow to collision.contacts[0].point, and set the rotation to LookRotation(lastStoredVelocity)

    Or make one of the colliders a trigger, as @Antistone suggests, though the disadvantage of that is that you wouldn't get detailed collision information (like where exactly the arrow struck). If you need that kind of information, you probably want to use the solution written here.

    ~~~

    (hypothetical tangent follows that you don't really need to read if you don't want to)

    There's one possibility that I've never thought about before that may happen - I'm not sure and I've never tried, but it could be a thing. When Unity is running physics/FixedUpdate, it doesn't really run them at fixed time intervals; rather, every frame, it runs enough FixedUpdates to catch up to whatever the real time is. So, if your game is running at 20fps, each frame will take 0.05 seconds, and you'll be getting 2 or 3 FixedUpdates and physics frames being run one after the other on that same frame.

    Now here's the thing: If you have 3 FixedUpdate/physics frame slated to run during a given regular frame, and you changed Time.deltaTime on the first of those frames(in this case, because your arrow collided with the wall on that frame), I don't know what the expected behavior is. It might be (option A) that Unity continues to execute the remaining FixedUpdates it had always planned to, because as far as Unity is concerned, that time has already passed - physics is just catching up. Or, (option B) Unity might stop executing physics/FixedUpdate and alter the Time.deltaTime of the next frame. I don't know. If it were me coding it, it'd probably be option A, because writing the algorithm to work out the time changes for option B sounds terrifying.

    This would be easy to test in your scenario. Set Time.timeScale to something ridiculous, like 20 or 50. That way, you'll be guaranteed to have lots of physics frames per game frame. Now shoot your arrow a few times. If Option A is true, then you'll have more physics frames continuing to run before the new Time.timeScale takes effect, and your bouncing/rotating effect will be more significant. If option B is true, then the magnitude of the issue you're seeing will be unaffected.

    The good news is, in either case, both solutions listed above are still valid solutions, hence the disclaimer at the top of this that you don't really need to read all of this. It's a really interesting edge case that I've never considered.
     
    eisenpony likes this.
  4. Craze74

    Craze74

    Joined:
    Nov 19, 2012
    Posts:
    83
    It will work indeed, but then that means I could not have proper collisions happening if I want to have some exceptions and not freezing the arrow if let's say it encounter an enemy right ?
     
  5. Craze74

    Craze74

    Joined:
    Nov 19, 2012
    Posts:
    83

    It actually makes sense indeed...



    Well I guess option A is imo what is most likely to happen it seems more... logical ?

    In any way that's something I would like to test out, thanks for the advices, I will not miss posting results on this if that can help anyone !
     
  6. Craze74

    Craze74

    Joined:
    Nov 19, 2012
    Posts:
    83
    I just did the test, it seems that it is actually Option B being in place...
     
    eisenpony likes this.
  7. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Interesting. Thanks for checking!