Search code examples
c#unity-game-enginegame-physics

2D Object Collison Unity


I'm developing a simple 2D game in Unity, and I have run into a problem when dealing with collision. I have two objects, a tree and a player. The tree doesn't move, and is represented by some sprites and a polygon collider. The player moves using a custom script (NOT the character controller), and has a kinematic Ridgidbody and polygon collider attached.

My intended behavior would be for the player to 'collide' with the tree and be blocked by it, so none of the objects could be able to move. However, it doesn't seem to be a simple way of doing this.

Setting the tree's RidgidBody component to 'static' or 'dynamic' results in no collision being detected. I considered making the player a 'dynamic' rigid body, but the unity docs suggest that dynamic rigidbodies should not be moved by their transform component, which is how my current system works. Additionally, setting it to dynamic results in unintended behavior where the player freezes for no reason, and since no physics will be applied on the player object, it seems like a bad use case for dynamic. I could just be wrong about this though.

I could possibly use a script to somehow lock the player position when a collider event is triggered, but this seems very hacky. Can anyone provide some insight on how to handle this?


Solution

  • Well apparently the 2D colissions are a little bit buggy. Here is some approach to avoid this issues. Basically instead of relying on colliders, it uses a raycast to check if there is an obstacle where the player is attempting to move

    using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;
    
    public class Player : MovingObjects {
    
        protected override void AttemptMove<T> (int xDir, int yDir)
        {
            base.AttemptMove<T> (xDir, yDir);
            RaycastHit2D hit;       
        }
        protected override void onCantMove<T>(T component)
        {
            Wall hitwall = component as Wall;
            hitwall.DamageWall (wallDamage);        
        }
    
        // Update is called once per frame
        void Update () {
    
            int horizontal = 0;
            int vertical = 0;
    
            horizontal = (int)Input.GetAxisRaw ("Horizontal");
            vertical = (int)Input.GetAxisRaw ("Vertical");
    
            if (horizontal != 0)
                vertical = 0;
    
            if (horizontal != 0 || vertical != 0)
                AttemptMove<Wall> (horizontal, vertical);
        }
    }
    

    Which inherits from:

    using UnityEngine;
    using System.Collections;
    
    public abstract class MovingObjects : MonoBehaviour {
    
        public float moveTime = 0.1f;
        public LayerMask blockingLayer;
    
        private BoxCollider2D boxCollider;
        private Rigidbody2D rb2D;
        private float inverseMoveTime;
    
        protected virtual void Start()
        {
            boxCollider = GetComponent<BoxCollider2D> ();
            rb2D = GetComponent <Rigidbody2D>();
            inverseMoveTime = 1f / moveTime;
    
        }
    
    
        protected IEnumerator SmoothMovement(Vector3 end)
        {
            float sqrRemaininDistance = (transform.position - end).sqrMagnitude;
    
            while (sqrRemaininDistance > float.Epsilon) {
    
                Vector3 newPosition = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime*Time.deltaTime);
    
                rb2D.MovePosition(newPosition);
                sqrRemaininDistance = (transform.position - end).sqrMagnitude;
    
                yield return null;
            }
        }
    
        protected bool Move(int xDir, int yDir, out RaycastHit2D hit)
        {
            Vector2 start = transform.position;
            Vector2 end = start + new Vector2 (xDir, yDir);
    
            boxCollider.enabled = false;
    
            hit = Physics2D.Linecast (start, end, blockingLayer);
    
            boxCollider.enabled = true;
    
            if (hit.transform == null) {
    
                StartCoroutine(SmoothMovement(end));
                return true;
            }
    
            return false;
        }
    
        protected virtual void AttemptMove<T>(int xDir, int yDir)
                                where T : Component
        {
    
            RaycastHit2D hit;
            bool canMove = Move (xDir, yDir, out hit);
    
            if (hit.transform == null)
                return;
    
    
            Debug.Log ("Something hit", gameObject);
    
            T hitComponent = hit.transform.GetComponent<T> ();
    
            if (!canMove && hitComponent != null)
                onCantMove (hitComponent);
    
    
        }
    
        protected abstract void onCantMove<T>(T component)
                           where T: Component;
    
    }
    

    This script belongs to a tutorial of the oficial Unity website. A 2D game called Rogue. Here is the link just in case you are planning to do something similar:

    https://unity3d.com/es/learn/tutorials/projects/2d-roguelike-tutorial