Github Repository (Scripts folder, has all code in .cs files)
I have this weird collision bug in unity, here's a gif of it:
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:
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:
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).
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:
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.
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...
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:
Or if the rectangular shape is essential, you can extend the corners with circles:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With