Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Handling physics updates in Coroutine

Discussion in 'Physics' started by StillPossible, Jun 19, 2023.

  1. StillPossible

    StillPossible

    Joined:
    Apr 19, 2022
    Posts:
    4
    Up until this point what I've been doing is yield return CoroutineUtility.WaitForFixedUpdate and then doing whatever I need to do. This has been fine until I just came across something unexpected and it had me rethinking the approach.

    I have a situation where in OnCollisionEnter I pass the collision as a parameter to a coroutine in order to handle some things. Where I noticed the issue is sometimes this would happen:

    Code (CSharp):
    1. IEnumerator MyCoroutine(Collider2D collisionDetails)
    2. {
    3.    yield return CoroutineUtility.WaitForFixedUpdate;
    4.    var magnitude = collisionDetails.rigidbody.velocity.magnitude
    5. }
    I get a null reference exception because the collisionDetails is now a completely different collision object since fixedupdate has run again before accessing the variable. I can easily solve this by caching the values I need before waiting etc., but what if I need to add velocity or something to that rigidbody before the next fixed update?

    So I guess I have 3 questions:
    1. Is it safe to handle physics updates right in the OnCollision methods?
    2. Is it safe to handle the physics immediately in the coroutine before the fixedupdate waiting, or is there no guarantee it will start immediately when I invoke it?
    3. Would the best scenario be to just wrap my physics adjustments in an action and put it in a queue to be processed inside FixedUpdate so I can guarantee it runs at the beginning of the next FixedUpdate?

    I also have some event listeners that need to be in coroutines because they temporarily ignore collision, wait a set amount of time, and reactivate the collision. I'm currently handling that by:
    1. Event handler triggered
    2. Wait for Fixed Update
    3. Ignore Collision
    4. Wait for seconds
    5. Wait for Fixed Updated
    6. Reactivate Collision
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    The above code isn't passing the collision details, it's passing a Collider2D reference but maybe that's just a typo.

    By default, to improve performance, both 2D/3D physics reuse the Collision(2D) structures passed to the collision callbacks. You can turn that off but I would not recommend it. I'd say extract what you need and pass that info on, potentially in your own structure if needed.

    Look at the docs here: https://docs.unity3d.com/ScriptReference/30_search.html?q=reusecollision

    Coroutines are irrelevant to the problem though.
     
  3. StillPossible

    StillPossible

    Joined:
    Apr 19, 2022
    Posts:
    4
    Yeah I came to that conclusion about the reuse once I saw the reference was updated in place.

    My post was more to get some clarity on the 3 questions I outlined.
    1. Can I make physics updates inside OnCollisionxxx?
    2. If instead I pass the data onto a coroutine will it start immediately in succession or does Unity decide when to invoke it?
    3. Am I better to just queue up physics updates and run them inside the fixedupdate loop instead to guarantee they happen there on the next fixed update since wait for fixed update will actually end up being after the next one instead of before/during?
    Thanks
     
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    You can make physics updates anywhere but I don't know what you mean by "physics updates" specifically.

    This isn't a physics question. Coroutines will be immediately executed until the first yield though.

    No context of what you're doing. There's no specific thing known as "physics updates". If you're (say) modifying the velocity of a Rigidbody then you can do it anywhere anytime. The point though is that physics only runs when you've got it set to run which is (by default) during the FixedUpdate. Setting the velocity would be used until then.

    https://docs.unity3d.com/Manual/ExecutionOrder.html

    Hope that helps.
     
  5. StillPossible

    StillPossible

    Joined:
    Apr 19, 2022
    Posts:
    4
    Ok, from what I've read it always seems to be stressed that you should only make physics updates inside FixedUpdate.

    By "physics updates", yes I mean changing the velocity of a rigidbody, .AddForce, Physics2D.OverlapCircle, Raycast etc.

    I have always understood this as do not modify those values/invoke those functions anywhere but inside FixedUpdate, but from what you've written it seems what you are saying is that it doesn't matter where you do these things it's just that modifications won't be applied until the next FixedUpdate.

    So regarding the best practice recommendations I've read as stated above, it's more of a question of awareness/predictability regarding your own code as opposed to bugs that could arise within Unity itself from modifying those properties outside the FixedUpdate function. Is that a fair interpretation?
     
  6. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    An analogy. It's recommended to press the brake on your car slowly to come to a stop but there's no rule that you have to do this in all circumstances and you might need to press it quickly and it all depends on the situation.

    My point being that if physics is running per fixed-update (the default but you can select it per-frame too) then clearly, when asking physics to do something, it makes more sense to do it immediately before it's going to process it. So it's "best" to perform operations that require physics to run before you get the results to be run just before physics runs so by default, during FixedUpdate.

    So calling "MovePosition" is "best" done in the script fixed-update because if you call it per-frame then you might end up calling it several times (frames) before physics actually runs meaning only the last one will be actioned. Same for adding forces; if you do this per-frame then the forces you're adding become frame-dependent so more frames means more forces. You can scale the force you apply by the delta-time but this can be problematic in itself. Adding a force is best done in fixed-update because then the forces you add are fixed-update dependent which is an improvement.

    You can set velocity per-frame but the position won't change until the simulation runs so you can do it per-frame but it "makes more sense" to do it in the fixed-update so it's processed soon after. That's not to say you have to.

    The short answer is "always do it in fixed-update" but the reasoning behind that is above. Note this only applies if you're running physics during the fixed-update.

    I hope that helps rather than hinders.
     
  7. StillPossible

    StillPossible

    Joined:
    Apr 19, 2022
    Posts:
    4
    Yes fair enough thank you. I think where I'm getting a little tripped up is in my events. When the event listener is invoked, we can't know where in the lifecycle we are, so how best to handle a situation where you have several steps, as opposed to where you have the standard scenario of "have movement variable set in update and apply the value in fixed update".

    As an example for context, here is a simple event and how I have handled it:

    Code (CSharp):
    1. IEnumerator BlockItem()
    2. {
    3.     var velocity = _rigidBody2d.velocity;
    4.     _rigidBody2d.velocity = Vector2.zero;
    5.     _rigidBody2d.angularVelocity = 0;
    6.     yield return CoroutineUtility.WaitForFixedUpdate;
    7.     _rigidBody2d.AddForce(velocity * -.1f, ForceMode2D.Impulse);
    8. }
    The idea is that the rigidbody has a normal material with bounciness etc. but in this one particular instance I want to override that behaviour, so I:
    1. Cache the current velocity in a variable
    2. 0 out the current velocity so it doesn't affect things when we add force
    3. Wait for the fixed update to ensure those values get applied
    4. Add the new force to get the desire "bounce" I'm looking for
    It works perfectly fine and exactly how I want it to, but with all the talk about doing everything in fixed update it would(seemingly) stand to reason I should queue up that add force call and then fire it in the next fixed update.

    However, due to the way the game works I am confident that the rigidbody will not be tampered with before that addforce gets applied, so I'm not really worried about it, which I suppose falls under the "you don't absolutely have to use fixed update if your situation doesn't call for it". This post is more looking to understand best practice in a scenario like this.

    I also have noticed you are usually the expert that responds to these posts so I was hoping you would chime in, so thank you.