Search code examples
c#unity-game-engine2dcollision-detection

Unity Box Collider 2D IsTrigger still colliding


I have a 2D Rectangular Tilemap in Unity which has a Tilemap Collider 2D component. Then I also have a GameObject Player with a Rigidbody 2D and two Box Colliders 2D. One of the Box Colliders has the default settings and the second one is bigger and is set to IsTrigger. The Player is linked with a Prefab of the Player and it has the exact same Components.

So my problem is that the second Collider still collides with the Tilemap Collider.

Player GameObject

Tilemap Collision

Function to pick up the Items

private void OnTriggerEnter2D(Collider2D other) 
{
   Item item = collider.GetComponent<Item>();
            
   if (item) 
   {
      foreach (ItemWrapper itemWrapper in item.items) 
      {
          inventory.addItem(itemWrapper.item, itemWrapper.amount);
      }

      if (item.getsDestroyed) 
      {
          Destroy(collider.gameObject);
      }
      else 
      {
          item.items = new List<ItemWrapper>();
      }
    }
}

Function to check if I can move

void FixedUpdate(){
        float currentMoveSpeed = moveSpeed;

        if(_movementInput != Vector2.zero){
            Vector3 playerPosition = transform.position;
            if (IsOnPathTilemap(playerPosition)) {
                currentMoveSpeed *= boostedSpeedMultiplier;
            }
            
            bool success = TryMove(_movementInput, currentMoveSpeed);
            if(!success) {
                success = TryMove(new Vector2(_movementInput.x, 0), currentMoveSpeed);
            }
            if(!success) {
                success = TryMove(new Vector2(0, _movementInput.y), currentMoveSpeed);
            }

            // Set Animation-Type
            if (success) {

                if (_movementInput.x < 0 || _movementInput.x > 0) { // Horizontal
                    _animator.SetInteger("isMovingDirection", 2);
                    
                } 

                else if (_movementInput.y < 0) { // Down
                    _animator.SetInteger("isMovingDirection", 1);
                } else if (_movementInput.y > 0){ // Up
                    _animator.SetInteger("isMovingDirection", 3);
                } 
                
            } else {
                _animator.SetInteger("isMovingDirection", 0);
            }
            
        } else {
            _animator.SetInteger("isMovingDirection", 0);
        }
        // Set direction of sprite to movement direction
        if(_movementInput.x < 0) {
            _spriteRenderer.flipX = true;
        } else if (_movementInput.x > 0) {
            _spriteRenderer.flipX = false;
        }
    }

private bool TryMove(Vector2 direction, float speed) {
        if(direction != Vector2.zero) {
            // Check for potential collisions
            int count = _rb.Cast(
                direction, // X and Y values between -1 and 1 that represent the direction from the body to look for collisions
                movementFilter, // The settings that determine where a collision can occur on such as layers to collide with
                castCollisions, // List of collisions to store the found collisions into after the Cast is finished
                speed * Time.fixedDeltaTime + collisionOffset); // The amount to cast equal to the movement plus an offset

            if(count == 0){
                _rb.MovePosition(_rb.position + direction * (speed * Time.fixedDeltaTime));
                return true;
            } else {
                return false;
            }
        } else {
            // Can't move if there's no direction to move in
            return false;
        }
    }

I tried to fix this by modifying the Layer Overrides, removing the collision from the Prefab, disabling all the collisions in the Layer Collision Matrix, changing the Layers of the GameObject and Tilemap and some more stuff.

You can probably see that I'm new to Unity, so i dind't really understand that much of what i did. Of course I googled, trying to understand how the whole thing works but I wasn't successful.

Thanks very much for your time in advance.


Solution

  • Instead of trying to handle both things on the same object I would rather move your collider to its own object

    Player (non-Trigger)
    |-- Item Detector (IsTrigger)
    

    an then have your component find and apply values to the player component

    public class ItemDetector : MonoBehaviour
    {
        [SerializeField] private PlayerController payerController;
    
        private void Awake()
        {
            FetchComponents();
        }
    
        private void Reset()
        {
            FetchComponents();
        }
    
        private void OnTriggerEnter2D(Collider2D other)
        {
            if(!other.TryGetComponent<Item>(out var item)) return;
            
            foreach(var itemWrapper in item.items) 
            {
                // TODO implement this in PlayerController 
                payerController.AddToInventory(itemWrapper.item, itemWrapper.amount);
            }
    
            // This I would even implement inside the Item class itself
            // There is no need to expose the "getsDestroyed"
            if (item.getsDestroyed) 
            {
                Destroy(item.gameObject);
            }
            else 
            {
                // Instead of deleting and recreating a new list (-> GC waste)
                // rather stick to the existing one and only empty it
                // This could then be private and to the public you only expose an IReadOnlyList
                // so it can't be modified from the outside
                item.items.Clear();
            }
        }
    
        [ContextMenu(nameof(FetchComponents))]
        private void FetchComponents()
        {
            if(!payerController) payerController = GetComponentInParent<PlayerController>();
    
    #if UNITY_EDITOR
            if(!Application.isPlaying)
            {
                UnityEditor.EditorUtility.SetDirty(this);
            }
    #endif
        } 
    }
    

    Now the Player and Item Detector object can be on different layers and you can configure the layer collision matrix accordingly