c# - tag - Error de colisión extraño en el juego Unity 2d
ontriggerenter (2)
El fenómeno que experimentas es causado por el colisionador rectangular de tu personaje que colisiona con el borde inferior de la siguiente ficha de pared. Este ''error'' es muy común en los motores de física. Es causado por algunos errores de cálculo, y debe esperarse. Esta es la razón por la que la mayoría de los juegos tienen elipsis saltantes para los personajes, ya que las elipses no tienen esquinas ni bordes.
Una forma de deshacerse de esta parada repentina es asegurarse de que todos los mosaicos contiguos de la pared se representen como un solo colisionador (un rectángulo o un polígono). Esto necesita una lógica separada que crea los colisionadores de los obstáculos después de cargar el nivel, y los colisionadores deben actualizarse después de cada cambio en el nivel (apertura de puertas, etc.)
Una forma mucho más simple de resolver el problema es cambiar el colisionador del personaje. Si la forma rectangular de tu personaje no es esencial, te recomiendo que uses un colisionador de la siguiente forma:
O si la forma rectangular es esencial, puedes extender las esquinas con círculos:
Repositorio de Github (carpeta de secuencias de comandos, tiene todo el código en archivos .cs)
Tengo este extraño error de colisión en la unidad, aquí hay un gif:
Recreando: en el gif, por ejemplo, presiono tanto la flecha izquierda como la flecha arriba hasta que la velocidad se normaliza, y obtengo un poco de razón por la cual estoy atrapado en un bloque.
He tenido esto antes con mi propio algoritmo de colisión cuando hice el juego en XNA, esperaba que esto no sucediera en Unity.
Esta es la secuencia de jugador 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
}
}
Este es el objeto del jugador actualmente:
Y este es el objeto de pared (prefabricado es el mismo para todas las paredes a excepción de la imagen:
Este es un gif de mi otro error:
Así es como se ven la caja de colisión y los círculos:
Y estos son los detalles del inspector
Entonces, después de hablar con Hamza Hasan, él me ayudó a convertir todos los colisionadores externos en cuatro colisionadores continuos, uno por cada lado (arriba, abajo, izquierda, derecha).
El código para ello está en la secuencia de comandos CreateWallsColliders
en el método CreateWallsColliders
.
Así es como se ve la escena en el editor de escena:
Bueno, primero mueva su código de entrada de FixedUpdate
a Update
contrario, lleva a la aplicación a un comportamiento lento. Lo segundo es que puedes hacer esto creando PhysicsMaterial2D con Friction = 0
y Bounciness = 0
y adjuntarlo al jugador, así como las paredes colisionar en Material
. Espero que esto te ayude.
EDITAR:
Aquí hay una solución alternativa para usted, en lugar de usar 1 colisionador de caja por bloque, use solo 1 colisionador por lado. 4 Colliders en total.
Aquí está el código, puede agregarlo a su clase BoardManager
. Y llámalo al final del método SetUpScene
, puedes modificarlo más.
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);
}