Подтвердить что ты не робот

Необычная ошибка столкновения в игре Unity 2d

Репозиторий Github (папка Scripts, имеет весь код в файлах .cs)

У меня есть эта странная ошибка столкновения в единстве, вот ее gif:

GIF

Воссоздание: например, в gif я нажимаю как Left arrow, так и Up arrow до тех пор, пока скорость не нормализуется, и я почему-то застрял в блоке.

У меня было это раньше с моим собственным алгоритмом столкновений, когда я сделал игру в XNA, надеясь, что этого не произойдет в Unity.

Это игрок 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
    }
}

Это объект игрока в настоящее время:

player

И это объект стены (prefab одинаково для всех стен, кроме изображения:

WALL

Это gif моей другой ошибки:

ошибка 2

Вот как выглядит столбец и круги столкновений:

новые столбики конфликтов

И это подробности от инспектора

новый инспектор


Поэтому, поговорив с Хамза Хасаном, он помог мне превратить все коллайдеры на внешней стене в четыре продолжающихся коллайдера, по одному на каждую сторону (сверху, снизу, влево, вправо).

Код для него находится в BoardManager script в методе CreateWallsColliders.

Так выглядит сцена в редакторе сцены:

новый редактор сцен

4b9b3361

Ответ 1

Ну, прежде всего, переместите код ввода от FixedUpdate до Update, иначе он приведет приложение к лагги. Во-вторых, вы можете сделать это, создав PhysicsMaterial2D с помощью Friction = 0 и Bounciness = 0 и прикрепите его к плееру, а также коллайдеру стены в Material. Надеюсь, это поможет вам.

EDIT:

Вот альтернативное решение для вас, вместо использования 1 box-коллайдера на блок используйте только 1 коллайдер на каждую сторону. Всего 4 коллайдера.

Вот код, вы можете добавить его в свой класс BoardManager. И назовите его в конце метода SetUpScene, вы можете его изменить.

void CreateWallsColliders ()
        {
            GameObject colliders = new GameObject ("Colliders");
            colliders.transform.position = Vector3.zero;

            GameObject leftCollider = new GameObject ("LeftCollider");
            leftCollider.transform.position = Vector3.zero;
            BoxCollider2D bcLeftCollider = leftCollider.AddComponent<BoxCollider2D> ();
            leftCollider.transform.parent = colliders.transform;

            GameObject rightCollider = new GameObject ("RightCollider");
            rightCollider.transform.position = Vector3.zero;
            BoxCollider2D bcRightCollider = rightCollider.AddComponent<BoxCollider2D> ();
            rightCollider.transform.parent = colliders.transform;

            GameObject topCollider = new GameObject ("TopCollider");
            topCollider.transform.position = Vector3.zero;
            BoxCollider2D bcTopCollider = topCollider.AddComponent<BoxCollider2D> ();
            topCollider.transform.parent = colliders.transform;

            GameObject bottomCollider = new GameObject ("BottomCollider");
            bottomCollider.transform.position = Vector3.zero;
            BoxCollider2D bcBottomCollider = bottomCollider.AddComponent<BoxCollider2D> ();
            bottomCollider.transform.parent = colliders.transform;

            // Assuming 15 x 15 tiles. Make it dynamic if you need.
            // Assuming -1 and 15 are the limits on both sides

            int rows = 15;
            int cols = 15;

            int lowerLimit = -1;
            int upperLimit = 15;

            leftCollider.transform.position = new Vector3 (lowerLimit, rows / 2);
            leftCollider.transform.localScale = new Vector3 (1, cols, 1);

            rightCollider.transform.position = new Vector3 (upperLimit, rows / 2);
            rightCollider.transform.localScale = new Vector3 (1, cols, 1);

            topCollider.transform.position = new Vector3 (cols / 2, upperLimit);
            topCollider.transform.localScale = new Vector3 (rows, 1, 1);

            bottomCollider.transform.position = new Vector3 (cols / 2, lowerLimit);
            bottomCollider.transform.localScale = new Vector3 (rows, 1, 1);
        }

Ответ 2

Явление, которое вы испытываете, вызвано прямоугольным коллайдером вашего персонажа, сталкивающимся с нижним краем следующей плитки стены. Эта "ошибка" очень распространена в физических двигателях. Это вызвано некоторыми ошибками вычислений, и их следует ожидать. Вот почему большинство игр имеют ограниченные эллипсы для персонажей, поскольку эллипсы не имеют углов или ребер.

Один из способов избавиться от этой внезапной остановки - убедиться, что все смежные плитки стены представлены как один коллайдер (прямоугольник или многоугольник). Это требует отдельной логики, которая создает коллайдеры с препятствий после загрузки уровня, и коллайдеры должны обновляться после каждого изменения уровня (открытие дверей и т.д.).

Более простой способ решить проблему - изменить коллайдер персонажа. Если прямоугольная форма вашего персонажа не является существенной, я рекомендую вам использовать коллайдер следующей формы:

капсульный образный коллайдер

Или, если прямоугольная форма важна, вы можете растягивать углы кругами:

введите описание изображения здесь