Search Unity

Need rigidbody2d.AddExplosionForce

Discussion in '2D' started by LaneMax, Nov 19, 2013.

  1. LaneMax

    LaneMax

    Joined:
    Aug 12, 2013
    Posts:
    194
    Been messing a lot with these 2D features, love them :D! But came something I really need rigidbody2d.AddExplosionForce, it exists within the regular rigidbody but not in the rigidbody2d. Anyone have any ideas, or a away around get this to work? Really hoping they'll add it later.
     
  2. dasbin

    dasbin

    Joined:
    Jan 14, 2012
    Posts:
    261
    Agreed.
    Also missing, and equally as important IMHO, is an equivalent for applying a ForceType, and also applying a rigidbody2d.rotation
    I hope the Rigidbody2D class gets expanded to be as full-featured as the 3D class in short order. In its current state it is useless for my game and we stick with PhysX just because of its feature-completeness.

    Also on the wishlist is the ability to change the axes the entire new 2D system works with. Our game is currently built on the XZ (top-down) not XY axes, which is unchangeable because this is also the only orientation of a NavMesh, AFAIK. Seems like a huge oversight to not have the 2D features compatible with their own NavMesh system.
     
    Last edited: Nov 20, 2013
  3. LaneMax

    LaneMax

    Joined:
    Aug 12, 2013
    Posts:
    194
    Sweet, here's hoping :D
     
    DonPuno likes this.
  4. dasbin

    dasbin

    Joined:
    Jan 14, 2012
    Posts:
    261
    Cool.
    Who did you contact at support?
    I'd really like to get in touch with them regarding the other missing features mentioned above, and it would be good to know they are going to a willing listener.
     
    DonPuno likes this.
  5. LaneMax

    LaneMax

    Joined:
    Aug 12, 2013
    Posts:
    194
  6. Swamy

    Swamy

    Joined:
    Feb 12, 2013
    Posts:
    14
    This is the extension script I'm currently using as a workaround. Don't know if that's of any use for you guys:

    Code (csharp):
    1. public static class Rigidbody2DExtension
    2. {
    3.     public static void AddExplosionForce(this Rigidbody2D body, float explosionForce, Vector3 explosionPosition, float explosionRadius)
    4.     {
    5.         var dir = (body.transform.position - explosionPosition);
    6.         float wearoff = 1 - (dir.magnitude / explosionRadius);
    7.         body.AddForce(dir.normalized * explosionForce * wearoff);
    8.     }
    9.  
    10.     public static void AddExplosionForce(this Rigidbody2D body, float explosionForce, Vector3 explosionPosition, float explosionRadius, float upliftModifier)
    11.     {
    12.         var dir = (body.transform.position - explosionPosition);
    13.         float wearoff = 1 - (dir.magnitude / explosionRadius);
    14.         Vector3 baseForce = dir.normalized * explosionForce * wearoff;
    15.         body.AddForce(baseForce);
    16.  
    17.         float upliftWearoff = 1 - upliftModifier / explosionRadius;
    18.         Vector3 upliftForce = Vector2.up * explosionForce * upliftWearoff;
    19.         body.AddForce(upliftForce);
    20.     }
    21. }
     
    kbartek, honor0102, Light000 and 4 others like this.
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    I have added a whole bunch of missing methods, unfortunately these didn't make it into the 4.3 release.

    If you are referring to ForceMode type used in the AddForce methods (etc) then this has been added since the 4.3 release.

    Can you not use the Rigidbody2D AddTorque method or just modify the angularVelocity property directly or were you referring to something else?
     
  8. dasbin

    dasbin

    Joined:
    Jan 14, 2012
    Posts:
    261
    Thanks for the reply!

    I was referring to ForceMode; my mistake. And excellent!

    In 3D, rigidbody.rotation sets the rotation (Quanternion) *directly* without force or velocity. It is akin to changing the Transform values, but my understanding is that setting it in rigidbody.rotation doesn't fight the physics step.
    For example, in currently using the 3D physics system, my (2D) player character gets its rotation changed directly to face the user's input direction, but also generally moves and bounces around by forces and interactions in the world. I did try merely setting transform.rotation, but there is a visible "jitter" every frame where the player shakes when he also has an angular velocity from bouncing around the world etc. Rigidbody.rotation eliminates this problem and plays much more nicely with the angular velocities in play. So that's why I'd like to see this method in the 2D rigidbody as well, if possible.

    I'm sure the MoveRotation and MovePosition methods would be similarly useful to others, as well. Those additionally check for collisions between the two positions.
     
    Last edited: Nov 23, 2013
  9. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    I'm not sure I appreciate the need for setting the 2D rigid-body directly so it's out of sync with the transform until the next physics update. Either way, setting the rigid-body directly is bad for physics and performance just as setting indirectly via the transform is. It seems like this is just different way of potentially encouraging bad behaviour.

    Unfortunately, this is not true at all. It causes contacts to (eventually) be re-evaluated and has other potential side-effects in Box2D.

    Here's what happens:
    If you change the rigid-body position or rotation (say I expose Rigidbody2D.position and rotation) then any interpolation/extrapolation until the next physic update would have to stop so smooth motion until the next update ceases and if this continually happens then you'll get jitter. If interpolation is off, you'll continue to render at the transform position but that won't match the Rigid-body position. This doesn't provide any benefit I can see.

    So until the next physics update the rigid-body and the transform won't match and it'll render at the current interpolated position or if interpolation is off, it'll render at the rigid-body position *before* you updated it. When the next physics update comes along, the rigid-body change will update the transform or, if interpolation is on, it'll interpolate from the rigid-body position you set to the position the physics system has updated to (assuming it's different).

    Don't get me wrong here though; I'm not trying to be obstructive or anything, my intention is to try to ensure we don't just copy the Physics 3D API into the 2D one blindly as there are a bunch of things that we are considering changing/deprecating later for 3D so we'd like to keep 2D as "clean" as possible. Of course this doesn't mean we should sacrifice useful functionality so If I can fully understand the use-case and it's real, it'll go in.

    Yes and that's coming although it really should only work for kinematic bodies. For dynamic bodies it's terrible for performance and doesn't give the correct behaviour i.e. only kinematic bodies will not be affected by collisions during the move position/rotation.

    With MoveRotation/MovePosition in-place and using a kinematic body you should be good to go.
     
  10. dasbin

    dasbin

    Joined:
    Jan 14, 2012
    Posts:
    261
    Hmm, interesting. All I can do is defend my own usage of the method to which I don't think there is an appropriate way of emulating the behaviour through forces or angular velocity.

    When there is user input in any direction, I quickly (but smoothly) lerp the rotation value of the rigidbody to match and follow the input, so that the player character is pointing in the direction of input.
    When there isn't user input in a direction, then I stop setting the rigidbody.rotation. What happens at that point is a unique behvaviour and why I like to do things this way. Although the rigidbody has been directly pointing in the direction of user input for a while. as soon as I stop setting the rotation directly, there is a continuation of the momentum in angular velocity. In other words, the rigidbody will then have a bit of angular momentum in the direction of something that would have physically caused him to turn a bit a couple seconds ago (ie something hit his side and would have caused him to spin if this hadn't been overcome by rigidbody.rotation). So you see the residual effects of that previous momentum on the rigidbody as soon as direct control is released.

    I know this is probably an edge case, but I do really like the mix of arcade-controls and physical interaction this gives us, and I can't think of any other way it'd be possible. It gives a feeling of being bounced around the world, even though the player always has very direct control of the character.

    What is interesting is that although I am using Interpolation and have a fairly high difference between my physics timestep and actual graphical framerate, I'm not seeing the negative jitter effects on interpolation that you mentioned should be happening. Perhaps because I am only setting the rotation by very small (lerped) amounts each frame.


    On a philosophical note: You can disallow something because it is a bad idea programatically, but that doesn't mean it is a bad idea for the game. An improper usage might be the only way to achieve a great game in some circumstances!
     
    Last edited: Nov 23, 2013
  11. _Acid_

    _Acid_

    Joined:
    Jul 26, 2013
    Posts:
    9
    MoveRotation is indeed very important. I have a ship that should be turned in the direction where the joystick is pushed.

    With 3D rigidbody it is done like this:

    targetAngle = Mathf.Rad2Deg * Mathf.Atan2(controlAxis2.y, controlAxis2.x) + 90f;

    //Move object angle towards target angle using linear interpolation
    currentAngle = Mathf.LerpAngle(currentAngle, targetAngle, rotationDamping/2 * Time.fixedDeltaTime);

    //Rotate object using physics handled MoveRotation
    rigidbody.MoveRotation(Quaternion.Euler(new Vector3(0f,currentAngle, 0f)));


    This is a top-down shooter, so it is the y-value that is rotated...

    But how on earth could that be done with AddTorque only?
     
    Last edited: Jan 18, 2014
  12. Mario-M701

    Mario-M701

    Joined:
    Feb 15, 2014
    Posts:
    3
    Thank you Swamy!!
    I also missed this feature, and your workaround just works perfect.
     
  13. iftah

    iftah

    Joined:
    Feb 25, 2014
    Posts:
    16
    Can you please post an extension for Rigidbody2D.MovePosition? Thank you
     
  14. tomandjerry-tas

    tomandjerry-tas

    Joined:
    Feb 7, 2013
    Posts:
    22
    perfect ...... Thank you :):):)
     
  15. renedon

    renedon

    Joined:
    Jul 14, 2017
    Posts:
    5
    Perfect, thanks!
     
  16. Merrick20

    Merrick20

    Joined:
    Feb 20, 2015
    Posts:
    4
    I just logged in liked your reply and needed to say this was amazingly useful for me as I needed it for my game desperately.
     
  17. MaDDoX

    MaDDoX

    Joined:
    Nov 10, 2009
    Posts:
    764
    Minor improvement to Swamy's excellent snippet, preventing attraction/negative explosion forces from happening, when radius is small:

    Code (csharp):
    1.  
    2. public static class Rigidbody2DExtension
    3. {
    4.     public static void AddExplosionForce(this Rigidbody2D body, float explosionForce, Vector3 explosionPosition, float explosionRadius)
    5.     {
    6.         var dir = (body.transform.position - explosionPosition);
    7.         float wearoff = 1 - (dir.magnitude / explosionRadius);
    8.         body.AddForce(dir.normalized * (wearoff <= 0f ? 0f : explosionForce) * wearoff);
    9.     }
    10.  
    11.     public static void AddExplosionForce(this Rigidbody2D body, float explosionForce, Vector3 explosionPosition, float explosionRadius, float upliftModifier)
    12.     {
    13.         var dir = (body.transform.position - explosionPosition);
    14.         float wearoff = 1 - (dir.magnitude / explosionRadius);
    15.         Vector3 baseForce = dir.normalized * (wearoff <= 0f ? 0f : explosionForce) * wearoff;
    16.         body.AddForce(baseForce);
    17.  
    18.         float upliftWearoff = 1 - upliftModifier / explosionRadius;
    19.         Vector3 upliftForce = Vector2.up * explosionForce * upliftWearoff;
    20.         body.AddForce(upliftForce);
    21.     }
    22. }
    23.  
    24.  
     
  18. chodimirko94

    chodimirko94

    Joined:
    Oct 29, 2016
    Posts:
    1
  19. NJFalzon

    NJFalzon

    Joined:
    Mar 11, 2020
    Posts:
    1
    If you just simply subtract the object's position with the explosion force, then the larger the distance the larger the force. Therefore you would need to make the distance Inversely Proportional to the force. This can be done if you know the Max Distance the bomb can effect. Furthermore, if the distance is negative switch the Max Distance into a negative.

    Code (CSharp):
    1. void ExplosionForce(List<Collider2D> players)
    2.     {
    3.         foreach (Collider2D hit in players)
    4.         {
    5.             Rigidbody2D rb = hit.GetComponent<Rigidbody2D>();
    6.             float x = MaxDistance - (rb.position.x - transform.position.x);
    7.             float y = MaxDistance - (rb.position.y - transform.position.y);
    8.             if (x > MaxDistance)
    9.             {
    10.                 x = -MaxDistance - (rb.position.x - transform.position.x);
    11.             }
    12.  
    13.             if (y > MaxDistance)
    14.             {
    15.                 y = -MaxDistance - (rb.position.y - transform.position.y);
    16.             }
    17.  
    18.             rb.AddForce(new Vector2(x,y)/2, ForceMode2D.Impulse);
    19.         }
    20.     }
     
  20. Czar-Man

    Czar-Man

    Joined:
    May 10, 2013
    Posts:
    20
    When I try this on a large physics object, it barely pushes it when I have an absurdly high explosion force. If your explosion radius isn't big enough, it's common to see wearoff go negative and turn the whole force to 0.

    I used the collision's attachedRigidBody as the body to add the force to.
     
  21. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    613
    I can't believe this. I came here to find a 2D implementation for AddExplosionForce. I find garbage.

    Here is the modified AddExplosionForce script from MaDDoX, originally Swampy. The blue cube is a rigidbody at (-0.683411, 0.679397) with AddExplosionForce(10f, Vector3.zero, 5f, 1f, ForceMode.Impulse). The red square is the exact same, except that it uses a Rigidbody2D and the Rigidbody2D AddExplosionForce method.
    gif_animation_002.gif

    I'm sorry, but I mean, common... I wanna be a nice guy but this one would be a dev-job-deal breaker.

    All kidding aside, I didn't came just to S*** talk. Here's my own method:
    gif_animation_003.gif

    Apart from the torque, which has been a real B to figure out, the movement is 100% solid lock-and-loaded double barrels accurate. Seriously I shot Tim and Tom here at a 5000 impulse force escape velocity and 10 seconds later their positions still matched.

    By all means, partake. When you download the Rigidbody2DExtensions.txt, make sure to change it to a .xml and put it in the same directory as the Rigidbody2DExtensions so the methods are correctly documented. If anyone finds out how to get that rotation right, optimize it better (I did my best but it can always be better), or has any issues with more complex bodies, I haven't tested those, please let me know.
     

    Attached Files:

  22. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    613
    I did some more testing on that rotation and annoyingly my code is correct.

    In 3D, both these methods shoot a rigidbody in exactly the same way, it is 1 to 1.
    Code (CSharp):
    1. void UnityMethod()
    2. {
    3.     rigidbody.AddExplosionForce(force, pos, 0f, upwards, mode);
    4. }
    5.  
    6. void MyMethod()
    7. {
    8.     // This is under the right conditions the same way force is applied in 2D.
    9.     var upPos = pos + Vector3.down * upwards;
    10.     var newPos = rigidbody.ClosestPointOnBounds(upPos);
    11.     var dir = (newPos - upPos).normalized;
    12.  
    13.     rigidbody.AddForceAtPosition(dir * force, newPos, mode);
    14. }
    It just seems to be a difference of Rigidbody2D rotation calculations and / or inertia.

    I did find a hack that works every time, even if it's very impractical. If you apply AddExplosionForce to a rigidbody, then do this:
    Code (CSharp):
    1. var angularVelocity = rigidbody.angularVelocity.z;
    2. rigidbody2D.angularVelocity = angularVelocity * Mathf.Rad2Deg;
    The rotations match perfectly. This only works if the rigidbody and rigidbody2D aren't already rotating.