Best Ground Detection Method

Hello, I’m relatively new to unity, but I have a lot of experience in applied programming like robotics. I’m trying to make a 2D platformer with controls and gameplay that is “done right”. However, the first major problem I have hit in that goal is ground detection. I’ve been spending hours over the past few days thinking about what the best way to go about this is.

Casting - Extends beyond the bounds of the player.

  • RayCasting - Doesn’t cover the entire bottom of the player
  • CircleCasting - Inconsistent distance depending on player shape (rectangle)
  • BoxCasting - Inconsistent distance depending on player shape (circle), doesn’t need to start with a box, would make a lot more sense if it were casting a line perpendicular to the casting direction.

Collision Layer - Most consistent, but it only needs to be a surface, not an area, and it is tedious to paint a layer everywhere you want the player to be grounded.
Basic Collider Logic - No good way of telling where the “bottom” of your character is.

My idea for how to make a perfect ground detector: A collider that replaces a paintable edge on the character so that whenever that collider hits an object in the ground layer, the character is grounded. However, there are two problems. One, I’m not sure how to go about replacing part of a collider and I doubt unity has official support for this (I’m very open to making custom functions, I just need help on where to look). Two, the built in edge collider for unity doesn’t detect collisions with other edge colliders or composite colliders which is a huge problem because many 2D games us tile maps with composite colliders for collision.

Any other ideas for making the ground collision more modular/flexible/expandable? I’m open to suggestions. Mostly, I need help figuring out how to actually go about implementing this. I haven’t programmed a collider from scratch before so this would be very new to me, though I have the computer science and mathematical background to figure it out with a little help on where to look.

I’ll also make sure to post my solution here when I’m done.

Thanks in Advance
MidnaTheBlackRobe

Hi!

So I’m the guy who integrated the 2D physics engine here at Unity so welcome!

Let’s get started step-by-step! So the physics engine creates contacts. These contacts are used to keep things separated and produce the physical responses you see but you can also use physics queries to fetch those contacts and there’s lots of methods to do this, see 2D stuff here.

With all 2D physics queries, you can pass a ContactFilter2D to filter results. Included in this is also Collision Normal angle which allows you to filter results that are only within a certain angle allowing you to (say) ask if you collided with something underneath or within an arc below you or maybe to the side etc.

To make this easy, there’s the IsTouching calls which simply go away and filter the existing contacts to see if something is touching. It’s not a spatial query, it’s querying existing contacts. You can pass the ContactFilter2D here to only get back the results you want.

Here’s a demo for “IsGrounded” where I only allow jump if the angle of the slope is within a certain range and only the “Ground” layer by checking the angle of the contact (surface) normal. The ContactFilter2D is used as a public field so you can edit it directly in the inspector. The actual check is a single line of code.

https://www.youtube.com/watch?v=ZgsrAHGU-mA

The above is one of many scenes in my PhysicsExamples2D GitHub project.

Edge/Edge can never work, they are infinitely thin lines. This isn’t supported at all in Box2D (the physics engine we use) for good reason. However we extended lines to be able to specify an edge radius. When you use a >0 edge radius, each edge is effectively a capsule and capsule vs other does work.

For ground detection though, there’s no good reason to use a special collider just for ground detection or queries such as raycast that don’t represent the actual character. Just look at the contacts on the whole Rigidbody2D or a specific collider you’re using to walk around on and ask if they’re touching.

You can also use a handful of IsTouching calls if you want to detect other things like touching walls or being on a slope etc.

NOTE: I’m going to move this to the 2D forum for you.

Just wanted to say thanks for this MelvMay!

Would this solution work with one way platforms made with the Platform Effector 2D component? Or would one need to modify it further in order to be consistent with one way platfroms?

Depending on what you’re doing, yes.

IsTouching ignores disabled contacts and the PlatformEffector2D works by disabling contacts: Unity - Scripting API: ContactPoint2D.enabled