0
\$\begingroup\$

TLDR; How to stop movement if two Rigidbodies collides?

Also, here's a potato video of the problem: https://gfycat.com/ComposedArcticBetafish

Say I have three objects in a scene:

  1. Player
  2. Enemy
  3. Rock

The player and enemies is moving freely, but the rock is static. So I attached a Rigidbody2D on player and enemy to move them around with script. All of them has a Box Collider 2D.

Now, if I (Player) move into an Enemy, I will start to push it (changing its position). I don't want this. I can set the Enemy's mass to something large, but then two enemies can still push eachother. I don't want this. If I try kinematic solutions I've found, but then they can move through the rock.

I want:

  • Player can not move enemies
  • Enemies can not move players
  • Enemies can not move each other
  • Nothing can move or go through the rock
  • Enemies can not move through player
  • Player can not move through enemies

I keep reading that if I'm moving colliders, I should also use Rigidbodies for performance. But I can't see how I get this to work with Rigidbodies.

How do I set it up? Or should I move away from rigidbodies and set it up myself?

\$\endgroup\$
0
\$\begingroup\$

Add Player, Enemies, and Rocks to their own layers. Then in Edit->Project Settings->Physics 2D has a collision matrix containing layers that collide with other layers. Disable the collisions you don't want, then implement your custom collision handlers for the collisions that should be handled differently (i.e. player to enemy collisions)

Since rocks don't move based on physics interactions, they should probably be kinematic.

Edit:

Unity's physics engine is meant to model a 2D version of real physics. In the real world, if a "Player" pushes an "Enemy" hard enough, the enemy will move. You're asking how to make the player move into the enemy's collider space and then react to being hit (possibly by jumping backwards)-- this is not supported by Unity's physics because it is a simulation that will not allow two objects to occupy the same space. Whichever object has sufficient force to move the other's mass will do so. Therefore, your only option is to turn off collision handling between these two object types and implement it yourself.

You don't need to completely throw away the RigidBodies though. At least one of the colliding objects needs a RigidBody for OnCollisionEnter() or OnTriggerEnter() events to fire and you could use these events to do your own collision handling.

Lastly, I should note that I tried using some of Unity's physics for a 2D platformer and I couldn't get it to feel right. I ended up turning off the whole collision matrix and using raycasting to detect collisions with immovable objects. Though I did keep rigidbodies on my objects so that I could still use OnCollisionEnter() and OnTriggerEnter()

\$\endgroup\$
  • \$\begingroup\$ I can't see how this helps me at all. Basically just set it up myself, and don't use rigidbodies? Btw, the rocks don't have rigidbodies, only a collider. \$\endgroup\$ – stiq Oct 7 '16 at 10:10
  • \$\begingroup\$ I have updated the answer \$\endgroup\$ – spectacularbob Oct 7 '16 at 15:25
0
\$\begingroup\$

Here's a very simple way to prevent characters from pushing or interpenetrating kinematic or static colliders:

(here assuming they use circular colliders - you can do this equivalently with rectangular colliders or combinations of shapes too)

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(CircleCollider2D))]
public class NoPushMovement : MonoBehaviour {

    public float moveSpeed = 1f;

    Rigidbody2D _body;
    CircleCollider2D _collider;

    void Start () {
        _body = GetComponent<Rigidbody2D> ();
        _collider = GetComponent<CircleCollider2D> ();
    }

    void FixedUpdate () {
        // Compute desired movement in this physics step
        // (using whatever method is appropriate for your player or AI)
        Vector2 movement = new Vector2 (
            Input.GetAxis ("Horizontal"),
            Input.GetAxis ("Vertical")   );

        movement *= moveSpeed * Time.deltaTime;

        // Check for a collision along our path.
        RaycastHit2D collision = Physics2D.CircleCast (
                        transform.TransformPoint (_collider.center), 
                        _collider.radius, 
                        movement,
                        movement.magnitude);

        // Move only if the way is clear.
        if(collision.collider == null)
            _body.MovePosition(_body.position + movement);
    }
}

More sophisticated versions can retain the portion of the movement that's parallel to the collision plane, so you slide along obstacles instead of stopping dead, or allow a partial-timestep of movement when you're close but not quite touching the obstacle.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.