Perfect Circle Collision Shape in 2D Engine

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Blackthorn

I’m currently working on a little mobile game, making use of the RigidBody2D Engine. However, the RigidBody’s collision shape is supposed to be a circle, but Godot only offers a circle-like shape which is actually a 24-corner shape. As a result, the RigidBody does not roll like a normal Ball would do, it rather slides down slopes without rotating.
Does anyone know a solution in order to get a perfect circular collision shape?

:bust_in_silhouette: Reply From: Zylann

AFAIK, CircleShape2D is not a polygon shape made of 24 segments. Where did you see this?
The debug visual surely is composed of 24 segments because graphics card use polygons to draw things, so it happens that the visible disk shows edges.
But in the 2D physics engine, it really uses a circle formula to solve collisions. You can verify this by angling a circle by 1 degree: if it was a 24-edge circle, it would have slowly shifted to the right, as it would induce a small desequilibrium. But that does not happen, rotation remains 1 degree.

I did some tests with just a circle body and a platform. First thing I had to check is the body being put to sleep. The physics engine estimated it was not moving enough to bother simulating it, so it freezes the body.
This can be fixed by unchecking can sleep in the inspector.

After that, I can see the circle rotate as it slides down a slope. It correctly rotates even very slowly on a slope angled at 1 degree only.

If it slides but doesn’t rotate, you may have to check its angular damping, or if something else sets its rotation (which you shouldnt do).

Thank you very much!
The angular damping indeed fixed it, sorry for my stupid guess about the 24-corner.

Blackthorn | 2020-04-08 12:45

Edit: I’ve discovered that the circle isn’t bumpy. The ground is bumpy. Because of the way I was constructing the ground, it ended up being made of many little segments even though it is flat. The inexact nature of the collision detection makes the ends of the segments contribute unevenly to the force that keeps the circle above it. I was able to prove this by using one long segment for the ground. After that the circle rolls perfectly. Unfortunately, I want to make the ground curved, and I was using many segments to approximate a bezier curve. Now I’m looking for a way to use a bezier curve directly as a collision object.

Original comment:

I’m not sure if this has changed in the last five years or if I’m doing something different but for me it’s definitely not a circle. I have a RigidBody2D with a CollisionShape2D with a Shape property set to CircleShape2D. Can Sleep is off. Linear and Angular Damp are both 0.0, with Damp Mode set to Replace.

When the RigidBody2D is dropped into a big flat, slightly inclined “ground”, it starts rolling, but then comes to a stop and totters back and forth a little.

I ended up here because when it gets rolling fast it often jumps a little, which would also make sense for something that is a polygon with a lot of sides rather than a true circle. It’s possible that it’s “trying” to be a circle and something else is getting in the way, like the way the ground is set up or some other settings.

1 Like

Hey cesoid, I believe I ran into this exact problem and found an answer in the godot docs here: Troubleshooting physics issues — Godot Engine (stable) documentation in English

TL DR: You cannot rely on the collision or physics layer on tiles within a tilemap itself to properly collide with circles. Best bet to get the desired behavior (no bumps, smooth constant contact with the ground/slopes) is to instead create larger static bodies + collision shapes over top the tile map/ground. Its a bit redundant, but any straight lines would need to be handled by a single collision object until the slope changes (in which case you can use a segment).

I haven’t experimented with doing a large collision shape that was meant to have a gradual curve like a half pipe or something yet, but I would imagine you’d need to follow a similar pattern and not rely on piecing together tilemap collisions.

I ended up making this work with a bezier curve. Keeping in mind that I did this about 8 months ago and I’m just looking at my commit logs to remind myself:

I wrote my own code for the collision, which was actually pretty easy because the bezier object has a “get_closest_point()” method. This is the actual closest point, not an approximation. I’m checking this against the center of the circle. Since it’s a circle that’s colliding with the bezier, the closest point to the center is guaranteed to be the first point of contact. As noted by other people, this fact about a circle makes it actually one of the easiest shapes for collision detection. In addition, the normal force would always be directed through the center of the circle and the force of friction would always be a tangent line. If I’m only checking one bezier curve against one circle, (or just a few bezier curves against a circle) these calculations are minimal.

In other words, I’m just bypassing the optimization of splitting the bezier into segments. All I have to do is copy the existing collision code and make some small substitutions. There’s just no built-in way to force an exact calculation, probably because most apps won’t benefit from it. Most apps have collisions where you wouldn’t notice this issue, and they have collisions where calculating the point of contact with a bezier directly would require a lot more processing power.

In my case, I have one shape colliding with the bezier – the user would be focused on it. It’s often rolling, it’s smooth, it’s circular, and the shape of the “ground” is really easy to see. If any one of those things weren’t true you wouldn’t expect a smooth movement. Most apps tend to have a character that is not a circle, or it’s colliding with rough, uneven surfaces, and/or not rolling. Any other shape colliding with a bezier would require more calculation, and having several shapes doing this would require a lot more calculation.