Search Unity

Prevent rigidbodies from pushing each other?

Discussion in '2D' started by herpanda, Feb 17, 2014.

  1. herpanda

    herpanda

    Joined:
    Jan 1, 2014
    Posts:
    27
    I have a player and an enemy, and they both have rigidbodies on them. When they touch it's possible for them to push each other, which I don't want. Is there an elegant way at preventing them from pushing each other while still being able to collide with each other? Something like, ignoreCollisionForce() or something.

    Thanks.
     
  2. AlenBrk

    AlenBrk

    Joined:
    Feb 17, 2014
    Posts:
    33
  3. diegzumillo

    diegzumillo

    Joined:
    Jul 26, 2010
    Posts:
    418
    But what exactly do you want to make it happen when they meet? If there were no forces they would pass right through each other. Maybe you want them to stick together?
     
  4. herpanda

    herpanda

    Joined:
    Jan 1, 2014
    Posts:
    27
    For one thing I wouldn't want them to overlap each other at all either, and they are going to be colliding with other things so I'm not sure if triggers are the best solution.

    Basically I want them to act like walls to each other. I currently set the player's X velocity to 0 when it comes within a short raycast of the enemy, but the raycast isn't always reliable (for example, my raycast is .2f long to stop the player, but sometimes the player can still get closer than that). Triggers also aren't reliable to keep the player from getting closer than it should.
     
  5. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,550
    You could add a parameter to your raycast length that increases it based on a fraction of your rigidbody.velocity.x. This way it will take into account the margin for error in physics steps from speed increases.
     
  6. diegzumillo

    diegzumillo

    Joined:
    Jul 26, 2010
    Posts:
    418
    Basically you want them to have inelastic collisions. How about altering the physics material, set the bounciness to zero whenever they touch each other.
     
  7. herpanda

    herpanda

    Joined:
    Jan 1, 2014
    Posts:
    27
    How do people have floating panels that you can walk on without pushing them down, or wall jump off of falling rocks without pushing the rocks to the side? It's the same problem, so if someone knows how to have that done then it'll solve mine.

    This didn't work for me; they can still push each other around sadly.

    This could be a possibility... but getting it to work perfectly might end up being more effort than it's worth. I might end up needing to do this though, or perhaps just scrap my controller and do this without Unity's physics engine :(
     
  8. Kurius

    Kurius

    Joined:
    Sep 29, 2013
    Posts:
    412
    In these examples the player (dynamic body) is interacting with other objects (kinematic body).
    In your case you actually have a dynamic body interacting with another dynamic body.

    What if you make your player a dynamic body, and make your enemy a kinematic body?

    In box2d physics...
    - Dynamic body colliding with Dynamic body results in both bodies pushing each other
    - Dynamic body colliding with Kinematic body results in Kinematic body acting like a wall (a wall that can move if it chooses to, but will not move if the dynamic body collides into it)
    - Dynamic body colliding with Static body results in Static body acting like a wall (a wall that can not move if it chooses to, and will not move if the dynamic body collides into it)
    - Kinematic body colliding with Kinematic body results in them actually passing right through each other
    - Kinematic body colliding with Static body results in them actually passing right through each other
     
  9. herpanda

    herpanda

    Joined:
    Jan 1, 2014
    Posts:
    27
    This sounds promising, but how can I move a Kinematic body while still getting collisions? Setting the velocity/adding forces doesn't move it, and transform translate goes right through colliders. I noticed there's a MovePosition for 3D, but there is no MovePosition function for 2D rigidbodies.
     
  10. Kurius

    Kurius

    Joined:
    Sep 29, 2013
    Posts:
    412
    That is very odd. That SHOULD move it.
     
  11. herpanda

    herpanda

    Joined:
    Jan 1, 2014
    Posts:
    27
    Oh shoot, you're right it does move. I forgot to enable my script :p. Thanks Kurius and company, I'll see where I can go from here.

    EDIT: It moves, but now it doesn't collide my walls/floor :/ any suggestions?
     
    Last edited: Feb 19, 2014
  12. Kurius

    Kurius

    Joined:
    Sep 29, 2013
    Posts:
    412
    Yup as I mentioned earlier a kinematic body will travel through a static body. So unfortunately you're once again left with trying to detect proximity to a wall etc then negating the force or velocity manually so that the enemy doesn't travel through the wall.
     
  13. herpanda

    herpanda

    Joined:
    Jan 1, 2014
    Posts:
    27
    I see. I was able to reuse my player's wall/groundcheck raycasts for the enemy collisions, and now the enemies won't be pushed around by the player. It also seems that Kinematic rigidbodies don't respond to triggers, so I'm going to implement something similar to what AlenBrk suggested with OverlapArea (which on it's own doesn't work all of the time since it only gives back one collider, wheras I'd need to check hits on multiple enemies in one spot).

    It looks like my game isn't on ice yet thankfully; if it was multiplayer I might have been stuck haha. Thanks all, I hope this is the last of my worries with Unity's collision engine.
     
  14. Kurius

    Kurius

    Joined:
    Sep 29, 2013
    Posts:
    412
    I think the issue of kinematic bodies not responding to triggers is a bug. You can search this forum about it.
     
  15. herpanda

    herpanda

    Joined:
    Jan 1, 2014
    Posts:
    27
    I looked at the bug report, and apparently it's supposed to be like that by design and the docs are just wrong. Strange, but here's the report if anyone's interested. It sounded like one of the Unity guys might be implementing the feature though, but I have no time to wait on that :).

    Bug Report listed as "By Design"

    Edit: It looks like using OverlapAreaNonAlloc() will snatch a list of colliders over a rectangle! So if anyone is in the same place as me you could use that or OverlapAreaAll (this one returns a list while allocating memory, the former takes a reference to an array and edits it).
     
    Last edited: Feb 20, 2014
  16. night5415

    night5415

    Joined:
    Feb 2, 2018
    Posts:
    1
    New to Unity, but just to give another way to solve this issue..
    1) is to set the gravity scale on the rigidbody to a high level so that player interaction does not move it
    2) is to add an empty child with a collider and set the parent as a trigger.

    I know this is an old question, but still very relevant.
     
    Dijin1000 likes this.
  17. magique

    magique

    Joined:
    May 2, 2014
    Posts:
    4,030
    This is definitely still relevant. I'd like to try your ideas, but they don't make sense to me. I don't see any such property as gravity scale for a rigidbody so I can't try that option. For the second option could you explain in more detail? I'm not quite sure what you're suggesting here.
     
  18. lmakor

    lmakor

    Joined:
    Oct 31, 2016
    Posts:
    23
  19. MikeFP

    MikeFP

    Joined:
    Jun 26, 2018
    Posts:
    1
    Since Unity doesn't plan on exposing those features for the devs any time soon, I managed to create that behaviour almost perfectly:

    1) You'll need 2 colliders, both attached to identical rigidbodies, and one being the other's child. Let's call the parent "World Collider" and the child "Player Collider".

    2) World Collider is a regular collider and its rigidbody is dynamic. The gameObject should be in a layer Player, that will be used by every collider that wants to collide with the player, except for other players.

    3) Player Collider is a trigger collider and its rigidbody is kinematic. The gameObject should be in a layer Player Collider, that will serve one purpose only: to collide with other players' World Colliders.

    4) Adjust the Layer Collision Matrix to the following image: https://imgur.com/a/mNfBdiu

    The players still push each other a little, but I'm sure you could adjust some mass settings and be able to achieve the wanted result. I'm not worried about it being perfect yet, but let me know if it works.
     
    tranxuandan likes this.
  20. Eleocraft

    Eleocraft

    Joined:
    Jun 19, 2019
    Posts:
    17
    set the rigidbody.drag to about 20, that will probably solve the problem
     
    BogoxD09 likes this.
  21. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    This doesn't work the moment the object moves, not the player but the other one. It doesn't update the position of the collider that the player will collide with
     
  22. blu3drag0n

    blu3drag0n

    Joined:
    Nov 9, 2018
    Posts:
    94
    Hi,

    I implemented this case for my 2,5D game.
    It sounds you are looking for this ?
    player_npc_walking.gif

    Here find an example PlayerController and NPCController.
    I removed animator stuff and everything thats not perfectly relevant to your question.
    So don't wonder if you can't just copy&paste to have the identical behaviour (it terms of animation and spirte rendering...) as from my preview GIF above.

    Both Rigidbodies can have Body Type Dynamic and both have a CircleCollider2D attached.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PlayerController : MonoBehaviour
    4. {
    5.     [Header("General:")]
    6.     [Space]
    7.     public bool npcTackled = false;
    8.     public Collision2D tackledNPC;
    9.  
    10.  
    11.     [Header("Movement Settings:")]
    12.     [Space]
    13.     public float movementBaseSpeed = 1.0f;
    14.     public Vector2 movementDirection = Vector2.zero;
    15.     public float movementSpeed = 0.0f;
    16.     public bool canMove = true;
    17.  
    18.     [Header("References:")]
    19.     [Space]
    20.     public Rigidbody2D playerRB;
    21.  
    22.  
    23.     void Update()
    24.     {
    25.         if (canMove)
    26.         {
    27.             ProcessMovementInputs();
    28.             Move();      
    29.         }  
    30.     }
    31.     #region Movement Handling
    32.     void ProcessMovementInputs()
    33.     {
    34.         //reset that we are moving
    35.         movementSpeed = 0.0f;
    36.  
    37.         //get the absolut inpuit from arrow keys to decide in which direction to move the player
    38.         movementDirection.x = Input.GetAxisRaw("Horizontal");
    39.         movementDirection.y = Input.GetAxisRaw("Vertical");
    40.  
    41.         //if the movement direction is not equal to the zero vector we will define the movmentspeed and declare that the player is actually moving
    42.         if (movementDirection != Vector2.zero)
    43.         {
    44.             //clamp the movementdirections magnitude between 0 and 1, so nobody cheat with special input devices (xbox controllers), and assign it as the movementspeed
    45.             movementSpeed = Mathf.Clamp(movementDirection.magnitude, 0.0f, 1.0f);
    46.             //normalize the movement direction, so we are not unrealisticly moving double as fast when using diagonal movement direction
    47.             movementDirection.Normalize();
    48.         }
    49.     }
    50.  
    51.     void Move()
    52.     {
    53.         //only move the palyer into the direction when he currently not in contact with an NPC
    54.         if (!npcTackled)
    55.         {
    56.             playerRB.velocity = movementDirection * movementSpeed * movementBaseSpeed;
    57.         }
    58.         else
    59.         {
    60.             //get the relative position of the NPC to the player
    61.             Vector2 positionRelative = transform.InverseTransformPoint(tackledNPC.transform.position);
    62.             //if we are stucking at the NPC we need to trick around, so we can leave the NPC's colliding shape again
    63.             //we do this by checking movementDirection (where the player would go to) and get the distance between the NPC's relative position and the movementDirection
    64.             float moveRelative = Vector2.Distance(positionRelative, movementDirection);
    65.             //as if the player is moving away from the NPC the moveRelative will get > 1, so we can assign the normal movement flow
    66.             //if the player would go into the NPC with his movementDirection again, then the moveRelative would be < 1, so we assign vector2.zero velocity to his RB
    67.             if (moveRelative > 1.0f)
    68.             {
    69.                 playerRB.velocity = movementDirection * movementSpeed * movementBaseSpeed;
    70.             }
    71.             else
    72.                 playerRB.velocity = Vector2.zero;
    73.         }
    74.     }
    75.  
    76.     private void OnCollisionEnter2D(Collision2D collision)
    77.     {
    78.         //only care for collision with NPC
    79.         //other collisions will be treated by the collider components (static structures, that cant get pushed)
    80.         if (collision.transform.tag == "NPC")
    81.         {
    82.             npcTackled = true;
    83.             //save the currently tackled NPC for later uses, e.g. relative position and talking with the NPC
    84.             tackledNPC = collision;
    85.         }
    86.     }
    87.  
    88.     private void OnCollisionExit2D(Collision2D collision)
    89.     {
    90.         if (npcTackled)
    91.         {
    92.             npcTackled = false;
    93.             tackledNPC = null;
    94.         }
    95.     }
    96.     #endregion
    97.  
    98. }
    99.  

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. public class NPCController : MonoBehaviour
    5. {
    6.     [Header("Movement Settings:")]
    7.     [Space]
    8.     public bool freeMoving = true;
    9.     public float movementFrequenceThreshold = 1.0f;
    10.     public float movementFrequence = 0.1f;
    11.     public float movementBaseSpeed = 1.0f;
    12.     public float movementDuration = 1.0f;
    13.     public Vector2 movementDirection = new Vector2(0.0f, 0.0f);
    14.  
    15.     public float movementSpeed;
    16.     public float movementFrequenceCounter = 0.0f;
    17.     public float movementDurationCounter = 0.0f;
    18.     public bool shouldMove = false;
    19.     public bool tackled = false;
    20.  
    21.     [Header("References:")]
    22.     [Space]
    23.     public Rigidbody2D npcRB;
    24.  
    25.     void Update()
    26.     {
    27.         if (!tackled)
    28.         {
    29.             if (freeMoving)
    30.             {
    31.                 ProcessAutoMovement();
    32.                 Move();
    33.             }
    34.             else
    35.                 movementSpeed = 0.0f;
    36.         }
    37.         else
    38.         {
    39.             movementSpeed = 0.0f;
    40.             movementDirection = Vector2.zero;
    41.             npcRB.velocity = Vector2.zero;
    42.         }
    43.     }
    44.  
    45.     void ProcessAutoMovement()
    46.     {
    47.         if (movementFrequenceCounter > movementFrequenceThreshold)
    48.         {
    49.             movementFrequenceCounter = 0.0f;
    50.             shouldMove = true;
    51.  
    52.             for (int i = 0; i < 2; i++)
    53.             {
    54.                 int randomizer = UnityEngine.Random.Range(0, 4);
    55.                 switch (randomizer)
    56.                 {
    57.                     case 0:
    58.                         movementDirection.x += 1.0f;
    59.                         break;
    60.                     case 1:
    61.                         movementDirection.x -= 1.0f;
    62.                         break;
    63.                     case 2:
    64.                         movementDirection.y += 1.0f;
    65.                         break;
    66.                     case 3:
    67.                         movementDirection.y -= 1.0f;
    68.                         break;
    69.                     default:
    70.                         movementDirection = Vector2.zero;
    71.                         break;
    72.                 }
    73.             }
    74.  
    75.             movementSpeed = Mathf.Clamp(movementDirection.magnitude, 0.0f, 1.0f);
    76.             movementDirection.Normalize();
    77.         }
    78.         else
    79.             movementFrequenceCounter += movementFrequence;
    80.     }
    81.  
    82.     void Move()
    83.     {
    84.         if (shouldMove)
    85.         {
    86.             if (movementDurationCounter < movementDuration)
    87.             {
    88.                 npcRB.velocity = movementDirection * movementSpeed * movementBaseSpeed;
    89.                 movementDurationCounter += Time.deltaTime;
    90.             }
    91.             else
    92.             {
    93.                 movementDurationCounter = 0.0f;
    94.                 shouldMove = false;
    95.                 npcRB.velocity = Vector2.zero;
    96.                 movementSpeed = 0.0f;
    97.             }
    98.         }
    99.     }
    100.  
    101.  
    102.  
    103.     void OnCollisionEnter2D(Collision2D collision)
    104.     {
    105.         tackled = true;
    106.         if (collision.transform.tag == "Player")
    107.         {
    108.             Vector2 positionRelative = transform.InverseTransformPoint(collision.transform.position);
    109.             movementDirection = positionRelative;
    110.         }
    111.     }
    112.  
    113.     private void OnCollisionStay2D(Collision2D collision)
    114.     {
    115.         if (!(collision.transform.tag == "Player"))
    116.             tackled = false;
    117.     }
    118.  
    119.     private void OnCollisionExit2D(Collision2D collision)
    120.     {
    121.         tackled = false;
    122.     }
    123. }
    124.  


    Tell me if you have any questions.

    KR,
    blu3
     
    Last edited: Nov 3, 2019
    pertusin likes this.
  23. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    833
    You can try to freeze the Rigidbody's constraints upon collision:

    Code (CSharp):
    1. // Inside of your OnCollisionEnter()
    2.  
    3. playerRb.constraints = RigidbodyConstraints.FreezePositionX;
    4.  
    5. // Inside of your OnCollisionExit()
    6.  
    7. playerRb.constraints = RigidbodyConstraints.None;
    I haven't tested this so not sure how it will work. This was very interesting to research though! If you need to re-freeze you Z-axis, you can utilize the OnCollisionEnter code. I'm just assumming the .None will clear all the tics on the constraint list. Also note there are Rotation constraint freezes as well. If you freeze everything, according to the Docs you can use code like this:

    Code (CSharp):
    1. playerRb.constraints = RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezeRotationX;
     
  24. optimusdag

    optimusdag

    Joined:
    Mar 22, 2020
    Posts:
    3
    I'm a noob in game dev and I just run into this same issue. I finally increased the enemy mass to 100 times the player mass, and that did the trick for me.
     
    FenixShadow, GarMatt_ and wlwl2 like this.
  25. BBaajour

    BBaajour

    Joined:
    Jan 2, 2020
    Posts:
    1
    I know this thread is old, and my solution is not an exact answer to the question, however, I believe this could be of benefit to someone dealing with this issue.

    The solution with changing an object's mass might be the solution for your project, however, it does not completely solve this issue. This will cause the object with the large mass to be almost unmovable, however, it would be able to move all other objects with ease. If you just want exactly one object to not be movable, then this is definitely the way to go.

    However, if you want to have both objects not have the ability to move one another, a solution that works well would be using rigidBody to move one object, and moving the other using transform.position (don't give it a rigidBody component). Then you just add a conditional check that stops the motion when a collision is occurring. Depending on the needs of your project, this might need more refinement.

    The object without the rigidBody would have the benefit of properly interacting with other colliders. This is mainly why I chose this solution rather than using a kinematic rigidBody.

    I know this is not the solution for many projects that benefit from the other features rigidBody offers, however, it is probably the most convenient and efficient way for many games (especially in the context of 2D projects).

    I hope this helped!
     
  26. TragicallyCanadian

    TragicallyCanadian

    Joined:
    Mar 26, 2020
    Posts:
    39
    here's a video solution.
     
    Rachan likes this.
  27. roywax

    roywax

    Joined:
    Dec 12, 2020
    Posts:
    2
    Hello TragicallyCanadian,
    I tried this solution and variants of it.
    It is "almost" working.
    I mean, at first i thought the issue was the Polygon collider 2D, so i switch both colliders to circle colliders.
    Making the blocker bigger circle than the character circle collider.
    But no matter what i do,1 of my players seems to "push" the other.
    I am not sure it is actually pushing it, no velocity or something similar.
    I think the blocker collider is actually overlapping the rigid body collider at some point so the rigid body just "moves" out of the way, seeming to be pushed.
    How can this be?
    What can be done to prevent this?
    Thanks
    Roy
     
    Last edited: Jan 5, 2021
    jdell64 likes this.
  28. roywax

    roywax

    Joined:
    Dec 12, 2020
    Posts:
    2
    Hello,
    In the end i think i finally gave up on all of these solutions since they do not really work
    The presence of the rigid body is causing the physics to be active in the game and i simply do not need it
    So currently i am looking at character controller and no rigid body,
    since it is 3d i had to to replace all my colliders with 3d versions and now at least this part works,
    players collide and do not push each other,
    Now i have other issues which i have still do not know to solve,
    primarily player not collider with tilemap.
    Anything to do about this with again returning the rigid body? (which i do not want)
     
  29. boltbanks

    boltbanks

    Joined:
    Sep 18, 2021
    Posts:
    2
    I'm feeling really bad because I'm so late reading this, if you would like, use linear drag, set it high, and the mass that's it. I'm saying this because, it's not adding anything extra to the game if you have a lot of enemies or players it can start costing a lot.
     
    Last edited: Oct 19, 2021
  30. boltbanks

    boltbanks

    Joined:
    Sep 18, 2021
    Posts:
    2
    If you would like, use linear drag, set it high and the mass.
     
    Last edited: Oct 19, 2021
  31. snowinrain

    snowinrain

    Joined:
    Jan 17, 2016
    Posts:
    15
    This worked for me. Thanks
     
  32. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    This doesn't work. As @roywax said, the kinematic rigid body is pushing apart the dynamic rigid body, resulting in one of them being able to push the other, and the other not being able to push the first one.
     
  33. John_Leorid

    John_Leorid

    Joined:
    Nov 5, 2012
    Posts:
    651
    This seems like a simple problem to solve.
    First thing needed is obviously the detection of character x character collisions.
    Both of them are driven by scripts - so their velocity is already controlled somewhere.

    Once such a collision happens - just get the forces towards a character and nullify it.
    For this, you'll need a vector ->
    Vector3 dirVec = characterB.position - characterA.position;

    Then just get the velocity in direction by using
    Vector3.Project()
    ->
    Vector3 velocityInDirection = Vector3.Project(myRigidbody.velocity, dirVec);

    Check if the velocity is pointing towards the other character
    if(Vector3.Dot(velocityInDirection.normalized, dirVec.normalized) > 0)

    if so, just substract it
    myRigidbody.velocity -= velocityInDirection;

    and maybe add some counter force, so they seperate after contact
    myRigidbody.velocity += -dirVec * counterForce;


    ELSE, when the character wasn't moving at all, correct the small offset that happend on the first impact by moving the character back to it's initial position. This is optional and just a small correction. So you'd have to track the "initial position" whenever a character has no velocity, then move it back to this position when there was an impact with another character.

    OR .. use the new contacts modification API to override contacts between characters entirely, only adding forces to moving bodies and only using counter forces.
     
  34. hisnamehappenstobe

    hisnamehappenstobe

    Joined:
    Feb 2, 2022
    Posts:
    3
    Was amazed at how well this worked. I used this direct implementation on my character class, which actually instantiates a pure copy from a prefab, and it actually made all the appropriate associations with-out any extra coding. Thankyou!