This is going to be a long post, so please bear with me. I first encountered this issue a long time ago. I made a post about it then but wasn't able to resolve it. I have since gathered some more details about it and I hope that this time I can make some progress.
My game doesn't use the built in physics system, instead characters perform a SweepTest in a given direction before updating their position with transform.position += moveVector;. This allows me to keep them nicely aligned to grid spaces, which is necessary for my puzzle game. Sometimes when playing my game, a character will fall through a piece of floor. Other characters or objects are able to move over the same piece of floor without any issues, it seems to be a special relationship between just the two objects, seemingly selected at random. It does not always effect the same character and it does not always effect the same piece of floor. It does not matter which direction the character approaches the floor from, or how it is positioned relative to the floor. Because of the given number of pieces of floor (or walls or other objects) as well as characters, I only stumble across it once in a blue moon. It can be months between when I happen upon it (I spend a lot of time developing but not a lot of time playing). Starting a new game in the application through the menus, restarting the application (if it is a built version) or exiting play mode (if it is in the editor) all fail to reproduce the issue in the same way. Again, it seems to happen seemingly at random.
This means that if I want to do some debugging I need to have it all coded before the current execution, because editing any scripts causes them to recompile and causes errors in the current session.
My game build the puzzle map by additively scene loading multiple puzzle rooms. I do not know if this could be a factor, but I will mention it anyway.
My character is has a scale of (100, 200, 100) and has a move speed of 20. His box Collider is set at a scale of (0.99, 0.99, 0.99). He also has a kinematic Rigidbody. The top of the floor is at a height of 0, and its box Collider has the equivalent scale of (1, 1, 1). The character normally sits at positional height of 100 and his origin is in his center. This means that his base should always be about 1 above the floor, meaning that his base should never align perfectly with the floor preventing floating point error overlap. This also means that a SweepTest with a magnitude of 20 downwards should pick up the floor without issue.
Character Inspector: https://i.imgur.com/c4detlv.png Floor Inspector: https://i.imgur.com/fQFotn7.png
Using some debugging I logged the results of every downwards SweepTest, here is the code and the results.
RaycastHit[] hitThings = GetComponent<Rigidbody>().SweepTestAll(moveVector, moveVector.magnitude, QueryTriggerInteraction.Ignore);
Debug.Log(gameObject.transform.position);
Debug.Log(moveVector);
Debug.Log(moveVector.magnitude);
Debug.Log(hitThings.Length);
foreach (RaycastHit hitThing in hitThings) {
Debug.Log(hitThing.collider.name);
Debug.Log(hitThing.collider.tag);
Debug.Log(hitThing.collider.transform.parent.name);
Debug.Log(hitThing.collider.transform.root.name);
Debug.Log(hitThing.distance);
Debug.Log(hitThing.collider.transform.position);
}
Debug.Log("--------");
.
(-2370.0, 100.0, -4050.0)
(0.0, -20.0, 0.0)
20
1
Piston
Piston
WalkwayExtendedEast
Tier1Room-
1.252515
(-2450.0, -50.0, -4000.0)
--------
(-2350.0, 100.0, -4050.0)
(0.0, -20.0, 0.0)
20
0
--------
(-2350.0, 80.0, -4050.0)
(0.0, -20.0, 0.0)
20
0
--------
(-2350.0, 60.0, -4050.0)
(0.0, -20.0, 0.0)
20
0
--------
(-2350.0, 40.0, -4050.0)
(0.0, -20.0, 0.0)
20
0
--------
(-2350.0, 20.0, -4050.0)
(0.0, -20.0, 0.0)
20
0
--------
The character starts on top of something solid. As soon as he moves 20 to the right he is now solely over the floor in question. As you can see the SweepTest does have any hits detected.
In a separate bit of debugging I did a Physics.OverlapBox() test on the character, to determine if the character begins inside the piece of floor in question. Here are those results.
Debug.Log(trasform.position);
Debug.Log(transform.scale * 0.99f);
Collider[] overlapping = Physics.OverlapBox(gameObject.transform.position, transform.scale * 0.99f / 2f);
foreach (Collider currentCollider in overlapping) {
if (currentCollider.tag != "Character") {
Debug.Log(currentCollider.tag);
Debug.Log(currentCollider.transform.position);
Debug.Log(currentCollider.transform.root.position);
}
}
Debug.Log("--------")
.
(-2370.0, 100.0, -4050.0)
(99.0, 198.0, 99.0)
--------
(-2350.0, 100.0, -4050.0)
(99.0, 198.0, 99.0)
--------
(-2350.0, 80.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, 60.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, 40.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, 20.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, 0.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, -20.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, -40.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, -60.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, -80.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, -100.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, -120.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, -140.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, -160.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, -180.0, -4050.0)
(99.0, 198.0, 99.0)
Fixture
(-2250.0, -75.0, -4000.0)
(-4000.0, 0.0, -4000.0)
--------
(-2350.0, -200.0, -4050.0)
(99.0, 198.0, 99.0)
--------
(-2350.0, -220.0, -4050.0)
(99.0, 198.0, 99.0)
--------
These results indicate that the character is not overlapping with the floor in any way, until it has started falling through it.
My character is kinematic. Making it non kinematic and enabling gravity will cause the character to come to rest on the offending piece of floor. This, combined with the Physics.OverlapBox() results show that the physics system is detecting the floor, just not the SweepTest.
I tried moving the piece of floor variously up and down slightly, the character still passed through. I tried moving the piece of floor to chest height so the character could approach it from another angle. Still, the the character was able to move through the object, but no other objects or characters could.
Character through object: https://imgur.com/VwbUfUx
I copied and pasted the piece of floor. My character was not able to phase through this new object and the debugging showed this as well. Even though the copied object had the exact same properties and values, collisions were now being detected with the SweepTest.
Adding another box Collider to the floor also prevented my character from falling through it. So did adding another box Collider to my character. I think that something is wrong with these two particular instances of these Colliders, adding a new Collider or copying the object was creating a new Collider component instance and that new instance presumably has the same "once in a blue moon" chance of having this glitch.
Lastly, I tried resetting each of the components on the offending piece of floor and my character. This didn't yield any results, the character was still able to pass through it.
Although it happens rarely, this can cause a big problem in a game. The player with the effected character could be forced to restart his game. Being unable to cross certain sections of floor can make many games unfinishable. The other players can not finish the game without every player present, so they would be interrupted as well.
This seems to me to be a problem with Unity, not with my code. If anybody knows of a Unity staff member in these forums, could they please tag the staff member? I think that would go a long way to narrowing in on this issue.
My theory: Although I have only experienced this issue between one of my characters and another object, I would hazard a guess that it is possible for this issue to occur between any two objects. Seeing as I only happen upon it once every couple of months and seeing as my characters are the objects which most often come into contact with other objects, I doubt I'm likely to ever experience this. This problem seems to link two Collider instances together and I imagine that there is a very slight chance that any two Colliders will have this relationship.
Unity 2018.4.9f1 Personal LTS. Physics settings: https://imgur.com/pKI7Q07