diff --git a/src/AcDream.Core/Physics/TransitionTypes.cs b/src/AcDream.Core/Physics/TransitionTypes.cs index b919790..8913c85 100644 --- a/src/AcDream.Core/Physics/TransitionTypes.cs +++ b/src/AcDream.Core/Physics/TransitionTypes.cs @@ -752,11 +752,23 @@ public sealed class Transition if (bestT >= float.MaxValue) return TransitionState.OK; // no collision + // Already overlapping at the START of the step (bestT == 0 or very small). + // This happens when the player spawns inside an object or a previous + // step left them penetrating. Push out along the collision normal + // instead of sliding — sliding with zero displacement gets stuck. + if (bestT <= PhysicsGlobals.EPSILON) + { + Vector3 pushOut = bestNormal * (sphereRadius * 0.5f + 0.01f); + sp.AddOffsetToCheckPos(pushOut); + ci.SetCollisionNormal(bestNormal); + ci.SetSlidingNormal(bestNormal); + return TransitionState.Adjusted; + } + // Rewind the sphere to the contact point (before penetration). if (bestT < 1f) { Vector3 contactPos = currPos + movement * bestT; - // Push slightly back along normal to prevent sitting exactly on the surface. contactPos += bestNormal * 0.005f; sp.SetCheckPos(contactPos, sp.CheckCellId); }