Replace ceiling Y offset with per-pose CharacterController capsule
Each PlayerPose now carries its own collider center Y, radius and height, eased and routed to the CharacterController via a SetCapsule single-writer. This lets crawl poses (Ball/Star) shrink the capsule to fit under furniture. The ceiling Y offset is removed; poses without authored sizes fall back to the base capsule, so existing poses keep their collision. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
13de058819
commit
52f9ace9e0
6 files changed
+144
-59
No files matched your search
@@ -127,7 +127,7 @@ public partial class PlayerCharacterController : MonoBehaviour
|
||||
public float wallExitGrace = 0.15f;
|
||||
|
||||
[Tooltip("Head-to-ceiling distance (units) at which climbing a wall up into an overhang hands off to the ceiling.")]
|
||||
public float ceilingGrabDistance = 0.3f;
|
||||
public float ceilingGrabDistance = 0.02f;
|
||||
|
||||
[Tooltip("Upward hop when topping out over a wall/slope edge, so holding W carries you onto the ledge instead of dropping back onto the face and re-grabbing. In meters of jump height; 0 = no hop.")]
|
||||
public float wallTopOutBoost = 0.3f;
|
||||
@@ -185,6 +185,16 @@ public partial class PlayerCharacterController : MonoBehaviour
|
||||
/// </summary>
|
||||
public Transform MeshOffsetTarget => meshOffsetTarget;
|
||||
|
||||
/// <summary>Authored CharacterController capsule center Y, captured in Awake. The per-pose capsule
|
||||
/// (driven by <see cref="PlayerPoseController"/>) eases back to this when no pose overrides it.</summary>
|
||||
public float BaseCapsuleCenterY { get; private set; }
|
||||
|
||||
/// <summary>Authored CharacterController capsule radius, captured in Awake (see <see cref="BaseCapsuleCenterY"/>).</summary>
|
||||
public float BaseCapsuleRadius { get; private set; }
|
||||
|
||||
/// <summary>Authored CharacterController capsule height, captured in Awake (see <see cref="BaseCapsuleCenterY"/>).</summary>
|
||||
public float BaseCapsuleHeight { get; private set; }
|
||||
|
||||
// Below this horizontal speed (m/s, squared) the character counts as idle for the run animation.
|
||||
private const float RunningSpeedThresholdSqr = 0.04f; // (0.2 m/s)^2
|
||||
|
||||
@@ -194,7 +204,6 @@ public partial class PlayerCharacterController : MonoBehaviour
|
||||
private bool _sprinting; // sprint key state this frame (exposed via IsSprinting)
|
||||
private bool _isIdle; // snapshot: player essentially still this frame (exposed via IsIdle)
|
||||
private bool _movingOnGround; // snapshot: grounded & moving above the run threshold (exposed via IsMovingOnGround)
|
||||
private float _poseYOffset; // local-up model offset from the active pose (ceiling only); folded into SetMeshLean
|
||||
private float _groundOffset; // signed vertical mesh raise from the ground mouse pose-adjust (+ up / - down); folded into SetMeshLean
|
||||
|
||||
// Ground contact normal from the latest Move (for slope sliding).
|
||||
@@ -234,6 +243,9 @@ public partial class PlayerCharacterController : MonoBehaviour
|
||||
void Awake()
|
||||
{
|
||||
_controller = GetComponent<CharacterController>();
|
||||
BaseCapsuleCenterY = _controller.center.y;
|
||||
BaseCapsuleRadius = _controller.radius;
|
||||
BaseCapsuleHeight = _controller.height;
|
||||
if (!cameraTransform && Camera.main)
|
||||
cameraTransform = Camera.main.transform;
|
||||
if (!cameraOrbit)
|
||||
@@ -720,7 +732,7 @@ public partial class PlayerCharacterController : MonoBehaviour
|
||||
Vector3 worldOffset = dir * offset;
|
||||
meshOffsetTarget.localPosition = _meshBaseLocalPos
|
||||
+ transform.InverseTransformVector(worldOffset)
|
||||
+ Vector3.up * (_poseYOffset + _groundOffset); // ceiling pose y-offset + ground mouse raise (both 0 unless in use)
|
||||
+ Vector3.up * _groundOffset; // ground mouse raise (0 unless in use)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -773,21 +785,19 @@ public partial class PlayerCharacterController : MonoBehaviour
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a local-up offset applied to the visual model only, composed into the wall-lean's single
|
||||
/// write of <see cref="meshOffsetTarget"/>. Used by <see cref="PlayerPoseController"/> to hang a
|
||||
/// ceiling pose at the right height; 0 = none. Re-applies immediately so a change shows this frame.
|
||||
/// Resizes the CharacterController capsule (center Y, radius, height) to the active pose's dimensions,
|
||||
/// driven by <see cref="PlayerPoseController"/> so a low pose can fit where the default standing capsule
|
||||
/// is too tall. Only the collision capsule changes — the visual model and camera target are untouched.
|
||||
/// Cached so an unchanged value skips the write (a resize re-bakes the capsule internally).
|
||||
/// </summary>
|
||||
public void SetPoseLocalYOffset(float y)
|
||||
public void SetCapsule(float centerY, float radius, float height)
|
||||
{
|
||||
if (_poseYOffset == y) return;
|
||||
_poseYOffset = y;
|
||||
if (!meshOffsetTarget) return;
|
||||
// While climbing the lean is the live _wallOffset along _wallNormal; otherwise re-apply along
|
||||
// whatever residual lean is easing home (both 0 when there's none, restoring the authored pose).
|
||||
if (_isWallClimbing)
|
||||
SetMeshLean(_wallNormal, _wallOffset);
|
||||
else
|
||||
SetMeshLean(_meshOffsetExitNormal, _meshOffsetResidual);
|
||||
Vector3 c = _controller.center;
|
||||
if (c.y == centerY && _controller.radius == radius && _controller.height == height) return;
|
||||
c.y = centerY;
|
||||
_controller.center = c;
|
||||
_controller.radius = radius;
|
||||
_controller.height = height;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in new issue
Block a user