Docs: reduce over-documentation across gameplay scripts

Trim restated narration, dead commented-out code, and oversized class
headers; keep all why-comments (authority, timing, edge cases), tooltips,
and serialization caveats.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Fable 5 committed 2026-07-02 10:08:28 +02:00
1 parent 882d30797e
commit 066e308cde
21 files changed
+478 -902

No files matched your search

+64 -112
View File
@@ -4,32 +4,18 @@ using UnityEngine.InputSystem;
#endif
/// <summary>
/// CharacterController-based player: camera-relative WASD movement, manual gravity
/// and jumping, sliding off too-steep slopes/edges, and a wall-climb mode.
/// CharacterController-based player: camera-relative WASD movement, manual gravity and jumping,
/// sliding off too-steep slopes/edges, and a wall/ceiling-climb mode.
///
/// Wall-climb (entered by pushing the move input into a wall): W/S climb straight up/down the
/// wall (velocity ramps in via a gentle acceleration for precise placement), A/D strafe left/right relative to the camera, and the body always
/// faces the wall. Holding the left mouse button and dragging up/down leans the visual mesh out
/// of or into the wall (only the model + its paint collider move; the capsule stays put, so the
/// lean can sink into the wall); holding the right mouse button and dragging left/right turns the
/// body. Either pauses the camera orbit; the lean offset eases back to zero while moving, the turn
/// is held until leaving the wall.
/// On leaving, the body eases back to its movement direction. Jumping detaches and works
/// normally; re-grab is blocked while rising and resumes once falling and pushing into a wall.
/// Reaching the top edge mantles the character over onto the surface.
/// Wall-climb (entered by pushing the move input into a wall): W/S climb straight up/down (velocity
/// ramps in for precise placement), A/D strafe camera-relative, the body faces the wall. Left-drag
/// leans the visual mesh out of / into the wall (capsule stays put), right-drag turns the body;
/// either pauses the camera orbit. Jumping detaches; reaching the top edge mantles over. Ceiling-climb
/// shares the mode (entered by jumping up into a ceiling or climbing a wall into an overhang): the
/// body stays upright and moves camera-relative in the horizontal plane; jump lets go. On the ground
/// the same mouse scheme raises/lowers the mesh and turns the body without moving the capsule.
///
/// Ceiling-climb shares the same mode: entered by jumping up into a ceiling or by climbing a
/// wall up into an overhang. The body stays upright and clings to the ceiling, moving in the
/// horizontal plane camera-relative (like on the ground). Jump (or reaching a ceiling edge)
/// lets go and drops.
///
/// On the ground the same mouse scheme fine-tunes a pose without moving the capsule: holding the
/// left mouse button and dragging up/down raises/lowers the visual mesh, holding the right mouse
/// button and dragging left/right turns the body. The raise eases back while walking and holds while
/// standing; the turn holds until movement re-aims the body. Both pause the camera orbit while held.
///
/// Named PlayerCharacterController to avoid a clash with Unity.VisualScripting's
/// example "PlayerController".
/// Named PlayerCharacterController to avoid a clash with Unity.VisualScripting's "PlayerController".
/// </summary>
[RequireComponent(typeof(CharacterController))]
[DisallowMultipleComponent]
@@ -188,18 +174,12 @@ public class PlayerCharacterController : MonoBehaviour
/// <summary>True while stuck to a ceiling (wall-climb mode with a downward-facing surface normal).</summary>
public bool IsOnCeiling => _isWallClimbing && _onCeiling;
/// <summary>
/// True while grounded and actually moving above the run threshold (the ground-locomotion state).
/// Airborne, wall/ceiling, and standing still all read false. This is the same signal that drives
/// the run bool, exposed so the pose system can pick its walk/run locomotion clips.
/// </summary>
/// <summary>True while grounded and moving above the run threshold (the ground-locomotion state);
/// airborne / wall / standing all read false. The pose system picks its walk/run clips off this.</summary>
public bool IsMovingOnGround => _movingOnGround;
/// <summary>
/// True while airborne and descending faster than <see cref="fallAnimationSpeed"/> (scaled by player
/// size): not grounded and not wall-climbing. Rising after a jump, standing, moving on the ground, and
/// wall/ceiling states all read false. Exposed so the pose system can pick its fall clip.
/// </summary>
/// <summary>True while airborne (not wall-climbing) and descending faster than
/// <see cref="fallAnimationSpeed"/> (scaled by player size). The pose system picks its fall clip off this.</summary>
public bool IsFalling => _isFalling;
/// <summary>
@@ -524,11 +504,6 @@ public class PlayerCharacterController : MonoBehaviour
_slideSpeed = 0f;
}
/// <summary>
/// Enters wall-climb mode only when the move input actively pushes into a wall
/// (not on mere contact). Leaving while climbing is handled in
/// <see cref="ApplyWallClimbMovement"/>.
/// </summary>
// Hunters may not grab a wall during the hide phase (they wait it out); every other role/phase climbs
// normally. The controller only runs for the local owner, so _setup is this avatar's own role.
private bool WallClimbBlockedNow()
@@ -538,6 +513,10 @@ public class PlayerCharacterController : MonoBehaviour
&& _setup && _setup.Role == PlayerRole.Hunter;
}
/// <summary>
/// Enters wall-climb mode only when the move input actively pushes into a wall (not on mere
/// contact). Leaving while climbing is handled in <see cref="ApplyWallClimbMovement"/>.
/// </summary>
private void UpdateWallClimbState(Vector3 moveDir)
{
if (_reentryCooldown > 0f)
@@ -589,17 +568,11 @@ public class PlayerCharacterController : MonoBehaviour
}
/// <summary>
/// Movement while climbing, for both walls and ceilings (distinguished by the surface
/// normal). On a WALL: W/S climb straight up/down at a constant speed (camera pitch never
/// tilts them), A/D strafe left/right relative to the camera, the body faces the wall, the
/// left mouse button + vertical drag leans the mesh out of / into the wall via
/// <see cref="_wallOffset"/> (capsule stays put), and the right
/// mouse button + horizontal drag turns the body via <see cref="_wallYaw"/>. On a
/// CEILING: the body stays upright and moves WASD camera-relative in the horizontal plane
/// (like on the ground), facing the move direction unless the right mouse button + horizontal
/// drag turns it; the lean-out drag pulls down off the ceiling. Surface normal and gap are
/// re-measured each frame; reaching a wall's top edge
/// mantles, climbing a wall up into an overhang hands off to the ceiling.
/// Movement while climbing, walls and ceilings (distinguished by the surface normal). WALL: W/S
/// climb straight up/down, A/D strafe camera-relative, the body faces the wall; left-drag leans the
/// mesh via <see cref="_wallOffset"/> (capsule stays put), right-drag turns via <see cref="_wallYaw"/>.
/// CEILING: upright, WASD camera-relative in the horizontal plane like on the ground. The surface is
/// re-probed each frame; a wall's top edge mantles, climbing into an overhang hands off to the ceiling.
/// </summary>
private void ApplyWallClimbMovement(float h, float v, Vector3 moveDir, bool jumpPressed, bool sprinting, float dt)
{
@@ -614,13 +587,11 @@ public class PlayerCharacterController : MonoBehaviour
UpdateCeilingClass();
bool onCeiling = _onCeiling;
// Map W/S to UP/DOWN by which key pushes INTO the wall, the same way A/D's _wallStrafeSign
// picks sideways from the camera: whichever vertical key drives the player toward the wall
// climbs up. Facing the wall that's W; with the camera on the wall's side (walking backward
// into it) it's S. Without this remap the grab (decided on the camera-relative world
// direction) and the base-release (decided on raw S) disagree, so the climb flickers on/off.
// Latched once per press and held until released; only walls remap (the ceiling crawl is
// already camera-relative). vEff is the effective climb axis used for every up/down test below.
// Map W/S to UP/DOWN by which key pushes INTO the wall (like A/D's strafe sign): facing the wall
// that's W; with the camera on the wall's side (walking backward into it) it's S. Without the
// remap the grab (camera-relative) and the base-release (raw S) disagree and the climb flickers.
// Latched once per press; only walls remap (the ceiling crawl is already camera-relative).
// vEff is the effective climb axis used for every up/down test below.
bool climbing = Mathf.Abs(v) > 1e-4f;
if (!climbing)
_wallClimbSign = 0;
@@ -655,12 +626,10 @@ public class PlayerCharacterController : MonoBehaviour
_wallOffset = 0f;
}
// Re-measure the surface each frame, probing the kind we're committed to. On a ceiling
// probe straight UP (ProbeCeiling), NOT along -_wallNormal: the ceiling normal tilts a
// little toward the wall in an inner corner, so the -_wallNormal ray would clip the wall
// and demote us to "wall" — then the hand-off re-promotes next frame, flipping the body's
// facing ~9 deg every frame (the corner twitch). Stay latched on the ceiling until it is
// genuinely gone overhead; only then fall back to the wall probe.
// Re-measure the surface each frame, probing the kind we're committed to. On a ceiling probe
// straight UP, NOT along -_wallNormal: in an inner corner that ray would clip the wall and
// demote us to "wall", the hand-off re-promotes next frame, and the body's facing flips ~9 deg
// every frame (the corner twitch). Stay latched until the ceiling is genuinely gone overhead.
bool found;
float gap;
if (onCeiling)
@@ -718,21 +687,18 @@ public class PlayerCharacterController : MonoBehaviour
}
else
{
// W/S climb straight up/down the wall (camera pitch never tilts them; speed ramps in
// below). Strafe along the wall's own horizontal tangent, not the live camera-right
// vector: feeding camera-right in here coupled movement to the orbit camera's
// W/S climb straight up/down (camera pitch never tilts them). Strafe along the wall's own
// horizontal tangent, not live camera-right: that coupled movement to the orbit camera's
// per-frame re-aim and jittered sideways motion. The camera only picks the sign.
Vector3 wallUp = Vector3.ProjectOnPlane(Vector3.up, _wallNormal);
if (wallUp.sqrMagnitude > 1e-6f) wallUp.Normalize();
Vector3 wallRight = Vector3.Cross(_wallNormal, wallUp);
if (wallRight.sqrMagnitude > 1e-6f) wallRight.Normalize();
// Pick the strafe sign ONCE from the camera when a strafe begins, then hold it.
// Re-deciding it every frame flips wallRight while rounding a curved (cylindrical)
// wall: exactly at 90 deg to the camera the dot crosses zero, so the sideways input
// inverts frame-to-frame and you lock there, jittering back and forth. Latching keeps
// a strafe continuous around the curve; it resets when the strafe input releases so
// the next one re-syncs to the camera.
// Pick the strafe sign ONCE from the camera when a strafe begins, then hold it: re-deciding
// per frame flips wallRight while rounding a curved wall (at 90 deg to the camera the dot
// crosses zero and the input inverts frame-to-frame, locking you there). Resets when the
// strafe input releases so the next one re-syncs to the camera.
bool strafing = Mathf.Abs(h) > 1e-4f;
if (!strafing)
_wallStrafeSign = 0;
@@ -769,12 +735,9 @@ public class PlayerCharacterController : MonoBehaviour
}
}
// Left button + vertical drag sets the lean offset (mouse up = out from the surface, mouse
// down = into it; the offset is signed and moves only the visual mesh, so it can sink into
// the wall where the capsule can't); right button + horizontal drag turns the body. The
// offset eases back to zero while moving and never detaches. On a WALL the turn is a held
// yaw offset from the wall normal (kept until leaving); on a CEILING the body is free like
// on the ground, so the drag rotates the transform directly. Either drag pauses the orbit.
// Left drag = signed lean (moves only the visual mesh, so it can sink into the wall where the
// capsule can't); right drag = body turn (WALL: held yaw offset from the wall normal; CEILING:
// rotates the transform directly). The lean eases home while moving; either drag pauses the orbit.
bool offsetDrag = found && InputUtil.LeftMouseHeld();
bool yawDrag = found && InputUtil.RightMouseHeld();
if (offsetDrag || yawDrag)
@@ -799,10 +762,9 @@ public class PlayerCharacterController : MonoBehaviour
float climbSpeed = moveSpeed * wallClimbSpeedMultiplier;
if (sprinting) climbSpeed *= sprintSpeedMultiplier;
// Ramp the along-surface velocity instead of snapping to full speed: a gentle acceleration
// lets a brief tap move you only a little, so you can place yourself precisely (especially
// without sprint). Sprint scales the accel too, so it still reaches its higher speed quickly;
// releasing decelerates at the ground decel rate for a prompt, controllable stop.
// Ramp the along-surface velocity instead of snapping: a brief tap moves only a little (precise
// placement). Sprint scales the accel too, so it reaches its higher speed just as quickly;
// releasing decelerates at the ground rate for a prompt, controllable stop.
Vector3 targetAlong = alongSurface.sqrMagnitude > 1e-6f
? alongSurface.normalized * climbSpeed
: Vector3.zero;
@@ -825,14 +787,12 @@ public class PlayerCharacterController : MonoBehaviour
_controller.Move(velocity * dt);
// Reaching walkable ground detaches back to normal grounded movement. The top-out hop
// (carry over the lip) fires ONLY when topping out upward over an edge (vEff > 0). Arriving
// at the floor by descending, OR pressing down/away (the effective down key, vEff < 0) at the
// wall's base while grounded, just lets go cleanly with no hop -- the latter is the only detach
// path that doesn't require having left the ground first, so a wall grabbed at its base can be
// released by pressing away. We test walkable ground under the feet rather than trusting
// isGrounded: the standoff correction can lift the capsule just off the surface, so at a
// slope's top isGrounded stays false and the old check never fired.
// Reaching walkable ground detaches. The top-out hop fires ONLY when topping out upward
// (vEff > 0); descending to the floor, or pressing away (vEff < 0) at the base while grounded,
// lets go cleanly with no hop - the latter is the only detach that doesn't require having left
// the ground first, so a wall grabbed at its base can be released by pressing away. Walkable
// ground is probed under the feet rather than trusting isGrounded (the standoff correction can
// lift the capsule just off the surface, so at a slope's top isGrounded stays false).
bool onWalkableGround = _controller.isGrounded || WalkableGroundBelow();
if (!onWalkableGround)
{
@@ -901,10 +861,8 @@ public class PlayerCharacterController : MonoBehaviour
/// <summary>
/// Offsets only the visual model (mesh + its baked paint collider) along the wall normal by the
/// given signed distance (+ = out from the wall, - = into it), relative to the model's authored
/// local position. The CharacterController capsule is untouched, so the lean can sink into the
/// wall where the capsule would be blocked. Applied in the player's local space so it tracks the
/// body yaw; offset 0 restores the authored pose.
/// given signed distance (+ = out, - = into the wall), relative to its authored local position.
/// The capsule is untouched, so the lean can sink into the wall where the capsule would be blocked.
/// </summary>
private void ApplyMeshOffset(float offset) => SetMeshLean(_wallNormal, offset);
@@ -922,11 +880,9 @@ public class PlayerCharacterController : MonoBehaviour
}
/// <summary>
/// Re-applies the resting mesh offset every non-climbing frame in a single write of
/// <see cref="meshOffsetTarget"/>: eases the leftover wall lean back toward the authored pose
/// (latched exit normal) while composing in the ground mouse raise and any pose y-offset. Writing
/// unconditionally (even at rest) keeps this the sole owner of the offset transform, so a held
/// ground raise stays applied after the residual has reached zero. Offset 0 restores the pose.
/// Re-applies the resting mesh offset every non-climbing frame in a single write: eases the
/// leftover wall lean home (along the latched exit normal) while composing in the ground raise.
/// Writing unconditionally keeps this the sole owner of the offset transform.
/// </summary>
private void EaseMeshOffsetToRest(float dt)
{
@@ -936,13 +892,11 @@ public class PlayerCharacterController : MonoBehaviour
}
/// <summary>
/// Ground pose-adjust via the mouse, mirroring the wall-climb scheme: holding the LEFT button and
/// dragging up/down raises/lowers the visual mesh vertically (<see cref="_groundOffset"/>, capsule
/// untouched), holding the RIGHT button and dragging left/right turns the body (world-up yaw). The
/// raise eases back to the authored height while moving and holds while standing or dragging; the
/// turn persists until movement re-aims the body. Returns true while a right-button turn is active
/// so the caller skips auto-rotate-toward-movement that frame. Call site gates on ControlEnabled so
/// paint mode keeps the left button for painting.
/// Ground pose-adjust, mirroring the wall-climb mouse scheme: LEFT drag raises/lowers the visual
/// mesh (<see cref="_groundOffset"/>, capsule untouched), RIGHT drag turns the body. The raise
/// eases back while moving and holds while standing; the turn persists until movement re-aims the
/// body. Returns true while a right-button turn is active so the caller skips
/// rotate-toward-movement that frame. The call site gates on ControlEnabled (paint mode owns the left button).
/// </summary>
private bool HandleGroundPoseAdjust(float h, float v, float dt)
{
@@ -1013,12 +967,10 @@ public class PlayerCharacterController : MonoBehaviour
private const float ProbeReachSlack = 0.5f; // extra ray length beyond the capsule surface (scale-1 units)
/// <summary>
/// True if the collider belongs to ANY player avatar -- this one OR a networked remote. Every climb
/// probe must skip players: avatars still collide physically (this player's CharacterController vs a
/// remote's baked MeshCollider), but another chameleon's body must never register as a climbable
/// wall/ceiling/ground. Replaces the old self-only IsChildOf check -- self is a player too, so it
/// stays excluded. Finds the controller even on remotes, where the component is disabled (only the
/// GameObject's active state gates GetComponentInParent, not the component's enabled flag).
/// True if the collider belongs to ANY player avatar (self or a networked remote): avatars still
/// collide physically, but another chameleon's body must never register as climbable. Finds the
/// controller even on remotes, where the component is disabled (GetComponentInParent only checks
/// the GameObject's active state, not the component's enabled flag).
/// </summary>
private static bool IsPlayerCollider(Collider c) => c.GetComponentInParent<PlayerCharacterController>();