Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weird collision bug in Unity 2d game

Github Repository (Scripts folder, has all code in .cs files)

I have this weird collision bug in unity, here's a gif of it:

GIF

Recreating: In the gif, for example, I press both Left arrow and Up arrow until the velocity normalizes, and I get some why stuck in a block.

I've had this before with my own collision algorithm when I did the game in XNA, hoped this would not happen in Unity.

This is the player script PlayerMovement:

using UnityEngine;
using UnityEngine.UI;

namespace Assets.Scripts
{
    public enum Directions
    {
        Back,
        Left,
        Front,
        Right,
        Idle = -1
    }

    public class PlayerMovement : MonoBehaviour
    {
        #region Public Members

        /// <summary>
        /// Maximum speed of the player (Accelerated to over a period of time)
        /// </summary>
        public float speed;

        /// <summary>
        /// Debug UI.Text element
        /// </summary>
        public Text debugText;

        #endregion

        #region Constants

        /// <summary>
        /// Constant for decaying the velocity on updates
        /// </summary>
        private const float VELOCITY_DECAY_FACTOR = 0.85f;

        /// <summary>
        /// Constant to convert normal speed sizes to fit the scale
        /// Of UnityEngine.Vector2
        /// </summary>
        private const float HUMAN_TO_VECTOR_SCALE_FACTOR = 850f;

        /// <summary>
        /// Constant to set the base speed of the player animation
        /// </summary>
        private const float BASE_ANIM_SPEED = 0.7f;

        /// <summary>
        /// Constant to slightly reduce the animation speed after 
        /// It is multiplied by the velocity of the player
        /// </summary>
        private const float POST_VELOCITY_MULTIPLICATION_ANIM_SPEED_FACTOR = 0.5f;

        /// <summary>
        /// Constant to set the animation speed
        /// </summary>
        private const float ANIM_SPEED_MODIFIER = BASE_ANIM_SPEED * POST_VELOCITY_MULTIPLICATION_ANIM_SPEED_FACTOR;

        /// <summary>
        /// Epsilon before velocity zerofication
        /// </summary>
        private const float VELOCITY_EPSILON = 0.1f;

        #endregion

        #region Private Members

        private Rigidbody2D rb2D;
        private Vector2 velocity;
        private Animator animator;
        private Directions dir = Directions.Idle;

        #endregion

        #region Game Loop Methods

        private void Awake()
        {
            animator = GetComponent<Animator>();
            rb2D = GetComponent<Rigidbody2D>();
        }

        private void FixedUpdate()
        {
            float vertical = Input.GetAxisRaw("Vertical");
            float horizontal = Input.GetAxisRaw("Horizontal");
            UpdateVelocity(horizontal, vertical);
            UpdateAnimation(horizontal, vertical);
            UpdateMovment();
        }

        #endregion

        #region Animation Methods

        private void UpdateAnimation(float horizontal, float vertical)
        {
            UpdateAnimation(new Vector2(horizontal, vertical));
        }

        private void UpdateAnimation(Vector2 input)
        {
            Directions direction;

            if (input.y > 0)
                direction = Directions.Back;
            else if (input.y < 0)
                direction = Directions.Front;
            else if (input.x > 0)
                direction = Directions.Right;
            else if (input.x < 0)
                direction = Directions.Left;
            else
                direction = Directions.Idle;

            SetDirection(direction);
        }

        private void SetDirection(Directions value)
        {
            animator.SetInteger("Direction", (int)value);
            dir = value;
        }

        #endregion

        #region Movement Methods

        private void UpdateMovment()
        {
            rb2D.MovePosition(rb2D.position + velocity * Time.fixedDeltaTime);
            KinematicsDebugPrints();
            ApplySpeedDecay();
        }

        private string GetDebugPrintDetails()
        {
            return string.Format("HOR : {0}\nVER : {1}\nDIR : {2}:{3}\nX : {4}\nY : {5}",
                velocity.x,
                velocity.y,
                animator.GetInteger("Direction").ToString().PadLeft(2),
                (Directions)animator.GetInteger("Direction"),
                rb2D.position.x,
                rb2D.position.y);
        }

        private void KinematicsDebugPrints()
        {
            var details = GetDebugPrintDetails();
            debugText.text = details;
            Debug.Log(details);
        }

        private void UpdateVelocity(float horizontal, float vertical)
        {
            if (vertical != 0)
                velocity.y += Mathf.Sign(vertical) * speed / HUMAN_TO_VECTOR_SCALE_FACTOR;
            if (horizontal != 0)
                velocity.x += Mathf.Sign(horizontal) * speed / HUMAN_TO_VECTOR_SCALE_FACTOR;
            animator.speed = ANIM_SPEED_MODIFIER * velocity.MaxOfXandY() ;
        }

        private void ApplySpeedDecay()
        {
            if (velocity == Vector2.zero) return;

            velocity *= VELOCITY_DECAY_FACTOR;
            velocity = velocity.ZerofiyTinyValues(0.1f);
        }

        #endregion
    }
}

This is the player object currently:

player

And this is the wall object (prefab is the same for all walls except for the image:

WALL

This is a gif of my other bug:

bug two

This is how the collision box and circles look like:

new collision boxes

And this are the details from the inspector

new inspector


So after speaking with Hamza Hasan, he helped me to turn all the outer wall box colliders into four continues colliders, one per side(top, bottom, left, right).

The code for it is on the BoardManager script in the CreateWallsColliders method.

This is how the scene currently looks like in the scene editor:

new scene editor

like image 710
Giora Guttsait Avatar asked Dec 24 '15 20:12

Giora Guttsait


People also ask

What is collider 2d in Unity?

In Unity, a 2D collider is a component that allows us to define a shape, where we want to receive notifications in our GameObject's script whenever another GameObject (with another collider) collides with the first collider.

Why is collision detection not working Unity?

A collision can not be detected if the Is Trigger attribute is set to the collider. Open the properties pane of the game object and look inside the Collider component to ensure that option is not selected. If a collider has the Is Trigger attribute set, instead of the OnCollision...


1 Answers

The phenomenon you experience is caused by the rectangular collider of your character colliding with the bottom edge of the next tile of wall. This 'bug' is very common in physics engines. It is caused by some computation errors, and should be expected. This is why most games have bounding ellipses for characters, as ellipses don't have corners or edges.

One way to get rid of this sudden stop is to ensure that all contiguous tiles of wall are represented as a single collider (a rectangle or a polygon). This needs a separate logic that creates the colliders from the obstacles after loading the level, and the colliders must be updated after every change in the level (opening doors, etc...)

A much simpler way to solve the problem is to change the collider of the character. If the rectangular shape of your character is not essential, I recommend you to use a collider of the following shape:

capsule shaped collider

Or if the rectangular shape is essential, you can extend the corners with circles:

enter image description here

like image 51
Tamas Hegedus Avatar answered Sep 28 '22 00:09

Tamas Hegedus