- Home /
why is there's a gap between colliders2d?
Despite the colliders being aligned to be pixel-perfect, there's still a weird gap between the player's collider and the ground's collider. I guess I could just make the player's collider a bit smaller (or shift the sprite renderer one pixel down), but it feels like not-solving the problem. Unless it's actually the desired behavior of colliders for [insert reason here].
Answer by Ermiq · Dec 03, 2019 at 01:12 PM
It might depend on what you use for character controls.
For rigidbody based character the gap could be changed by setting up physics default contact offset in Edit->Project Setting->Physics(or Physics2D)->Collision detection offset
. The higher value you set, the more colliders will intersect each other before the engine will detect collision between them.
For CharacterController it's controller.SkinWidth
property. Unity suggests to set it to 10% of the controller radius. Just like with physics contact offset, it determines how far colliders should intersect each other before the collision detection.
For NavMeshAgent driven character it depends on the nav mesh. There's an option in nav mesh baking setting to make it more presice and to fit the terrain better, without that setting nav mesh could be significantly higher above the ground.
Thanks. Collision detection offset was the guilty one, but trying to set it to 0 caused bugs with the controls (character lost ability to detect when they're standing on the ground), so I had to go with the value of 0.0075 for now. It's not perfect, but it gives the character an offset of exactly 1 pixel from the surface so I could just edit the sprite, I guess.
To prevent this grounded/notGrounded glitch you have to include the physics contact offset in your ground check code. Usually, with physics based character (with non-kinematic rigidbody) the ground check is done by casting a sphere (or circle in 2D), and in the cast code you need to set the sphere start point height as transform.position + Vector3.up * (characterRadius + Physics.defaultContactOffset)
, the sphere radius to characterRadius - Physics.defaultContactOffset
, and the distance to Physics.defaultContactOffset
. With this casting setup it won't glitch.
I'm vandalizing the stock 2d template for $$anonymous$$e, so there the collision check is done in this way (as far as I can tell:
protected virtual void Start()
{
contactFilter.useTriggers = false;
contactFilter.SetLayer$$anonymous$$ask(Physics2D.GetLayerCollision$$anonymous$$ask(gameObject.layer));
contactFilter.useLayer$$anonymous$$ask = true;
}
///-------Code omitted------///
void Perform$$anonymous$$ovement(Vector2 move, bool y$$anonymous$$ovement)
{
var distance = move.magnitude;
if (distance > $$anonymous$$$$anonymous$$oveDistance)
{
//check if we hit anything in current direction of travel
var count = body.Cast(move, contactFilter, hitBuffer, distance + shellRadius);
for (var i = 0; i < count; i++)
{
var currentNormal = hitBuffer[i].normal;
//is this surface flat enough to land on?
if (currentNormal.y > $$anonymous$$GroundNormalY)
{
IsGrounded = true;
// if moving up, change the groundNormal to new surface normal.
if (y$$anonymous$$ovement)
{
groundNormal = currentNormal;
currentNormal.x = 0;
}
}
if (IsGrounded)
{
//how much of our velocity aligns with surface normal?
var projection = Vector2.Dot(velocity, currentNormal);
if (projection < 0)
{
//slower velocity if moving against the normal (up a hill).
velocity = velocity - projection * currentNormal;
}
}
else
{
//We are airborne, but hit something, so cancel vertical up and horizontal velocity.
velocity.x *= 0;
velocity.y = $$anonymous$$athf.$$anonymous$$in(velocity.y, 0);
}
//remove shellDistance from actual move distance.
var modifiedDistance = hitBuffer[i].distance - shellRadius;
distance = modifiedDistance < distance ? modifiedDistance : distance;
}
}
body.position = body.position + move.normalized * distance;
}
What should be changed in this case?