Snake Game Collision Detection Problem

Snake Game Collision Detection Problem

Issue Description

In my 2D snake game, I’m experiencing issues with collision detection between two snake characters. The snakes occasionally pass through each other instead of colliding and changing direction as intended.

Current Behavior

  • Snakes sometimes overlap or intersect during gameplay
  • Collision detection appears inconsistent, especially during fast movements
  • OnTriggerEnter2D is triggered, but doesn’t always prevent intersection

Expected Behavior

  • Snakes should never pass through each other
  • Upon collision, snakes should change direction or bounce off each other
  • Collision detection should be consistent and reliable, even at high speeds

Technical Details

  • Using Unity 2D
  • Snakes are composed of multiple segments (head + body parts)
  • Each segment has a Collider2D (CircleCollider2D) and Rigidbody2D
  • Movement is grid-based, controlled by joystick input
  • Current collision detection uses OnTriggerEnter2D

Attempted Solutions

  • Implemented custom collision logic in OnTriggerEnter2D
  • Tried adjusting collider sizes and physics settings
  • Experimented with different collision detection modes

snippet code for player :

    public float gridSize = 1f;
    public float moveInterval = 0.1f;
    public Joystick joystick;
    public GameObject bodyTmp;
    public float collisionCooldown = 0.5f;
    public float pushForce = 10f;

    private Vector2 direction = Vector2.right;
    private Vector2 nextPosition;
    private float highLimit;
    private float widthLimit;
    private bool isCollided = false;
    private Rigidbody2D rb;

    private void Start()
    {
        highLimit = Camera.main.orthographicSize;
        widthLimit = highLimit * Camera.main.aspect;
        nextPosition = transform.position;
        rb = GetComponent<Rigidbody2D>();
        rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
        InvokeRepeating(nameof(Move), moveInterval, moveInterval);
    }

    private void Update()
    {
        if (!isCollided)
        {
            HandleInput();
        }
    }

    private void HandleInput()
    {
        Vector2 inputDirection = new Vector2(joystick.Horizontal, joystick.Vertical).normalized;
        if (inputDirection != Vector2.zero)
        {
            direction = inputDirection;
        }
    }

    private void Move()
    {
        if (isCollided) return;

        nextPosition += direction * gridSize;
        rb.MovePosition(nextPosition);
        RotateHead();
    }

    private void RotateHead()
    {
        float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
        transform.rotation = Quaternion.Euler(new Vector3(0, 0, angle - 90));
    }

    public void ChangeDirection()
    {
        transform.Rotate(0, 0, 180);
        direction *= -1f;
        nextPosition += direction * 0.5f;
        rb.MovePosition(nextPosition);
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("HeadEnemy") || collision.gameObject.CompareTag("bodyEnemy") || collision.gameObject.CompareTag("bodyExtension"))
        {
            HandleCollision(collision);
        }
    }

    private void HandleCollision(Collision2D collision)
    {
        if (isCollided) return;

        Vector2 collisionNormal = collision.contacts[0].normal;
        Vector2 reflectDirection = Vector2.Reflect(direction, collisionNormal).normalized;

        direction = reflectDirection;
        nextPosition = transform.position + (Vector3)(direction * gridSize);

        rb.velocity = Vector2.zero;
        rb.AddForce(reflectDirection * pushForce, ForceMode2D.Impulse);

        isCollided = true;
        StartCoroutine(CollisionCooldown());
    }

    private IEnumerator CollisionCooldown()
    {
        yield return new WaitForSeconds(collisionCooldown);
        isCollided = false;
    }

Any insights or solutions would be greatly appreciated. Thank you!

This likely isn’t helping you:

Either let the physics move the snakes by setting their velocities, or else call your Move() method from FixedUpdate() if you insist on driving the physics yourself.

Line 54 may also cause issues, as it rotates something directly by the transform, bypassing physics. It might not matter if your collider is perfectly circular, but then again it might as you are bypassing physics and making it have to catch up, leading to errors.

If you use physics, pretty much you need to 100% use the physics API:

With Physics (or Physics2D), never manipulate the Transform directly. If you manipulate the Transform directly, you are bypassing the physics system and you can reasonably expect glitching and missed collisions and other physics mayhem.

This means you may not change transform.position, transform.rotation, you may not call transform.Translate(), transform.Rotate() or other such methods, and also transform.localScale is off limits. You also cannot set rigidbody.position or rigidbody.rotation directly. These ALL bypass physics.

Always use the .MovePosition() and .MoveRotation() methods on the Rigidbody (or Rigidbody2D) instance in order to move or rotate things. Doing this keeps the physics system informed about what is going on.

1 Like

Thank you for your answer, I will refactor the code depending on your feedback.