Search Unity

Impact Damage - after multiple hits

Discussion in '2D' started by BACALL, Apr 28, 2022.

  1. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Hi!

    I'm trying to calculate impact damage. At the moment I am calculating it in "OnCollisionEnter2D". I'm grabbing the relativeVelocity. Looks something like this:

    Code (CSharp):
    1.  
    2. // very basic example
    3. private void OnCollisionEnter2D(Collision2D collision)
    4. {
    5.       var impactForce = collision.relativeVelocity.magnitude;
    6. }
    This isn't a working solution for me, because if the ground and the wall is part of the same collider, the collision only registers ONCE. For example: if the player hits a wall, collision is entered (OnCollisionEnter2D is triggered). If the player then slides down and hit the ground, the collision is not called, because it was already entered. The second impact force was never calculated (See picture at bottom).

    Already tried 01: Separate the wall from the ground, so they have their own colliders. For me, that's not possible. I'm using the SpriteShape to create my levels. And some shapes are really convoluted.

    Already tried 02: Calculate relativeVelocity in OnCollisionStay2D. But it always returns 0 on impact. I guess it's because the collision already happened, and it's returning the "stay" value.

    Possible solution: Somehow reset OnCollisionEnter2D after first hit, so it can enter again. Is this possible somehow? Without creating an Enter/Exit loop...

    I'm not interested in calculating "fall damage" or anything similar, I need the impact force, so I can destroy crates on impact etc. It's a physics based game.

    All suggestions appreciated!

     
    Last edited: Apr 28, 2022
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Why not use the available contacts on the Collision2D type? This gives you everything the physics knows: https://docs.unity3d.com/ScriptReference/ContactPoint2D.html

    OnCollisionEnter2D is called when you first "enter" the collision with OnCollisionExit2D being when you "exit" the collision. OnCollisionStay2D is called continuously so will give you a time to retrieve the data continuously.

    OnCollisionEnter2D/OnCollisionStay2D are the same thing. It calls Stay if there's already been an enter, it's the same data. Reset what? The relative velocity is the relative velocity, it's not stored anywhere.

    No, it's a recording of the state as it happened. This is why you get impulse info etc. WOuld impulse magnitude be a better match? This is used for breaking joints etc.

    Surely relative velocity is only good for the initial impact because after that it'll be stopped.
     
  3. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Can't find info for 2D impulse info, only seems to be available for 3D collisions? Thanks in advance!
     
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Did you look at the link I provided?
     
  5. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
  6. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Impulse, relative velocity etc. Per-contact which is what you seem to be asking for i.e. multiple points of contact.

    Know that you can also simply retrieve all the contact information whenever you like using GetContacts (See Rigidbody2D/Collider2D/Physics2D) API.
     
  7. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Thanks for the quick reply! Basically, under FixedUpdate I get all of the contact points, for example through Rigidbody2D or Collider2D attached to the player, and then I check all of the relativeVelocity of all of the contacts?
     
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    I'm not really telling you what to do here (you'll have to determine what you need) but yes, you can retrieve contacts whenever you like, not limited to fixed-update. Obviously contacts are only updated during the simulation step but you can read them per-frame if you like but they won't change per-frame necessarily. Contacts are what other queries like IsTouching uses.
     
  9. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Awesome, I think I get it now. Thank you!
     
    MelvMay likes this.
  10. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Edit: Just realised OnCollisionEnter2D is after FixedUpdate in the order of execution, so never mind

    What would be the best way to check IsTouching so it's synced with OnCollisionEnter2D?

    For example, IsTouching and IsTouchingLayers is delayed by a frame when used in FixedUpdate, compared to using OnCollisionEnter2D. Meaning, OnCollisionEnter2D triggers at the exact frame the collisions happened, while "IsTouching" triggers one frame after the collision happened.

    Code (CSharp):
    1.  
    2. public LayerMask layersToTouch;
    3. public Rigidbody2D rb2D;
    4.  
    5. private void OnCollisionEnter2D(Collision2D collision)
    6. {
    7.     Debug.Log("Will trigger on collision");
    8. }
    9.  
    10. private void FixedUpdate()
    11. {
    12.     if (rb2D.IsTouchingLayers(layersToTouch))
    13.     {
    14.         Debug.Log("Will trigger a frame later, but I want it to trigger same frame when colliding.");
    15.     }
    16. }
    17.  
     
    Last edited: May 8, 2022
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    You need to really understand what happens to better express your question TBH.

    To be clear here, when you say "FixedUpdate" you mean scripts (monobehaviours) and their FixedUpdate callback. Unity calls all Script FixedUpdates before the internal FixedUpdate. The internal FixedUpdate is where internal systems such as physics, animation etc are called. The same is true for Update; you get the script Update callbacks then after all those, the internal Updates happens.

    You can configure 2D physics to run in the internal FixedUpdate or Update or manually when you choose. By default it's during the internal FixedUpdate. This means anything you do in the script FixedUpdate is before the internal FixedUpdate happens. If you check for contacts before the simulation step then you're checking the previous simulation step data; IsTouching isn't delayed by a "frame" at all. You are reading the previous simulation step. Also, I keep using the term "simulation step" intentionally because "frames" infer rendering and unless you're running the simulation per-frame it has nothing to do with "frames".

    The final part of the simulation step, no matter when it is called, is performing the physics callbacks such as the one you mentioned.

    There is no "AfterFixedUpdate" but the "Update" script callbacks will always be after FixedUpdate both for script and internal so checking for contacts using things like IsTouching or any other query, will be querying the last simulation step. That said, it doesn't mean physics ran that "frame", you might do several "frames" before simulation step happens but calling it in "Update" always means it's the latest simulation data.

    So in short, you don't "sync" OnCollisionEnter2D with IsTouching, they are different things but now you know when things run, you can ensure you're querying the last simulation step data by, for example, querying in "Update".

    Also, you don't have to run physics in FixedUpdate if don't want. If your project allows, you can simply run physics per-frame. Physics runs at a variable frame-rate so the quality of the simulation is affected by frame-rate but there's lot of times when this doesn't matter at all. You then don't need to use FixedUpdate at all for anything physics. Even interpolation is automatically ignored for you.
     
    BACALL likes this.
  12. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Yes exactly

    I think YouTubers always saying "always put anything physics related in FixedUpdate" messed up my learning a lot :confused: But finally starting to understand
    So if I check "rb2D.IsTouchingLayers" in Update (instead of FixedUpdate), it have had the chance to collect all of the simulation data for that specific frame (not simulation step)? Meaning, when I visually see that the object is touching whatever, rb2D.IsTouchingLayers is then "true"?
     
    Last edited: May 10, 2022
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Nothing is being collected. Update is always called after FixedUpdate (both the scripts and internal fixed-update) so you are guaranteed to be querying the latest simulation step as I said above. Also as I said above, FixedUpdate isn't called each frame, it's called at the fixed-update interval you can set.

    I'm not going to comment on what visuals you have because physics isn't about visuals at all. IsTouching isn't about "visually touching", it's about whether there's an active contact saying two colliders are touching in the last simulation step. I might sound like I'm being pedantic but what I'm saying is 100% correct. Any difference in visuals could be a number of things.
     
    BACALL likes this.
  14. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    I don't know if this is a bug, but I can't figure out why this is happening:

    In short, I successfully created the impact damage system, where I check each contact and it's relativeVelocity etc...

    Later, I started to notice my character randomly dies even from the slightest touch of other physics objects. This always happens very randomly. I created a debug and noticed the "relative velocity" of contacts sometimes produce very extreme values, and I mean VERY extreme. From the console: Impact for: Physics Object (90): (-64895290000000000000000000000000000.00, NaN)

    Doesn't matter what Rigidbody2D settings I use, Discrete, Continuous, Interpolate etc.

    1. If you download this project, https://www.dropbox.com/sh/a7barqibub55d6f/AADwJbldiYpl9yIqPcFENu2Ma?dl=0
    2. hit play and let the balls fall it sometimes produces the LogWarning from the ImpactDamage.cs script.
    If it doesn't happen, hit stop, then play again and repeat 3-5 times until the warning gets produced.
     
  15. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    If you want to host something like the above, you'll first need to delete the library folder as it's just too large. The library folder alone can be Gb in size.
     
  16. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Okay, thank you, removed the library folder

    Any thoughts if it's a bug or something else? Thanks in advance
     
  17. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    No idea, it was too big to download and you've only just said you've removed the folder. I'll try to remember to look at this tomorrow.
     
  18. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Sorry, I thought you downloaded the folder, earlier, and let me know the library was too large for future uploads.
     
  19. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    No, it said it was too large to download as a zip. To be honest, you're best zipping it up anyway before hosting it. Always worth removing the library folder too.
     
  20. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    So I took a look and what you're doing and it is definately extreme with huge forces involved on a few specific things. Visual circles using the PolygonCollider2D (so multiple shapes) with a x8 gravity using Continuous Collision Detection on a "terrain" that's also composed of multiple shapes where some get compressed together at high velocities.

    To show this, you can replace the circle shape with a CircleCollider2D and this stops the issue. Either that or reduce the gravity force and then it also goes away. Or changing the "terrain". You can see the culprits if you turn-off continuous collision detection and go to discrete; they get thrust into the terrain.

    This seems to be coming out of the the continuous collision detection solver so Box2D itself. It certainly does have its limits in such situations. Essentially the solver has an impossible task in trying to solve the situation.

    I'll do a little more digging to find the specific part of Box2D which is producing these high impulses and we'll go from there.
     
  21. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Nice, thanks for the inputs!

    I apologize for my stupidity in advance, I now realize I messed up: I increased the gravity scale because the game is large in scale (meaning I created every prefab/Sprite/Collider too large). I later learned ”by default, 1 Unity unit is 1 meter.”.

    We’ve not come too far yet and there’s still time for large changes. Would it help I decreased the size of everything, and changed gravity scale back to 1. Or is there some other workaround, for example changing the default unit size?
     
  22. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    I detailed the workarounds above. Mainly it's helping the solver out. Try replacing those circles with CircleCollider2D. It'll be a lot faster too.

    The size of things makes no difference or at least it's not relevant to this issue.

    It still shouldn't do this but I need to find out if it can be avoided or not. Rewriting the Box2D solver here isn't an option but it might be avoidable. It might also be a bug in Unity in passing this info although that would surprise me being as it's used all over the place for many, many years but I will check. I did notice that it seems to be the last contact that has this which is very suspicious indeed and would suggest it might indeed be a data-transfer issue. I am investigating.

    NOTE: If your terrain really is like this test and you're using continuous collision detection then you can add a CompositeCollider2D in outline mode to produce edges. This'll massively reduce the complexity here for you i.e. increase performance.
     
  23. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Maybe this helps with the digging, just some more info:
    In the real project I have for example crates, with a simple BoxCollider2D, and the player/characters has a single CapsuleCollider2D.
    The terrain is sometimes very simple (indoors, flat surfaces), other times not so simple (outdoors, hills etc, like the project example). I've tried "discrete" and the spikes are still there, maybe not so much, but they still happen. I've also tried interpolate, non, etc.
    The spikes also randomly happen when the character just slightly rubbing up against stuff, not limited to dropping crates/circles from the sky. Not that common, but happens too often.
    The mysterious thing is, in my original project, when testing and dropping the set of crates over and over (like the circles in the project example), I've noticed it always happens to the same crates. I hope that helps with the digging.
    I'd be more than happy to provide the original project, it's around 3GB in size.

    I was thinking, if lowering the gravity scale makes the issue go away. I would have to lower the size of stuff too. But that would be a real pain, so I'm just happy I I don't have to.
     
  24. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Well ultimately it only seems to be happening when using continuous collision detection. I hope you know that this is very expensive to use for a lot of objects.

    I don't need a large project, I'm not trying to optimize that but just find what is the reason for this large value and a smaller project with fewer objects is better.

    It's impossible to debug something when there's lot of things happening. Right now I've narrowed it down to only using 3 objects in your project. Physics Object (30, 31 & 32).
     
  25. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Increasing gravity x8 because your scale is wrong is a bad thing to do. It makes the solvers job that much harder because there's a constant high force acting on everything and will result in everything else scaling up too including friction, bounce, the forces you apply etc.

    Regardless, I need to focus on isolating where this is coming from for you.
     
  26. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Ah I think you are correct, my fault. The crates have discrete, but the player was still using "continuous".
     
  27. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Nothing wrong with a handful of specific objects using it, assuming there's a reason why. It's normally only used for when you might see tunnelling through colliders because of very high speeds. In Box2D itself it's known as a "bullet" because that's its typical use-case.
     
    BACALL likes this.
  28. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Sorry for the delay.

    It certainly seems like there's a problem here but it was hard to isolate so I need to do more work on it. Would you mind submitting a bug report with the project attached? That way you can track the bug and subsequent fix. If not, no worries, I can process the bug fix without it.

    If you do, can you please post the incident number you'll get here.

    Thanks.
     
    BACALL likes this.
  29. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Submitted! Case: IN-7926
     
    MelvMay likes this.
  30. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Thanks. I've contacted our QA and told them to accept and progress it to an internal case so I can use it for fixing the bug.

    I'll keep you informed via this thread.
     
    BACALL likes this.
  31. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Sorry for the late reply but I wanted to mention that the fixes for this have been submitted. They are not in a public release yet but will eventually land in the future.

    The technical detail was that this only applied when using continuous because only continuous can adjust the contact points from 1 to 2 contacts for each physics shape. This resulted in the recorded velocity when there was one contact not being updated when there were two with the end result being that the second contact point relative velocity being uninitialized.

    This is all due to the fact that a Unity collider can be comprised of many shapes so we have to jump through hoops gathers all related contacts for each shape and collate them to a single collider. In your project, the "balls" are polygons being comprised of many shapes. If these were a CircleCollider2D for instance, you wouldn't get this as there's only ever a single contact for such a shape.

    Anyway, it's now fixed and will land eventually.
     
    BACALL likes this.
  32. BACALL

    BACALL

    Joined:
    Feb 21, 2017
    Posts:
    87
    Been waiting for this fix, very glad to hear and thank you! Cheers
     
    MelvMay likes this.
  33. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    You're most welcome and sorry for the delay on the fix. Summer holidays etc.
     
  34. REDACT3D_

    REDACT3D_

    Joined:
    Nov 8, 2020
    Posts:
    222
    using a checksphere instead of oncollision is what I use for this kind of problem.
    as it would check every frame if it is in contact with whatever tag

    like for ground checks on a character controller
     
  35. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Well yes, using queries is more appropriate than waiting for contact events in some circumstances but TBH this isn't useful for this thread. Note that this is about 2D physics, not 3D physics and CheckSphere is just asking if something is overlapping the 3D sphere. Also, there's no point in doing queries per-frame if the physics doesn't run per-frame as nothing moves per-frame.
     
  36. REDACT3D_

    REDACT3D_

    Joined:
    Nov 8, 2020
    Posts:
    222
    woops.
    blew right past the 2d part lol
     
    MelvMay likes this.
  37. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    It happens. I do the same sometimes. ;)