1. Help us improve the editor usability and artist workflows. Join our discussion to provide your feedback.
    Dismiss Notice
  2. We're looking for feedback on Unity Starter Kits! Let us know what you’d like.
    Dismiss Notice
  3. We’re giving 2017.1 beta testers a chance to win t-shirts and a Nintendo Switch. Read more on the blog.
    Dismiss Notice
  4. We want to know how you learned Unity! Help us by taking this quick survey and have a chance at a $25 gift card
    Dismiss Notice
  5. Are you an artist or level designer going to Unite Europe? Join our roundtables there to discuss artist features.
    Dismiss Notice
  6. Unity 5.6 is now released.
    Dismiss Notice
  7. Check out all the fixes for 5.6 on the patch releases page.
    Dismiss Notice

[2d] [Isometric] - How to disable pushing between two characters

Discussion in 'Physics' started by Bujakowski, Jun 16, 2017.

  1. Bujakowski

    Bujakowski

    Joined:
    Nov 22, 2016
    Posts:
    6
    I've got a similar problem to the one described over here:
    https://forum.unity3d.com/threads/unity-2d-avoid-pushing-between-rigidbody.298183/
    Though it's kind of unsolved.

    *** The problem ***
    How do I make player 1 stop at player 2 (and vice versa) without pushing one another? I still want them to collide with other objects with 2d box colliders.

    *** DEMO ***
    I'll demonstrate the problem:


    Now some code:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Player : MonoBehaviour {
    6.     private Rigidbody2D body;
    7.     private float horizontalInput;
    8.     private float verticalInput;
    9.     private float speed = 10;
    10.  
    11.     void Start () {
    12.         body = GetComponent<Rigidbody2D>();
    13.     }
    14.    
    15.     void FixedUpdate () {
    16.         horizontalInput = Input.GetAxisRaw(gameObject.name + "_Horizontal");
    17.         verticalInput = Input.GetAxisRaw(gameObject.name + "_Vertical");
    18.         MoveCharacter(horizontalInput, verticalInput);
    19.     }
    20.  
    21.     private void MoveCharacter(float horizontalInput, float verticalInput)
    22.     {
    23.         Vector2 direction = new Vector2(horizontalInput, verticalInput);
    24.  
    25.         if (direction.x != 0 || direction.y != 0)
    26.         {
    27.             body.MovePosition((Vector2) gameObject.transform.position + direction * speed * Time.deltaTime);
    28.         }
    29.     }
    30. }
    31.  
    Finally the prefabs config:
    [​IMG]

    *** What I've tried / what won't work ***
    - Changing body type to Kinematic - they don't push each other but they don't collide at all,
    - Tried CharacterController - no difference obviously =)
    - Changing mass - this only works one way

    This is such a common problem I think that maybe in unity 5.6 there is finally a sollution =)?

    Thanks for all advice,
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,164
    Terminology is important here. You're saying "collide" but it's not obvious if you mean "come into contact" or the "collision response to coming into contact". The "pushing" you're talking about is a collision response so it seems you're saying you don't want a collision response and only want to know if two things come in contact, presumably by getting a physics callback or using the GetContacts calls?

    If you don't want a collision response then you must use a Kinematic body as this is what this is for as it doesn't respond to forces. If you want two Kinematic bodies to produce a contact i.e. "collide" and get OnCollisionXXX2D calls then set Rigidbody2D.useFullKinematicContacts to true.

    This option appears when you set the Body Type to Kinematic.
     
  3. Bujakowski

    Bujakowski

    Joined:
    Nov 22, 2016
    Posts:
    6
    I've tried setting Rigidbody2D.useFullKinematicContacts to true and I've read some docs and some other forum questions, but still no luck. I have, however encountered this bug topic:
    https://issuetracker.unity3d.com/is...nematic-contacts-on-rigidbody2d-has-no-effect
    Could this bug be my problem?

    So far the only possible solution I found is to detect collison with another object and manually set flags cannotMoveRight, cannotMoveLeft, cannotMoveTop, cannotMoveBottom depending on the collison's direction. However, I'm not sure what's the best to implement this.

    Thanks for your reply anyway =)
     
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,164
    That issue you refer to is just plain wrong. As I've said, Full kinematic contacts allows contacts to be created between kinematic bodies so you'll get callbacks and you can retrieve contacts using GetContacts. Without that you won't get either.

    If you expect the bodies to produce a collision response then you're fundamentally misunderstanding as that is what a dynamic body type is!

    Could you ellaborate? What were you expecting?
     
  5. Bujakowski

    Bujakowski

    Joined:
    Nov 22, 2016
    Posts:
    6
    When setting type to kinematic the characters move through each other. Well I wasn't expecting too much

    I want Player 1 (red knight) not to be able to move to Player 2's (blue knight) position and not to push him. Just to stop.

    I'm not sure whether you understand what I'm trying to achieve? I'm really just poking around with unity, any (non-hacky) solution will work for me if you just point me in the right direction. Am I correct to assume that I need to implement this manually by collision detection?

    #2 How Do I use GetContacts? I've got something like this, though obviously the argument(s) is wrong.

    Code (CSharp):
    1. void OnCollisionEnter2D(Collision2D coll) {
    2.     Collider2D.GetContacts(coll.contacts);
    3. }
     
  6. eXonius

    eXonius

    Joined:
    Feb 2, 2016
    Posts:
    110
    Try just setting both knights velocity to 0 in OnCollisionEnter.

    I also wonder what happens if you make a gameobject with kinematic rigidbody move along with the non-kinematic rigidbody, but set its layer to only interact with the other Knight.
     
    Last edited: Jun 18, 2017
  7. Bujakowski

    Bujakowski

    Joined:
    Nov 22, 2016
    Posts:
    6
    Already tried that through:
    Code (CSharp):
    1. void OnCollisionEnter2D(Collision2D coll) {
    2.     body.velocity = Vector2.zero;
    3. }
    But nothing changes :/

    This may work but only for 2 players, what if I want to add more players? I need a more general approach but still, thanks for you suggestions.
     
  8. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,190
    Have the same issue, but in 3D. I simply want the same behaviour From Software have been doing for almost a decade in the Dark Souls/Bloodborne games.

    Characters never push each other, but still collide with each other.

    The environment may push characters (traps, walls, door). Characters can push debris or breakable objects.

    Unknown number of character, unknown velocity, animation driven motion.

    It should be super simple, but it appears totally impossible in Unity.
     
  9. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,164
    It's not impossible, you're asking for kinematic motion and it's used all the time. You want a body that doesn't response to forces and with kinematics you need to determine collision yourself and move appropriately. I do not know your movement requirements but here's a simple example I just put together.

    - Add two GameObject, each with a Rigidbody2D (set to be Kinematic) and a CircleCollider2D. Add the following script to one of them so that it moves. It uses Rigidbody2D.MovePosition to move but it doesn't just move there, it first checks if it can move there. It does this with a single "Cast" call and then checks the returned hits to see if they are a valid move. In my code a valid move is if the move is non-zero distance. We also finish and don't move at all if a tiny or zero distance is moving into the contact. This is just one of many movement models you can adopt but it's all centered around the same thing; using Kinematic bodies, checking for collision then using MovePosition/MoveRotation to move if appropriate (determined by your game).

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Move : MonoBehaviour {
    4.  
    5.     public float Speed = 1;
    6.     private Rigidbody2D m_Rigidbody;
    7.     private RaycastHit2D[] m_Contacts = new RaycastHit2D[100];
    8.  
    9.    // Use this for initialization
    10.    void Start ()
    11.     {
    12.         m_Rigidbody = GetComponent<Rigidbody2D>();
    13.    }
    14.  
    15.    // Update is called once per frame
    16.    void FixedUpdate ()
    17.     {
    18.         // Set initial velocity as zero.
    19.        var velocity = Vector2.zero;
    20.  
    21.         // Do some crude movement.
    22.         if (Input.GetKey(KeyCode.LeftArrow))
    23.         {
    24.             velocity.x = -Speed;
    25.         }
    26.         if (Input.GetKey(KeyCode.RightArrow))
    27.         {
    28.             velocity.x = Speed;
    29.         }    
    30.         if (Input.GetKey(KeyCode.UpArrow))
    31.         {
    32.             velocity.y = Speed;
    33.         }    
    34.         if (Input.GetKey(KeyCode.DownArrow))
    35.         {
    36.             velocity.y = -Speed;
    37.         }
    38.  
    39.         velocity *= Time.deltaTime;
    40.         var direction = velocity.normalized;
    41.  
    42.         // Find contacts along our movement direction.
    43.         var resultCount = m_Rigidbody.Cast(direction.normalized, m_Contacts, velocity.magnitude);
    44.  
    45.         // We need to find a hit where we're actually moving.
    46.         for(var i = 0; i < resultCount; ++i)
    47.         {
    48.             var contact = m_Contacts[i];
    49.             var distance = contact.distance;
    50.  
    51.             // Are we actually moving?
    52.             if (distance > Mathf.Epsilon)
    53.             {
    54.                 // Yes, so schedule the move.
    55.                 m_Rigidbody.MovePosition(m_Rigidbody.position + (direction * distance));
    56.                 return;
    57.             }
    58.             // If we're moving into a contact then finish as we cannot move.
    59.             else if (Vector2.Dot(contact.normal, direction) < 0)
    60.                 return;
    61.         }
    62.  
    63.         // No contact was found so move the full velocity.
    64.         m_Rigidbody.MovePosition(m_Rigidbody.position + velocity);
    65.    }
    66. }
    67.  
    68.  
    https://gyazo.com/dbdd885b304854f3799713b04830a094
     
  10. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,190
    We want it to respond to forces, just not forces applied by a specific physic layer.

    We want a character that works like any other character, moving around, pushing object, colliding with walls, pushed by movers, explosion, attacks... Except it cannot push or by pushed by other characters.
     
  11. Bujakowski

    Bujakowski

    Joined:
    Nov 22, 2016
    Posts:
    6
    @MelvMay - Nice, this looks like it may actually work, still a pity we can't do it out of the box. Will try it in a couple of days when I find some free time and give feedback whether it works for my situation.
     
  12. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,164
    So to stay with Dynamic bodies, it'd need deep integration into the solver for 2D (Box2D) and I can only presume the same for 3D (PhysX). I would consider it as an option for 2D but I'm not even sure it's possible via PhysX, I'd need to ask one of the 3D physics guys. You're saying be Dynamic on a certainly layer but act Kinematic on another although still solve overlaps as per a Dynamic body.

    I had a look but couldn't see this as a request in the feedback on the site, maybe it's there but I can't see it so it doesn't seem to be a commonly requested thing. Maybe there's lots of posts asking for it though but I'm not aware of them.
     
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,164
    Another issue to consider is that if continuous col-det isn't used for such a feature then things will get overlapped with discrete col-det. The physics system will solve that overlap by repositioning the body. If something continues to move into overlap then it'll look like it's "pushing" because both will move out of overlap. You cannot stop this otherwise it'll just act like a Kinematic body allowing overlap. The only way to get around this is to always use continuous collision detection for the dynamic body which is much more expensive so this feature won't scale well.
     
  14. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,190
    If you search for "Rigidbody not pushing" or "Character not pushing", it's a topic that comes back quite often in one wording or another. Usually Dark Souls is listed as an example. In 2D, the problem is most likely immensely easier then in 3D.

    I'm kinda surprised PhysX doesn't offer any way to affect the energy transferred on a collision. Like... energy transfer between Layer A and B is 0%.

    I see where you're going with the repositionning issue. If you had to perfectly duplicate the Dark Souls character collision system in Unity, how would you do it?
     
  15. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,164
    I know nothing about "Dark Souls" nor do I see how that is relevant. What you want is clear however you're going to struggle doing that with Unity as is. I'm NOT the 3d physics guy and do not know what might help you achieve that in the future. Maybe some other users have had more success with various techniques, I'm not sure. It's certainly the first time I've seen the feature request.
     
  16. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,190
    Well, I said Dark Souls, simply because it's probably the most well-known third person RPG where a character cannot push another.

    Thanks for your help. Hopefully someone had or will have more luck.
     
  17. VergilUa

    VergilUa

    Joined:
    Dec 22, 2014
    Posts:
    32
    You can also try increasing mass and drag of objects.
     
  18. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,190
    Only works one way. Character A cannot push B, but then B can very easily push A.

    As for friction, only works if the contact is maintained with the floor.
     
    Last edited: Jun 20, 2017 at 2:58 PM
  19. Bujakowski

    Bujakowski

    Joined:
    Nov 22, 2016
    Posts:
    6
    @MelvMay your code works as expected, many thanks! It probably needs to be polished to ensure smooth gameplay but it's a good start.
     
    MelvMay likes this.