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

5 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!

2 Likes

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