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:
// very basic example
private void OnCollisionEnter2D(Collision2D collision)
{
var impactForce = collision.relativeVelocity.magnitude;
}
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.
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.
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?
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.
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.
public LayerMask layersToTouch;
public Rigidbody2D rb2D;
private void OnCollisionEnter2D(Collision2D collision)
{
Debug.Log("Will trigger on collision");
}
private void FixedUpdate()
{
if (rb2D.IsTouchingLayers(layersToTouch))
{
Debug.Log("Will trigger a frame later, but I want it to trigger same frame when colliding.");
}
}
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.
I think YouTubers always saying “always put anything physics related in FixedUpdate” messed up my learning a lot 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”?
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.
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.
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.
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.
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.
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.