简体   繁体   English

Unity 2d游戏中奇怪的碰撞错误

[英]Weird collision bug in Unity 2d game

Github Repository (Scripts folder, has all code in .cs files) Github Repository(Scripts文件夹,包含.cs文件中的所有代码)

I have this weird collision bug in unity, here's a gif of it: 我在团结中有这个奇怪的碰撞错误,这里是它的一个gif:

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. 重新创建:例如,在gif中,我按下向左箭头向上箭头,直到速度标准化,然后我得到一些为什么卡在一个块中。

I've had this before with my own collision algorithm when I did the game in XNA, hoped this would not happen in Unity. 当我在XNA中进行游戏时,我之前已经使用了自己的碰撞算法,希望在Unity中不会发生这种情况。

This is the player script PlayerMovement : 这是播放器脚本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: 这是目前的玩家对象:

播放机

And this is the wall object (prefab is the same for all walls except for the image: 这是墙体物体(除了图像之外,所有墙壁的预制都是相同的:

壁

This is a gif of my other bug: 这是我的另一个bug的gif:

错误二

This is how the collision box and circles look like: 这是碰撞框和圆圈的样子:

新的碰撞盒

And this are the details from the 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). 所以在与Hamza Hasan交谈之后,他帮助我将所有外墙箱式对撞机变成了四个连续对撞机,每侧一个(顶部,底部,左侧,右侧)。

The code for it is on the BoardManager script in the CreateWallsColliders method. 它的代码位于CreateWallsColliders方法的BoardManager脚本中。

This is how the scene currently looks like in the scene editor: 这是场景编辑器中当前场景的样子:

新的场景编辑器

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. 这个'bug'在物理引擎中很常见。 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: 如果角色的矩形形状不是必需的,我建议您使用以下形状的对撞机:

胶囊形状的对撞机

Or if the rectangular shape is essential, you can extend the corners with circles: 或者如果矩形形状是必不可少的,您可以用圆圈扩展角落:

在此输入图像描述

Well, first of all move your input code from FixedUpdate to Update otherwise it leads app to laggy behaviour. 好吧,首先将输入代码从FixedUpdate移动到Update否则会导致应用程序出现滞后行为。 Second thing is you can do this by creating PhysicsMaterial2D with Friction = 0 and Bounciness = 0 and attach it to the player as well as walls collider in Material . 第二件事情是,你可以通过创建做到这一点PhysicsMaterial2DFriction = 0Bounciness = 0 ,并将其连接到播放器以及墙壁撞机Material Hope this helps you. 希望这对你有所帮助。

EDIT: 编辑:

Here is an alternative solution for you, instead of using 1 box collider per block, use only 1 collider per side. 这里有一个替代解决方案,而不是每个块使用1个盒子对撞机,每侧只使用1个对撞机。 4 Colliders in total. 共有4个碰撞者。

Here is the code, you can add it to your BoardManager class. 这是代码,您可以将其添加到BoardManager类。 And call it in at the end of SetUpScene method, you can further modify it. 并在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);
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM