Multiple shapecasting precision issues/erroneous collisions

I’ve been utilizing Blockcasts in my game to simulate custom kinematic controllers (my characters are simply one-part, anchored instances that do not rotate). Throughout developing the collide and slide algorithm that I use, I have encountered a lot of frustrating and unsolvable collision issues (NOT RELATED TO A LACK OF MARGINS) that can be catastrophic for larger maps.

Note that the issues I am about to describe are, to the best of my ability, not an issue caused by my code whatsoever. I handle collisions on their respective axes, meaning I handle the X, Y, and Z velocities in their own collide and slide calls, rather than together (which can be a cause for clipping/even more imprecision). This means that I should expect precise normals, distances, and generally precise sweeps, especially because I am simulating physics at a constant tick rate of 60 fps, meaning that physics steps are of the same length and should stay consistent.

In my game, I utilize a “tolerance” value for a skin width/margin. This value is set to 0.1, and in my previous solution, it subtracted from the root’s ExtentsSize by vector.one * TOLERANCE. This prevented most collision errors, but it, for some reason, did not prevent me from falling through the floor on certain coordinates (and I was able to easily reproduce it by simply having a movement vector of either 1,1 or -1,-1 by pressing the according movement keys simultaneously), seen below:


Fig. 1: My character falls through the ground once positioned to 1, 16, 1.

Note that what happens in Figure 1 does not happen when the horizontal axes do not have the same sign, e.g. -1,1 or 1,-1. I do not know why this occurs, and it still occurred whether I raised or lowered the margin, just at different increments.

This led to my current solution for the blockcasting size: ((size - (vector.one * TOLERANCE)) * (vector.one - vector.abs(direction))) + (direction * TOLERANCE) (this is simplified in the repro below since only the Y axis is being casted). This prevented the collision errors that caused Figure 1, but it introduced another error.
When I go further from the origin point, collisions are much more unpredictable and cause severe issues, such as problems with my jitter fix (repositions the character instead of adding to the velocity if RaycastResult.Distance - (0.5 * (vector.magnitude(size * direction) + TOLERANCE)) is less than 0, essentially) which causes the character to cling to edges or stick to walls, when it shouldn’t at all:


Fig. 2a: My character clings to the edges at certain points when I am falling too close to an edge or when I try to walk off of the edge.

Note that the collisions seen in this video should not be occurring at all. The collision itself, when accounting for the margin, does not intersect with the edges of either parts:


Fig. 2b: A collision resulting from an edge cling. The red box shows RaycastResult.Position, while the green box shows the Blockcast’s origin and given collision size (visualized as size - (vector.one * TOLERANCE) though the true collision would have size.Y as TOLERANCE), showing a clear gap between the part and the edge.

Collisions like these should not be swept whatsoever, and this should be particularly true as the world is aligned to a grid. Everything except for the character is within one-stud increments, and the character itself does not rotate, meaning this cannot be caused by the origin itself being rotated or the world itself. These are all precision issues and erroneous collisions that are not on my behalf.

Finally, the last issue I have experienced is with RaycastResult.Normal. Since it is a unit vector, it is expected that it should have a length that is exactly 1, and no more or no less. But, in certain circumstances, it can end up being grossly outside of a tolerable range (my EPSILON is 2 ^ -23, which is what works the best for me when it comes to floating point values), causing visible jitters when moving into walls or even standing still.


Fig. 3a: The character jitters while walking into the wall and step, as the normal’s length staggers between ~0.9999998807907104 and ~1.0000001192092896.

Note that you may have to play the videos fullscreened to see the jitters properly.
I fixed this by simply re-normalizing RaycastResult.Normal, e.g. vector.normalize(RaycastResult.Normal), which fixed it:


Fig. 3b: The intended behavior; the character no longer jitters and is stable.

I’m unsure how this occurs; generally, the normal should always have a length of 1 or be close to that within a tolerable range. I should not have to re-normalize the normal vector.

All of what I have demonstrated can be reproduced within this place file, following the same steps that I did in the provided figures 1-2:
SHAPECAST_REPRO.rbxl (54.9 KB)

