Search code examples
c#unity-game-enginecollision-detectionrigid-bodies

How to stop Rigidbody on collision in unity


When my RigidBody collides with Collison object the event is triggered but the object is not always stopped. It depends on the on the speed it has but that for me is not acceptable. I suspect it has something to do with how Update works but i cant get arund that.

this is the code

public class Mover : MonoBehaviour
{
    private Rigidbody _rb;
    private bool _isColliding;
    [SerializeField] private float moveSpeed = 1f;
    
    // Start is called before the first frame update
    void Start()
    {
        _rb = GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void Update()
    {
        MovePlayer();
    }
    
    void MovePlayer()
    {

        float xValue = Input.GetAxis("Horizontal") * moveSpeed;
        float zValue = Input.GetAxis("Vertical") * moveSpeed;
        
        
        Vector3 movement = new Vector3(xValue, 0f, zValue);
        
        _rb.MovePosition(_rb.position + movement * Time.fixedDeltaTime);

    }

    
    private void OnCollisionEnter(Collision collision)
    {
        _isColliding = true;
        _rb.velocity = Vector3.zero;
        _rb.angularVelocity = Vector3.zero;
    }

}

but when debugging _rb.velocity _rb.angularVelocity seem to already be at zero

What i tried was

  • setting walls to static
  • adding new Physic Material as suggested in older threads
  • i have resized wall collison boxes so that they dont overlap
  • I have set collision detection on rigidBody to Continous
  • tried to catch and handle the event on the "wall" side.
 public class WallHit : MonoBehaviour
{
    private void OnCollisionEnter(Collision collision) 
    {

        if (collision.gameObject.CompareTag("Player"))
        {
            Rigidbody rbdy = collision.gameObject.GetComponent<Rigidbody>();

            rbdy.velocity = Vector3.zero;

            rbdy.angularVelocity = Vector3.zero;
        }
    }

}
  • tried with _rb.isKinematic = true; OnCollisionEnter, but it only slows it down permanently
  • and also tried to handle it with raycast
void Update()
{
    // Check for player input
    if (!IsColliding())
    {
        MovePlayer();
    }
}

bool IsColliding()
{
    // Calculate movement vector based on player input
    float xValue = Input.GetAxis("Horizontal") * moveSpeed;
    float zValue = Input.GetAxis("Vertical") * moveSpeed;
    Vector3 movement = new Vector3(xValue, 0f, zValue);

    // Perform a collision check using a Raycast
    RaycastHit hit;
    if (Physics.Raycast(transform.position, movement, out hit, movement.magnitude * Time.deltaTime))
    {
        // If a collision is detected, return true
        return true;
    }

    // If no collision is detected, return false
    return false;
}

void MovePlayer()
{
    // Move the player
    float xValue = Input.GetAxis("Horizontal") * moveSpeed;
    float zValue = Input.GetAxis("Vertical") * moveSpeed;
    Vector3 movement = new Vector3(xValue, 0f, zValue);
    _rb.MovePosition(_rb.position + movement * Time.fixedDeltaTime);
}

i am runnign out of ideas and i refusing to belive this cannot be handled


Solution

  • It can be difficult to handle collisions at high speeds due to physical timing constraints in Unity3d, so I modified the code so I can set a minimum and maximum speed and the speeds I tested seem to work as intended, so try it for yourself.

        private Rigidbody _rb;
    
        [SerializeField] private float moveSpeed = 1f;
        [SerializeField] private float maxSpeed = 10f; 
    
        void Start()
        {
            _rb = GetComponent<Rigidbody>();
        }
    
        void Update()
        {
            MovePlayer();
        }
    
        void MovePlayer()
        {
            float xValue = Input.GetAxis("Horizontal") * moveSpeed;
            float zValue = Input.GetAxis("Vertical") * moveSpeed;
    
            Vector3 movement = new Vector3(xValue, 0f, zValue);
    
            _rb.AddForce(movement, ForceMode.Acceleration);
    
            _rb.velocity = Vector3.ClampMagnitude(_rb.velocity, maxSpeed);
        }
    
        private void OnCollisionEnter(Collision collision)
        {
            if (Vector3.Dot(_rb.velocity.normalized, collision.contacts[0].normal) < -0.5f)
            {
                _rb.velocity = Vector3.zero;
                _rb.angularVelocity = Vector3.zero;
            }