Using this sample, the issues shown in Figure 2 and Figure 3 can be easily replicated by moving around the Part with grid snapping turned off. There is a red BoxHandleAdornment which shows the resulting collision’s hit position and normal vector if it hits. The output is also logged whenever the normal vector is incorrect, which is when the length is not equal to 1 within an epsilon and when it is not 0, 1, 0 (accounting for an epsilon), as shown below:


Fig. 4: The part being moved downwards near the edge of the baseplate, showing that precision issues are logged while it grazes the edge.

Do note that in Figure 4, the size of the part is not accurate to the size being used in the Blockcast call. I scaled down the part by 0.1 in order to position the part to ensure that the “skin” was not touching the baseplate, and then scaled it back up to 4, 4, 4, which is the part size that is seen in the video. The skin itself is not touching the edge of the baseplate, much like what was shown in Figure 2. Nevertheless, I should be receiving normal vectors that are 0, 1, 0 no matter the point on this cuboid; it’s completely flat and bevel-less.


Fig. 5: Many imprecision issues with the normal vector being logged as the part is moved up and down high-precision increments.

While I was able to fix some of these issues, the fact that all of them occur regularly shows that there are significant issues with precision that need to be addressed. These can end up being critical issues if I wish to extend my game further, especially because I was able to consistently clip through the ground just through player input. I cannot fix all of these problems myself, especially what was introduced in Figure 2, as the fixes I have tried to implement clash with the character trying to move up/down slopes, or even stop collisions from working altogether.

Expected behavior

I expect the intersections to be within the collider size, meaning it does not go beyond size - (vector.one * TOLERANCE). I also expect RaycastResult.Normal to always return a unit vector with a length of 1.

A private message is associated with this bug report

14 Likes

Adding onto this

Axis aligned blockcasts against meshparts or unions are pretty imprecise, and end up sinking into them quite a lot


The blockcast has a size of Vector3.zero, and it’s .Position is the one slightly more sinked into the union. The other Attachment is the result of a regular Raycast. This issue happens no matter the size of the blockcast, and I believe it happens with all Shapecasts.

2 Likes

You know, it is a HUGE relief to see a bug report about this, especially because I’ve had issues with this exact same thing before, and I’d just shrugged it off as a fault to my own code. But knowing that it’s an issue with the Roblox engine that other people have been experiencing is genuinely a headache gone for me. I hope this gets resolved, and soon!

3 Likes

Bump, incase this hasn’t been looked at. I still have these issues occur.

1 Like

Bump, this has been the cause of a strange bug in my custom character controller where the character walks down a wall after walking off of it. A simple skin width option would solve this, but trying to create that on my side just leads to other issues. Had to default back to multiple ray casts for now.

3 Likes

bump. trying to make a custom character controller right now and meeting a lot of weird behaviour

2 Likes

Has this not been looked into whatsoever?
I loaded up my repro file, checked around, still seems to have the same issues

Length of the shapecast normal can also be beyond or under exactly 1 without re-normalizing it, collisions like the above still occur frequently and can be very, very easily recreated ingame… I had to put my project on hold over these issues, since it was way too unstable without weird fixes that I shouldn’t have to be doing - and even with the weird fixes, there is always one or more issues that come out of nowhere

Bump. I believe these precision error are the cause of my character controller clipping thru walls a lot

Edit: I am very stupid and replied to the wrong message

1 Like

Bump.

Still happening, still an issue.

This has killed my motivation to work on the project shown above, and it seems like there’s no engineers on the physics team that have bothered giving an update involving shapecasts (including margins, as was promised 2 years ago). Incredibly frustrating that I have to overcomplicate my collision code to fix issues like this, even though it shouldn’t be my problem whatsoever. Very disappointed with the state of the engine at the minute.

3 Likes

Addendum:

This is really absurd. Note I moved the collider’s visualizer down just to see if it was intersecting with the blue box on top; it recognized the red part below the blue part (which you can see via the red cube that represents the ray position), despite the collider initially being above the blue part???

How???

3 Likes

Hi, it’s been a long time coming. We’re currently diving into this internally, and we think we may have a fix. I or another Roblox Staff member will report back with any updates.

7 Likes

This issue has been plaguing my ability to fully move over to a virtualized approach for my player collider. Please let us know what you find!

3 Likes

Please do give updates whenever possible. This brings a glint of light to my eyes.

oh my god I just figured out GJK & how to parse your damned collision mesh format and you’re doing This Now </3

1 Like