diff --git a/Assets/Scripts/Components/Concealment/ConcealmentColliderBaker.cs b/Assets/Scripts/Components/Concealment/ConcealmentColliderBaker.cs
index 9b3eb3c..1b604f4 100644
--- a/Assets/Scripts/Components/Concealment/ConcealmentColliderBaker.cs
+++ b/Assets/Scripts/Components/Concealment/ConcealmentColliderBaker.cs
@@ -7,26 +7,21 @@ using UnityEditor.SceneManagement;
#endif
///
-/// Editor tool that decomposes an environment mesh (typically an open-shell / non-watertight
-/// furniture mesh) into a small set of CONVEX MeshColliders via surface clustering, so
-/// can treat the furniture as "embeddable" through its
-/// convex-collider ClosestPoint path WITHOUT a single convex hull filling the open compartments.
+/// Editor tool that decomposes an environment mesh (typically open-shell furniture) into a small set
+/// of CONVEX MeshColliders via surface clustering, so can treat the
+/// furniture as "embeddable" through its convex-collider ClosestPoint path WITHOUT a single convex
+/// hull filling the open compartments.
///
-/// Approach (no voxels, no native plugin — "approximate convex decomposition" on the surface):
-/// 1. Weld coincident vertices so connectivity is detected across split-normal/UV seams.
-/// 2. Grow connected triangle regions into SLABS: a region keeps absorbing edge-adjacent
-/// triangles while it stays thinner than along its flattest
-/// axis (PCA minor axis). This spans both faces of one board (they are within a board's
-/// thickness) but refuses to jump the gap to the next board (that would thicken the region).
-/// 3. Convex-hull each slab into its own convex collider. Boards become thin convex slabs;
-/// the empty space between boards is covered by no collider, so it stays hollow.
+/// Approach (approximate convex decomposition on the surface — no voxels, no native plugin): weld
+/// coincident vertices so connectivity survives split-normal/UV seams, grow edge-connected triangle
+/// regions into SLABS kept thinner than along their PCA minor axis
+/// (spans both faces of one board, refuses to jump the gap to the next), then convex-hull each slab.
+/// The empty space between boards is covered by no collider, so it stays hollow.
///
/// The colliders are real child GameObjects referencing SAVED hull-mesh assets, so they survive
-/// domain reload / play-mode / scene save (unlike an in-memory-only bake).
-///
-/// Runtime-inert: the component exists only so can detect it in a
-/// collider's parent and skip the furniture's own concave MeshCollider (exactly like a baked
-/// ConcealmentVolume does) — the generated convex slabs are the authoritative embedding shape.
+/// domain reload / play mode / scene save. Runtime-inert: the component exists only so
+/// can detect it in a collider's parent and skip the furniture's own
+/// concave MeshCollider — the generated convex slabs are the authoritative embedding shape.
///
[DisallowMultipleComponent]
public class ConcealmentColliderBaker : MonoBehaviour
@@ -240,7 +235,7 @@ public class ConcealmentColliderBaker : MonoBehaviour
AssetDatabase.SaveAssets();
EditorUtility.SetDirty(this);
- UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(gameObject.scene);
+ EditorSceneManager.MarkSceneDirty(gameObject.scene);
Debug.Log($"[ConcealmentColliderBaker] '{name}': baked {made} convex collider(s)" +
(dropped > 0 ? $" (+{dropped} smaller slab(s) dropped by Max Colliders={maxColliders})" : "") +
@@ -262,7 +257,7 @@ public class ConcealmentColliderBaker : MonoBehaviour
EditorUtility.SetDirty(this);
if (gameObject.scene.IsValid())
- UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(gameObject.scene);
+ EditorSceneManager.MarkSceneDirty(gameObject.scene);
}
private static void EnsureFolder()
diff --git a/Assets/Scripts/Components/Concealment/ConcealmentMeter.cs b/Assets/Scripts/Components/Concealment/ConcealmentMeter.cs
index cb66bf0..ca7f733 100644
--- a/Assets/Scripts/Components/Concealment/ConcealmentMeter.cs
+++ b/Assets/Scripts/Components/Concealment/ConcealmentMeter.cs
@@ -2,27 +2,19 @@ using System.Collections.Generic;
using UnityEngine;
///
-/// Standalone volumetric concealment meter for the painted character.
-///
-/// Each evaluation bakes the character's current (deformed) skinned mesh, voxelizes its
-/// SOLID volume, and reports what fraction of that volume sits INSIDE environment colliders
-/// (= embedded / hidden) versus exposed. is the headline value,
+/// Standalone volumetric concealment meter for the painted character. Each evaluation bakes the
+/// current (deformed) skinned mesh, voxelizes its SOLID volume, and reports the fraction embedded
+/// inside environment colliders: is the headline value,
/// its complement.
///
-/// Pipeline:
-/// 1. -> mesh in the renderer's LOCAL space, then the
-/// player's world scale is divided back out (BakeMesh bakes that scale IN; see PlayerScaler) so the
-/// convention world = transform.localToWorldMatrix holds at ANY size — the same fixup SkinnedColliderBaker
-/// applies, and the unit-scale mesh MeshPainter borrows from it.
-/// 2. Column ray-stabbing in local space: each (x,z) grid column collects the Y where the mesh
-/// surface crosses it; parity (odd # of crossings below a voxel center) marks interior voxels.
-/// This is the same XZ triangle rasterization MeshPainter.GetIslandMask uses for UV islands.
-/// 3. Each interior voxel center -> world; tested against broadphase-filtered environment
-/// colliders: convex primitives / convex MeshCollider via ClosestPoint, Terrain via
-/// SampleHeight, optional concave MeshCollider via raycast parity.
+/// Pipeline: BakeMesh with the player's world scale divided back out (see Evaluate), then column
+/// ray-stabbing in local space — odd # of surface crossings below a voxel center marks it interior
+/// (same XZ triangle rasterization MeshPainter.GetIslandMask uses) — then each interior voxel center
+/// is tested in world space against broadphase-filtered environment colliders: convex via
+/// ClosestPoint, Terrain via SampleHeight, optional concave MeshCollider via raycast parity.
///
-/// Surface-watertightness caveat: parity assumes a closed mesh. After the Blender merge-by-distance
-/// the paintman rig is effectively watertight; small leaks only perturb the percentage slightly.
+/// Watertightness caveat: parity assumes a closed mesh. After the Blender merge-by-distance the
+/// paintman rig is effectively watertight; small leaks only perturb the percentage slightly.
///
[DisallowMultipleComponent]
public class ConcealmentMeter : MonoBehaviour
@@ -134,12 +126,11 @@ public class ConcealmentMeter : MonoBehaviour
targetRenderer.BakeMesh(_baked);
- // BakeMesh freezes the bone deformation — which already carries the player's world scale (PlayerScaler)
- // — into the mesh. Everything below treats the bake as unit-scale LOCAL geometry and raises it to world
- // via targetRenderer.localToWorldMatrix, which re-applies that same scale. Divide it back out first,
- // else a scaled player is measured at scale² (voxel world positions, broadphase box AND gizmos), which
- // also corrupts EmbeddedPercent. No-op at scale 1; the uniform divide is rotation-independent, which the
- // player's uniform scale guarantees. Mirrors SkinnedColliderBaker's identical fixup.
+ // BakeMesh freezes the bone deformation — which already carries the player's world scale
+ // (PlayerScaler) — into the mesh, and everything below re-applies that scale by raising the
+ // bake to world via localToWorldMatrix, so a scaled player would be measured at scale²,
+ // corrupting EmbeddedPercent. Divide it back out first. No-op at scale 1; the player's
+ // uniform scale makes the divide rotation-independent. Mirrors SkinnedColliderBaker's fixup.
GeometryUtil.RescaleToUnitScale(_baked, targetRenderer.transform.lossyScale, _vbuf);
// mesh.vertices/.triangles return fresh arrays per call - at 10 evals/s that is steady GC
@@ -361,7 +352,7 @@ public class ConcealmentMeter : MonoBehaviour
TerrainData td = terr.terrainData;
float lx = p.x - tp.x, lz = p.z - tp.z;
if (lx < 0f || lz < 0f || lx > td.size.x || lz > td.size.z) continue;
- if (p.y <= tp.y + terr.SampleHeight(p)) return true; // below the surface
+ if (p.y <= tp.y + terr.SampleHeight(p)) return true;
}
}
diff --git a/Assets/Scripts/Components/Concealment/ConcealmentVolume.cs b/Assets/Scripts/Components/Concealment/ConcealmentVolume.cs
index e07cf2b..820fe4f 100644
--- a/Assets/Scripts/Components/Concealment/ConcealmentVolume.cs
+++ b/Assets/Scripts/Components/Concealment/ConcealmentVolume.cs
@@ -4,19 +4,16 @@ using UnityEngine;
///
/// Editor-baked solid/enclosed voxel volume for a single (typically non-convex, non-watertight)
/// environment mesh. Runtime lookup is an O(1) grid test, so can
-/// treat furniture as "embeddable" without per-voxel raycast parity (which is unreliable on the
-/// open-shell furniture meshes of asset kits).
+/// treat furniture as "embeddable" without per-voxel raycast parity (unreliable on the open-shell
+/// furniture meshes of asset kits).
///
-/// Bake (editor only): voxelizes the source mesh in LOCAL space and marks a voxel as INSIDE when
-/// either classifier fires:
-/// * Generalized Winding Number > 0.5 -> robustly captures SOLID material even for open shells.
-/// * Exterior flood-fill cannot reach it -> captures fully ENCLOSED air cavities.
-/// The union is conservative in the safe direction: an unreachable solid OR an enclosed pocket both
-/// count as concealment; open regions (seat surface, under a thin table) correctly read as exposed.
+/// The bake marks a voxel INSIDE when either classifier fires: generalized winding number > 0.5
+/// (captures SOLID material, robust for open shells) OR unreachable by exterior flood-fill
+/// (captures fully ENCLOSED air cavities). The union errs in the safe direction — solid or enclosed
+/// both count as concealment; open regions (seat surface, under a thin table) read as exposed.
///
-/// Query is done via ; bake and query share the SAME mesh-local space
-/// (verts at bake time, transform.InverseTransformPoint at query time), so position/rotation/scale
-/// are handled uniformly.
+/// Bake and query () share the SAME mesh-local space, so
+/// position/rotation/scale are handled uniformly.
///
[DisallowMultipleComponent]
public class ConcealmentVolume : MonoBehaviour
diff --git a/Assets/Scripts/Components/Controller/FirstPersonController.cs b/Assets/Scripts/Components/Controller/FirstPersonController.cs
index 7bd5367..6644c20 100644
--- a/Assets/Scripts/Components/Controller/FirstPersonController.cs
+++ b/Assets/Scripts/Components/Controller/FirstPersonController.cs
@@ -3,16 +3,10 @@ using Unity.Cinemachine;
using Chameleon.Components;
///
-/// First-person camera. Drives its OWN dedicated : each frame it snaps
-/// to the player's dedicated FirstPersonTarget (the eye anchor) for position and
-/// applies free mouse-look (yaw/pitch) for rotation. While active the player's mesh is hidden so the
-/// body isn't seen from the inside. Movement stays with the player controller, which reads the
-/// brain-driven Main Camera forward, so WASD is automatically relative to this view.
-///
-/// Activation is driven by via : it raises the
-/// vcam priority so the CinemachineBrain blends to it, snaps look to the current view (no jump), hides
-/// the mesh and locks the cursor. While inactive the vcam rests at a low priority and this component
-/// does nothing.
+/// First-person camera on its own dedicated : snaps to the player's eye
+/// anchor, applies free mouse-look, and hides the player mesh while active. Driven by
+/// via ; WASD stays view-relative automatically
+/// because movement reads the brain-driven Main Camera forward.
///
[RequireComponent(typeof(CinemachineCamera))]
[DisallowMultipleComponent]
diff --git a/Assets/Scripts/Components/Controller/FreeCamController.cs b/Assets/Scripts/Components/Controller/FreeCamController.cs
index f57e525..37e1a69 100644
--- a/Assets/Scripts/Components/Controller/FreeCamController.cs
+++ b/Assets/Scripts/Components/Controller/FreeCamController.cs
@@ -5,14 +5,10 @@ using UnityEngine.InputSystem;
#endif
///
-/// Free-flying spectator camera. Drives its OWN dedicated by moving
-/// this transform with WASD (relative to where it looks), Q/E (or Space/Left Ctrl) for down/up
-/// and the mouse for look. Collision is a capsule sized like the
-/// player's, so the freecam slides along world geometry instead of clipping through walls/floors.
-///
-/// Activation is driven by via : it raises the vcam
-/// priority so the CinemachineBrain blends to it, snaps the rig to the current view (no jump) and
-/// locks the cursor. While inactive the vcam rests at a low priority and this component does nothing.
+/// Free-flying spectator camera on its own dedicated : WASD flight relative
+/// to the view, Q/E (or Space/Left Ctrl) for down/up, mouse look. A
+/// capsule makes it slide along world geometry instead of clipping. Driven by
+/// via ; while inactive this component does nothing.
///
[RequireComponent(typeof(CharacterController))]
[DisallowMultipleComponent]
@@ -194,5 +190,4 @@ public class FreeCamController : MonoBehaviour
return Input.GetKey(KeyCode.LeftShift);
#endif
}
-
}
diff --git a/Assets/Scripts/Components/Controller/PlayerPoseController.cs b/Assets/Scripts/Components/Controller/PlayerPoseController.cs
index db819ab..ceca823 100644
--- a/Assets/Scripts/Components/Controller/PlayerPoseController.cs
+++ b/Assets/Scripts/Components/Controller/PlayerPoseController.cs
@@ -8,16 +8,10 @@ using UnityEngine.InputSystem;
#endif
///
-/// Holds a list of s plus the active index. The pose system owns ALL character
-/// animation (the Animator is left empty): standing / airborne / wall / ceiling holds the active pose's
-/// idle clip, walking/sprinting on the ground plays its optional walk/run clip (unset = idle held).
-///
-/// One key cycles poses (wrapping); sprinting in any state snaps back to the default pose, held
-/// continuously so the reset shows up immediately. Clips play through a tiny
-/// (two-input mixer on an ) that crossfades every clip switch over
-/// . Each pose also carries its own CharacterController capsule (eased,
-/// so a low pose can crawl under furniture) and per-pose camera y-offsets (third-person framing and
-/// first-person eye).
+/// Owns ALL character animation (the Animator is left empty): plays the active 's
+/// idle or optional walk/run/fall clip through a two-input mixer that
+/// crossfades every clip switch. One key cycles poses, sprinting resets to the default pose; each pose
+/// also carries its own eased CharacterController capsule and per-pose camera y-offsets.
///
[RequireComponent(typeof(PlayerCharacterController))]
[DisallowMultipleComponent]
@@ -115,10 +109,9 @@ public class PlayerPoseController : MonoBehaviour
private Quaternion[] _restRot;
private Vector3[] _restScale;
- /// Index of the currently selected pose.
public int CurrentIndex => _currentIndex;
- /// Locomotion category resolved this frame (which clip the active pose is playing). The owner publishes it for network sync.
+ /// Locomotion category resolved this frame; the owner publishes it for network sync.
public Locomotion CurrentLocomotion => _currentLocomotion;
///
@@ -219,7 +212,6 @@ public class PlayerPoseController : MonoBehaviour
_firstPersonCameraOffset = EaseCameraOffset(_firstPersonCameraOffset, firstPersonTarget);
}
- /// Eases a camera y-offset toward its target at ; 0 snaps.
private float EaseCameraOffset(float current, float target)
{
return cameraOffsetSpeed > 0f
@@ -261,7 +253,6 @@ public class PlayerPoseController : MonoBehaviour
return poses[Mathf.Clamp(_currentIndex, 0, poses.Count - 1)];
}
- /// Maps the controller's current movement state to a locomotion category for clip selection.
private Locomotion ResolveLocalLocomotion()
{
if (_player.IsMovingOnGround)
@@ -375,7 +366,6 @@ public class PlayerPoseController : MonoBehaviour
_posePlaying = true;
}
- /// Builds the graph (a 2-input mixer feeding the Animator output) lazily if it isn't alive yet.
private void EnsureGraph()
{
if (_graph.IsValid())
@@ -426,7 +416,6 @@ public class PlayerPoseController : MonoBehaviour
ApplyBlendWeights();
}
- /// Advances an in-flight crossfade and drops the outgoing pose once it has fully faded.
private void AdvanceBlend(float dt)
{
if (!_blending)
@@ -446,7 +435,6 @@ public class PlayerPoseController : MonoBehaviour
ApplyBlendWeights();
}
- /// Pushes the current blend onto the mixer inputs (smoothstepped while a "from" pose exists).
private void ApplyBlendWeights()
{
if (!_mixer.IsValid())
diff --git a/Assets/Scripts/Components/Controller/ThirdPersonOrbitInput.cs b/Assets/Scripts/Components/Controller/ThirdPersonOrbitInput.cs
index b85607c..355f9f9 100644
--- a/Assets/Scripts/Components/Controller/ThirdPersonOrbitInput.cs
+++ b/Assets/Scripts/Components/Controller/ThirdPersonOrbitInput.cs
@@ -2,10 +2,9 @@ using UnityEngine;
using Unity.Cinemachine;
///
-/// Feeds mouse input into a so the camera
-/// orbits the follow target. Mouse movement drives yaw/pitch, the wheel zooms.
-/// The orbit behaviour depends on (usually set by the
-/// GameModeManager). Attach to the camera rig (the CinemachineOrbitalFollow).
+/// Feeds mouse input into a : mouse movement drives yaw/pitch,
+/// the wheel zooms, middle-mouse pans (paint mode only). Orbit behaviour depends on
+/// (usually set by the GameModeManager). Attach to the camera rig.
///
[RequireComponent(typeof(CinemachineOrbitalFollow))]
[DisallowMultipleComponent]
@@ -346,5 +345,4 @@ public class ThirdPersonOrbitInput : MonoBehaviour
// Offset is camera-local (x=right, y=up); the extension translates eye + look-at together -> true pan.
_camOffset.Offset = new Vector3(_pan.x, _pan.y, 0f);
}
-
}
diff --git a/Assets/Scripts/Components/FrameRateLimiter.cs b/Assets/Scripts/Components/FrameRateLimiter.cs
index fbfc23f..f9db245 100644
--- a/Assets/Scripts/Components/FrameRateLimiter.cs
+++ b/Assets/Scripts/Components/FrameRateLimiter.cs
@@ -32,7 +32,6 @@ public class FrameRateLimiter : MonoBehaviour
Apply();
}
- /// Applies the current FPS + VSync settings.
public void Apply()
{
if (limitFrameRate)
diff --git a/Assets/Scripts/Components/FurShellRenderer.cs b/Assets/Scripts/Components/FurShellRenderer.cs
index 7dee4ad..0686901 100644
--- a/Assets/Scripts/Components/FurShellRenderer.cs
+++ b/Assets/Scripts/Components/FurShellRenderer.cs
@@ -3,17 +3,14 @@ using UnityEngine.Rendering;
namespace Chameleon.Components
{
- // Drives the shell-texturing fur look for FurShell.shader. Draws the source mesh _shellCount times
- // via GPU instancing (Graphics.RenderMeshInstanced); every instance is one shell layer and reads its
- // index from SV_InstanceID in the shader to pick its extrusion height. Put this next to a MeshFilter
- // (static mesh) OR on an animated character's SkinnedMeshRenderer that already carries a
- // SkinnedColliderBaker - then the fur borrows that component's per-frame baked pose and follows the
- // animation. Assign the FurShell material either way.
+ // Shell-texturing fur for FurShell.shader: draws the source mesh _shellCount times via GPU instancing
+ // (Graphics.RenderMeshInstanced); each instance is one shell layer and picks its extrusion height from
+ // SV_InstanceID in the shader. Put this next to a MeshFilter (static mesh) OR on a skinned avatar whose
+ // SkinnedColliderBaker provides the per-frame baked pose the fur follows.
//
- // This component is the SINGLE source of truth for the fur look: every tunable shader parameter is a
- // serialized field here and gets pushed via a MaterialPropertyBlock. The material's own inspector
- // sliders are therefore only template defaults - they are overridden live. (Use the "Copy Values From
- // Material" context menu to pull a material you already tuned into these fields.)
+ // SINGLE source of truth for the fur look: every tunable shader parameter is a serialized field here,
+ // pushed via a MaterialPropertyBlock, so the material's own inspector sliders are only template
+ // defaults. (Use the "Copy Values From Material" context menu to pull in a hand-tuned material.)
//
// The draw is issued from RenderPipelineManager.beginCameraRendering (not Update/LateUpdate) so it
// renders reliably for EVERY camera pass - including the Scene View and edit-mode repaints, where
@@ -80,11 +77,9 @@ namespace Chameleon.Components
[SerializeField] Vector2 _windDirection = new Vector2(1f, 0f);
[Header("X-Ray Reveal")]
- // The sibling MeshPainter's body see-through reveal is mirrored onto the fur so the strands glow WITH
- // the reveal instead of hiding it under opaque fur (colour/pulse are pulled from the painter, in phase
- // with the body). The fur draws up to _shellCount stacked TRANSPARENT shells, so that additive glow
- // composites once per overlapping layer and blows out; this scales the per-shell glow down to tame the
- // stacking so the total reads at roughly the body's intensity. Lower = subtler.
+ // The fur draws up to _shellCount stacked TRANSPARENT shells, so the mirrored X-ray glow composites
+ // once per overlapping layer and blows out; this scales the per-shell glow down so the total reads
+ // at roughly the body's intensity. Lower = subtler.
[SerializeField, Range(0f, 1f)] float _xrayFurStrength = 0.2f;
[Header("Rendering")]
@@ -265,18 +260,16 @@ namespace Chameleon.Components
_mpb.SetFloat(FurObjScaleId, transform.lossyScale.y);
// Mirror the sibling painter's live body X-ray reveal onto the fur so the strands glow WITH the
- // reveal (see-through camo) instead of hiding it under opaque fur. Same colour/pulse/flash the
- // body uses => in phase. ToRenderColor matches the raw MPB upload to Material.SetColor's gamma
- // handling (same convention as the fur tint above). Off (alpha 0) when no painter or not revealed
- // => the shader's additive term is zero, so the fur is unchanged.
+ // reveal instead of hiding it under opaque fur. Same colour/pulse the body uses => in phase.
+ // ToRenderColor matches the raw MPB upload to Material.SetColor's gamma handling (same
+ // convention as the fur tint above). Off (alpha 0) when no painter or not revealed => the
+ // shader's additive term is zero, so the fur is unchanged.
if (_painter)
{
_mpb.SetColor(XRayColorId, ToRenderColor(_painter.CurrentXRayColor));
_mpb.SetFloat(XRayPulseSpeedId, _painter.XRayPulseSpeed);
_mpb.SetFloat(XRayPulseStrengthId, _painter.XRayPulseStrength);
_mpb.SetFloat(XRayPulseShapeId, _painter.XRayPulseShape);
- // Per-shell glow scale: the body writes the reveal once (opaque) but the fur stacks many
- // transparent shells that each composite it, so scale it down to keep the total near the body.
_mpb.SetFloat(XRayFurStrengthId, _xrayFurStrength);
}
}
diff --git a/Assets/Scripts/Components/HeroCutout.cs b/Assets/Scripts/Components/HeroCutout.cs
index 43585d1..df22422 100644
--- a/Assets/Scripts/Components/HeroCutout.cs
+++ b/Assets/Scripts/Components/HeroCutout.cs
@@ -2,16 +2,15 @@ using UnityEngine;
using Unity.Cinemachine;
///
-/// Drives the world-space "hero cutout": occluder geometry inside a cone from the third-person camera
-/// (apex) to the player is clipped away, punching a see-through funnel toward the character. Each late
-/// frame this feeds two global shader properties (_HeroCutoutTarget, _HeroCutoutParams)
-/// that Custom/HeroCutoutLit reads.
+/// World-space "hero cutout": occluder geometry inside a cone from the third-person camera (apex) to
+/// the player is clipped away, punching a see-through funnel toward the character. Each late frame this
+/// feeds two globals (_HeroCutoutTarget, _HeroCutoutParams) that Custom/HeroCutoutLit reads.
///
-/// Arming is deliberately simple: ONE ray from the camera toward the player - blocked = armed, clear
-/// view = occluders stay solid. The cut volume lives entirely in the shader: everything between camera
-/// and player (within the cone) is cut, geometry behind the player stays solid, and the far end fades
-/// across a soft depth band () instead of a hard plane. The cone radius grows
-/// with camera distance, so the hole keeps a constant on-screen size at every occluder depth.
+/// Arming is deliberately simple: ONE ray camera -> player; blocked = armed, clear view = occluders stay
+/// solid. The cut volume lives entirely in the shader: everything between camera and player (within the
+/// cone) is cut, geometry behind the player stays solid, and the far end fades across a soft depth band
+/// () instead of a hard plane. The cone tapers toward the camera, so the hole
+/// keeps a constant on-screen size at every occluder depth.
///
/// Active only while the third-person orbit vcam is the live Cinemachine camera; disarming clears the
/// global flag (also the zero-initialised default), so scenes without this driver render cutout
@@ -193,9 +192,8 @@ public class HeroCutout : MonoBehaviour
}
///
- /// Lazily resolves the orbit vcam and its Cinemachine Deoccluder (they may live in a scene that
- /// loads after this driver). Captures the deoccluder's authored obstacle-avoidance state once so it
- /// can be restored later.
+ /// Lazily resolves the orbit vcam and its Deoccluder (they may live in a scene that loads after this
+ /// driver); captures the deoccluder's authored obstacle-avoidance state once so it can be restored.
///
private void EnsureRefs()
{
@@ -209,9 +207,9 @@ public class HeroCutout : MonoBehaviour
}
///
- /// Switches the deoccluder's obstacle avoidance (the camera pull-in) off while this driver replaces
- /// it with the cutout, or restores the authored state when the replacement is turned off. Only
- /// toggles on transitions, so it never fights a value changed elsewhere mid-frame.
+ /// Toggles the deoccluder's obstacle avoidance (the camera pull-in) off/back per
+ /// . Only acts on transitions, so it never fights a value
+ /// changed elsewhere mid-frame.
///
private void ApplyPullInSuppression()
{
@@ -229,7 +227,6 @@ public class HeroCutout : MonoBehaviour
}
}
- /// Restores the deoccluder's authored pull-in if this driver had suppressed it.
private void RestoreDeoccluderPullIn()
{
if (_pullInSuppressed && _deoccluder)
diff --git a/Assets/Scripts/Components/HunterLaserBeam.cs b/Assets/Scripts/Components/HunterLaserBeam.cs
index d543c07..caad03e 100644
--- a/Assets/Scripts/Components/HunterLaserBeam.cs
+++ b/Assets/Scripts/Components/HunterLaserBeam.cs
@@ -2,12 +2,11 @@ using UnityEngine;
using UnityEngine.Rendering;
///
-/// Momentary world-space laser beam for a hunter's shot. Purely visual (no networking): a LineRenderer drawn
-/// from the muzzle to the hit point that fades IN fast then OUT over its lifetime, animating both opacity
-/// (via per-vertex colour alpha, read by the Chameleon/Laser shader) and thickness (via widthMultiplier).
-/// Self-contained: it builds its own LineRenderer child + an additive material at runtime, so it can be added
-/// to a hunter avatar purely in code with no prefab authoring. Call to trigger; the beam
-/// recycles itself (disables the renderer) once it expires.
+/// Momentary world-space laser beam for a hunter's shot. Purely visual (no networking): a LineRenderer
+/// from muzzle to hit point that fades IN fast then OUT, animating opacity (per-vertex colour alpha, read
+/// by the Chameleon/Laser shader) and thickness (widthMultiplier). Self-contained: builds its own
+/// LineRenderer child + additive material at runtime, so it can be added to an avatar purely in code with
+/// no prefab authoring. Call to trigger; the beam disables its renderer once expired.
///
[DisallowMultipleComponent]
public sealed class HunterLaserBeam : MonoBehaviour
diff --git a/Assets/Scripts/Components/MeshPainter/MeshPainter.Backend.cs b/Assets/Scripts/Components/MeshPainter/MeshPainter.Backend.cs
index 1c5eb62..36ee9bf 100644
--- a/Assets/Scripts/Components/MeshPainter/MeshPainter.Backend.cs
+++ b/Assets/Scripts/Components/MeshPainter/MeshPainter.Backend.cs
@@ -20,7 +20,6 @@ public partial class MeshPainter
}
}
- /// Switches the backend at runtime, carrying over all painted maps.
private void SwitchBackend(bool toGpu)
{
var canonical = new Color32[MAP_COUNT][];
@@ -66,7 +65,6 @@ public partial class MeshPainter
if (!_backendSeeded) ReseedCanonical(null);
}
- /// Tears down the active backend and sets up the requested one on the given maps.
private void RebuildBackend(bool gpu, Color32[][] canonical)
{
if (_gpuActive) TeardownGpu();
@@ -75,7 +73,6 @@ public partial class MeshPainter
_hasLastScreen = false;
}
- /// Seed source for a map: the supplied canonical data when valid, else the material seed.
private Color32[] ResolveSeed(int mi, Color32[][] canonical)
{
return (canonical != null && canonical[mi] != null && canonical[mi].Length == textureSize * textureSize)
diff --git a/Assets/Scripts/Components/MeshPainter/MeshPainter.Gpu.cs b/Assets/Scripts/Components/MeshPainter/MeshPainter.Gpu.cs
index 12de2fd..beb7912 100644
--- a/Assets/Scripts/Components/MeshPainter/MeshPainter.Gpu.cs
+++ b/Assets/Scripts/Components/MeshPainter/MeshPainter.Gpu.cs
@@ -85,7 +85,7 @@ public partial class MeshPainter
if (!DeferInitialSeed) SeedGpuMap(mi, canonical); // deferred: RTs stay unseeded until the snapshot re-seed
// Color maps render to the display RT; the MS map exposes its packed final RT.
- AssignMapTexture(mi, gm.final != null ? gm.final : gm.display);
+ AssignMapTexture(mi, gm.final ? gm.final : gm.display);
}
return true;
}
@@ -133,7 +133,7 @@ public partial class MeshPainter
{
SeedGpuMap(mi, canonical);
var gm = _gpu[mi];
- AssignMapTexture(mi, gm.final != null ? gm.final : gm.display);
+ AssignMapTexture(mi, gm.final ? gm.final : gm.display);
}
}
@@ -426,11 +426,11 @@ public partial class MeshPainter
Graphics.Blit(gm.display, gm.temp, _dilateMat, 0);
var swap = gm.display; gm.display = gm.temp; gm.temp = swap;
- if (gm.final == null) AssignMapTexture(mi, gm.display);
+ if (!gm.final) AssignMapTexture(mi, gm.display);
}
// MS: pack the work layout (R=metallic, G=smoothness) into the URP final layout.
- if (gm.final != null) PackMs(gm);
+ if (gm.final) PackMs(gm);
}
GL.sRGBWrite = _prevSRGB;
diff --git a/Assets/Scripts/Components/MeshPainter/MeshPainter.Net.cs b/Assets/Scripts/Components/MeshPainter/MeshPainter.Net.cs
index d26f897..1dc2e25 100644
--- a/Assets/Scripts/Components/MeshPainter/MeshPainter.Net.cs
+++ b/Assets/Scripts/Components/MeshPainter/MeshPainter.Net.cs
@@ -73,7 +73,6 @@ public partial class MeshPainter
/// True if anyone is listening for replicated stamps (the owner is networked + painting).
private bool WantsStampCapture => OnStampCommitted != null;
- /// Builds the replicated stamp from the current brush state + resolved placement and raises the event.
private void EmitStamp(Vector3 p, float r, Vector3 u, Vector3 v, Vector3 n)
{
var handler = OnStampCommitted;
diff --git a/Assets/Scripts/Components/MeshPainter/MeshPainter.Persistence.cs b/Assets/Scripts/Components/MeshPainter/MeshPainter.Persistence.cs
index d150c4e..ef82013 100644
--- a/Assets/Scripts/Components/MeshPainter/MeshPainter.Persistence.cs
+++ b/Assets/Scripts/Components/MeshPainter/MeshPainter.Persistence.cs
@@ -381,7 +381,6 @@ public partial class MeshPainter
return dst;
}
- /// Bilinear blend of four byte samples (rounded, clamped to 0..255).
private static byte Bilerp(byte c00, byte c10, byte c01, byte c11, float fx, float fy)
{
float top = c00 + (c10 - c00) * fx;
diff --git a/Assets/Scripts/Components/MeshPainter/MeshPainter.Pick.cs b/Assets/Scripts/Components/MeshPainter/MeshPainter.Pick.cs
index 2f3728f..b2e28c2 100644
--- a/Assets/Scripts/Components/MeshPainter/MeshPainter.Pick.cs
+++ b/Assets/Scripts/Components/MeshPainter/MeshPainter.Pick.cs
@@ -296,7 +296,7 @@ public partial class MeshPainter
for (int i = 0; i < count; i++)
{
TerrainLayer L = layers[i];
- if (L == null) continue;
+ if (!L) continue;
float w = weights[0, 0, i];
if (w <= 0f) continue;
@@ -437,7 +437,7 @@ public partial class MeshPainter
private static Color SampleTexel(Texture tex, Vector2 uv, bool srgb)
{
Texture2D t2d = tex as Texture2D;
- if (t2d != null && t2d.isReadable)
+ if (t2d && t2d.isReadable)
return t2d.GetPixelBilinear(uv.x, uv.y);
int w = Mathf.Max(1, tex.width), h = Mathf.Max(1, tex.height);
diff --git a/Assets/Scripts/Components/MeshPainter/MeshPainter.Setup.cs b/Assets/Scripts/Components/MeshPainter/MeshPainter.Setup.cs
index 492cee0..63b8821 100644
--- a/Assets/Scripts/Components/MeshPainter/MeshPainter.Setup.cs
+++ b/Assets/Scripts/Components/MeshPainter/MeshPainter.Setup.cs
@@ -260,8 +260,8 @@ public partial class MeshPainter
{
_materialInstance.EnableKeyword("_PARALLAXMAP");
_materialInstance.EnableKeyword("_NORMALMAP");
- if (GetMatFloat(ParallaxId, 0.02f) < 0.02f)
- SetMatFloat(ParallaxId, 0.02f);
+ if (GetMatFloat(ParallaxId, ParallaxBase) < ParallaxBase)
+ SetMatFloat(ParallaxId, ParallaxBase);
}
}
diff --git a/Assets/Scripts/Components/MeshPainter/MeshPainter.cs b/Assets/Scripts/Components/MeshPainter/MeshPainter.cs
index 3549c1d..55a6fb3 100644
--- a/Assets/Scripts/Components/MeshPainter/MeshPainter.cs
+++ b/Assets/Scripts/Components/MeshPainter/MeshPainter.cs
@@ -7,16 +7,9 @@ using UnityEngine.InputSystem;
using Random = UnityEngine.Random; // System (for Action) also defines Random -> keep the UnityEngine one
///
-/// Live mesh painting in Play mode. Three paint maps are driven at once (each toggleable): albedo
-/// (_BaseMap), an HDRP-style mask map (R = metallic, G = occlusion, A = smoothness; one texture bound
-/// to BOTH _MetallicGlossMap and _OcclusionMap) and emission (_EmissionMap). Two backends, switchable
-/// live via : GPU shader painting into RenderTextures (fast, default) and CPU
-/// rasterization (identical result). Both paint seamlessly across UV seams because the brush is
-/// evaluated in 3D local space around the hit point, capturing triangles on both sides of a seam.
-///
-/// The brush is a square stamp in the surface tangent plane shaped by a grayscale mask (mask value =
-/// per-texel opacity); an empty mask list falls back to a procedural circle (classic round brush).
-/// A non-destructive preview () shows size + mask at the hover point.
+/// Live Play-mode mesh painting of albedo, an HDRP-style mask map (metallic/occlusion/smoothness)
+/// and emission, with live-switchable GPU/CPU backends () that paint identically
+/// and seamlessly across UV seams: the masked square brush is evaluated in 3D local space at the hit.
///
[DisallowMultipleComponent]
public partial class MeshPainter : MonoBehaviour
@@ -727,7 +720,6 @@ public partial class MeshPainter : MonoBehaviour
// --- Brush preview ---
- /// Creates the preview material and the unit quad used by the flat-decal mode.
private void SetupPreview()
{
if (!previewShader) previewShader = Shader.Find("Hidden/MeshPaintPreview");
@@ -860,7 +852,7 @@ public partial class MeshPainter : MonoBehaviour
return;
// Don't paint through the in-game UI (a HUD click must not also stamp the mesh behind it).
- if (pressed && IsPointerOverUi())
+ if (pressed && InputUtil.PointerOverUi())
pressed = false;
// Brush preview at the hover point - independent of painting AND of the pipette, so the
@@ -946,7 +938,6 @@ public partial class MeshPainter : MonoBehaviour
_hasLastScreen = true;
}
- /// Raycasts at a screen position and stamps the masked square brush onto the mesh hit point.
private void PaintAtScreen(Vector2 screenPos)
{
if (!TryComputeBrushPlacement(screenPos, out Vector3 pLocal, out float radiusLocal,
@@ -1132,7 +1123,6 @@ public partial class MeshPainter : MonoBehaviour
// --- Channels (shared by both backends) ---
- /// True if at least one paint channel is enabled (used to gate the per-frame stroke).
private bool AnyChannelActive()
{
for (int mi = 0; mi < MAP_COUNT; mi++)
@@ -1224,9 +1214,6 @@ public partial class MeshPainter : MonoBehaviour
BrushSizeChanged?.Invoke();
}
- /// True if the pointer is currently over an interactive UI element (raycast target).
- private static bool IsPointerOverUi() => InputUtil.PointerOverUi();
-
private void OnDestroy()
{
if (_bakedMesh && !_baker) Destroy(_bakedMesh); // _baker owns its mesh; only free our own
diff --git a/Assets/Scripts/Components/PlayerScaler.cs b/Assets/Scripts/Components/PlayerScaler.cs
index aa523b0..ea20d62 100644
--- a/Assets/Scripts/Components/PlayerScaler.cs
+++ b/Assets/Scripts/Components/PlayerScaler.cs
@@ -2,17 +2,16 @@ using UnityEngine;
///
/// One knob that resizes the whole player. Setting drives transform.localScale
-/// — which Unity already propagates to the visible mesh, the CharacterController capsule, the per-pose
-/// capsules (written as local values) and the camera target — and additionally rescales the movement,
-/// jump, slide and wall tunables on proportionally, so a smaller
-/// or larger player still moves at the same body-relative pace instead of zipping or crawling. Without
-/// this the speeds (units/second, not geometry) stay put and a half-size player feels twice as fast.
+/// (mesh, CharacterController capsule, per-pose capsules and camera target follow automatically) and
+/// rescales the movement/jump/slide/wall tunables on
+/// proportionally — the speeds are units/second, not geometry, so without this a half-size player feels
+/// twice as fast.
///
-/// The authored values on the controller ARE the scale-1 design values: tune them normally there. They are
-/// captured as the base in Awake and multiplied by Scale at runtime, so the serialized design values are
-/// never overwritten on disk. In the editor only localScale is previewed live (geometry is the only
-/// visual part); the speed rescale happens on play. The skinned mesh collider scales correctly because
-/// divides the player scale back out of its bake.
+/// The authored controller values ARE the scale-1 design values: they are captured as the base in Awake
+/// and multiplied by Scale at runtime, so the serialized values are never overwritten on disk. In the
+/// editor only localScale is previewed live; the speed rescale happens on play. The skinned mesh
+/// collider scales correctly because divides the player scale back
+/// out of its bake.
///
[DisallowMultipleComponent]
[RequireComponent(typeof(PlayerCharacterController))]
diff --git a/Assets/Scripts/Components/Radar.cs b/Assets/Scripts/Components/Radar.cs
index 424081f..eb99292 100644
--- a/Assets/Scripts/Components/Radar.cs
+++ b/Assets/Scripts/Components/Radar.cs
@@ -3,15 +3,14 @@ using UnityEngine;
///
/// Sits on the "Radar" overlay object (a disc rendered by the Radar Camera in front of the main camera).
-/// Spawns "Target" blips - clones of the child "Target Template" - each of which continuously points from
-/// the reference camera toward a world-space transform (visualising the bearing camera -> target) and then
-/// destroys itself after a given lifetime. See for the per-blip behaviour.
+/// Spawns "Target" blips - clones of the child "Target Template" - each pointing from the reference
+/// camera toward a world-space transform until its lifetime elapses (see ).
///
-/// The whole radar overlay is only shown while it is useful: at least one live target, first-person is active,
-/// AND the match is in its Seek phase. Instead of snapping on/off it fades: a shared 0..1 factor ()
-/// drives the alpha of every (transparent) radar renderer - the disc here, the blip points in -
-/// and scales the disc from up to 1. Fade-in and fade-out speeds are independent. The overlay
-/// camera is switched off only once fully faded out, so hidden blips keep counting down their lifetime meanwhile.
+/// Shown only while useful: at least one live target, first-person active, AND the match in its Seek
+/// phase. Instead of snapping on/off it fades: a shared 0..1 factor () drives
+/// the alpha of every radar renderer - the disc here, the blip points in RadarTarget - and scales the
+/// disc from up to 1. The overlay camera is switched off only once fully faded
+/// out, so hidden blips keep counting down their lifetime meanwhile.
///
[DisallowMultipleComponent]
public sealed class Radar : MonoBehaviour
diff --git a/Assets/Scripts/Components/RadarTarget.cs b/Assets/Scripts/Components/RadarTarget.cs
index 0e8585a..86f0456 100644
--- a/Assets/Scripts/Components/RadarTarget.cs
+++ b/Assets/Scripts/Components/RadarTarget.cs
@@ -1,17 +1,16 @@
using UnityEngine;
///
-/// A single radar blip spawned by . Each frame it orients itself so its forward (+Z, where
-/// the "Point" marker sits) faces the direction from the reference camera to a world-space target, visualising
-/// the bearing camera -> target. Tracks continuously (the camera turns / the target moves) and self-destructs
-/// once its lifetime elapses - or earlier if its target is destroyed. The point's base colour and size encode
-/// the two things the flat radar disc can't show via position alone: tints it by
-/// how far the target sits ahead of / behind the camera, and its scale shrinks with camera -> target distance.
+/// A single radar blip spawned by . Each frame it orients its forward (+Z, where the
+/// "Point" marker sits) along the bearing reference camera -> world target, and self-destructs once its
+/// lifetime elapses - or earlier if the target is destroyed. The point encodes the two things the flat
+/// disc can't show via position alone: tints it by how far the target sits
+/// ahead of / behind the camera, and its scale shrinks with camera -> target distance.
///
-/// On top of the radar-wide fade, each blip fades in when it spawns and fades out at the end of its life (or when
-/// its world target vanishes) via its own factor, so blips appear/disappear smoothly instead
-/// of popping. Fade-in and fade-out speeds are independent; the factor persists across so a
-/// refreshed blip is pulled back to full opacity rather than flickering to zero first.
+/// On top of the radar-wide fade, each blip ramps its own in on spawn and out at end
+/// of life (or when its world target vanishes), so blips never pop. The factor persists across
+/// so a refreshed blip is pulled back to full opacity rather than flickering to
+/// zero first.
///
[DisallowMultipleComponent]
public sealed class RadarTarget : MonoBehaviour
diff --git a/Assets/Scripts/Components/SkinnedColliderBaker.cs b/Assets/Scripts/Components/SkinnedColliderBaker.cs
index d61d68d..372901e 100644
--- a/Assets/Scripts/Components/SkinnedColliderBaker.cs
+++ b/Assets/Scripts/Components/SkinnedColliderBaker.cs
@@ -3,16 +3,15 @@ using UnityEngine;
///
/// Sole owner of the animated character's collider bake: keeps a non-convex MeshCollider in sync with
-/// the SkinnedMeshRenderer's current pose every frame, independent of any paint/game mode. MeshPainter's
-/// own per-frame bake sits behind its PaintingEnabled gate, so the collider would otherwise freeze in
-/// player-control / insect mode. When this component is present MeshPainter delegates to it (it stops
-/// baking and only borrows for its GPU raster path), so the deformed-pose
-/// collider stays fresh for everyone - paint raycasts, physics, future seeker / line-of-sight queries.
+/// the SkinnedMeshRenderer's current pose every frame, independent of any paint/game mode (MeshPainter's
+/// own bake sits behind its PaintingEnabled gate, so the collider would otherwise freeze in
+/// player-control / insect mode). When present, MeshPainter delegates to it and only borrows
+/// for its GPU raster path.
///
/// Bakes in the renderer's LOCAL space (BakeMesh, scale = 1) - the same convention MeshPainter and
/// ConcealmentMeter use (world = transform.localToWorldMatrix). Runs at a negative execution order so
-/// the collider is fresh before MeshPainter (and other consumers) read it in the same frame. Owns the
-/// baked Mesh (creates + destroys it); MeshPainter never destroys a borrowed mesh.
+/// the collider is fresh before consumers read it in the same frame. Owns the baked Mesh (creates +
+/// destroys it); MeshPainter never destroys a borrowed mesh.
///
[DisallowMultipleComponent]
[DefaultExecutionOrder(-50)]
@@ -66,13 +65,12 @@ public class SkinnedColliderBaker : MonoBehaviour
_skinned.BakeMesh(_baked);
// BakeMesh freezes the bone deformation — which already carries this transform's world scale —
- // into the mesh. The MeshCollider below then applies the same lossyScale a SECOND time, so a
- // scaled player (see PlayerScaler) would get a collider half/double the size of the visible
- // mesh. Divide the scale back out so the baked mesh is true scale-1 local geometry (the contract
- // this class already documents): the MeshCollider re-applies it exactly once (matching the
- // renderer), and MeshPainter / ConcealmentMeter — which raise the baked mesh to world via
- // localToWorldMatrix — stay correct at any player scale. No-op (and untouched) at scale 1; the
- // uniform divide is rotation-independent, which the player's uniform scale guarantees.
+ // into the mesh, and the MeshCollider below applies the same lossyScale a SECOND time, so a
+ // scaled player (see PlayerScaler) would get a collider half/double the visible mesh. Divide the
+ // scale back out so the baked mesh is true scale-1 local geometry: the MeshCollider re-applies it
+ // exactly once, and MeshPainter / ConcealmentMeter (world via localToWorldMatrix) stay correct at
+ // any player scale. No-op at scale 1; the uniform divide is rotation-independent, which the
+ // player's uniform scale guarantees.
GeometryUtil.RescaleToUnitScale(_baked, transform.lossyScale, _vbuf);
_collider.sharedMesh = null; // force a physics re-cook of the new geometry
diff --git a/Assets/Scripts/Components/SkyboxEnvironmentUpdater.cs b/Assets/Scripts/Components/SkyboxEnvironmentUpdater.cs
index d743164..4e44da6 100644
--- a/Assets/Scripts/Components/SkyboxEnvironmentUpdater.cs
+++ b/Assets/Scripts/Components/SkyboxEnvironmentUpdater.cs
@@ -4,26 +4,22 @@ using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
///
-/// Builds the environment reflection from the current Skybox in memory (renders
-/// the sky into a cubemap and assigns it as the Custom reflection), so
-/// smooth/metallic materials reflect the sky WITHOUT a baked LightingData.asset.
-/// Runs in Edit and Play mode ([ExecuteAlways]); drop it on one object per scene
-/// (or a prefab that lives in every scene) and you never have to assign a foreign
-/// LightingData again — also works for runtime-loaded / procedural scenes.
+/// Builds the environment reflection from the current Skybox in memory (renders the sky into a cubemap
+/// assigned as the Custom reflection), so smooth/metallic materials reflect the sky WITHOUT a baked
+/// LightingData.asset. Runs in Edit and Play mode ([ExecuteAlways]); drop it on one object per scene —
+/// also works for runtime-loaded / procedural scenes.
///
-/// Why a rendered cubemap and not :
-/// in this project UpdateEnvironment does NOT produce a valid skybox reflection
-/// probe (a mirror stays black), so
-/// had nothing to scale. Rendering the sky into a Custom reflection cubemap gives a
-/// real reflection that intensity can actually drive.
+/// Why a rendered cubemap and not : in this project
+/// UpdateEnvironment does NOT produce a valid skybox reflection probe (a mirror stays black), so
+/// had nothing to scale. Rendering the sky into a
+/// Custom reflection cubemap gives a real reflection that intensity can actually drive.
///
-/// The skybox AMBIENT probe still comes from UpdateEnvironment — but it only convolves a
-/// non-zero probe once the sky has actually been rendered this session. Straight after a
-/// domain reload / scene load / play-start the sky hasn't rendered yet, so the probe is
-/// ZERO and the Environment intensity multiplier has nothing to scale ("doesn't take
-/// effect" until a manual re-enable). Fix: do one synchronous full-screen sky render to
-/// "warm" the sky right before UpdateEnvironment — deterministic, no waiting on renders.
-/// (RenderToCubemap does NOT warm the ambient path; a normal Camera.Render does.)
+/// The skybox AMBIENT probe still comes from UpdateEnvironment — but it only convolves a non-zero probe
+/// once the sky has actually been rendered this session. Straight after a domain reload / scene load /
+/// play-start the sky hasn't rendered yet, so the probe is ZERO and the Environment intensity multiplier
+/// has nothing to scale ("doesn't take effect" until a manual re-enable). Fix: one synchronous
+/// full-screen sky render "warms" the sky right before UpdateEnvironment — deterministic, no waiting on
+/// renders. (RenderToCubemap does NOT warm the ambient path; a normal Camera.Render does.)
///
[ExecuteAlways]
[DisallowMultipleComponent]
diff --git a/Assets/Scripts/Components/StartMatchTrigger.cs b/Assets/Scripts/Components/StartMatchTrigger.cs
index 4f0024b..57210fb 100644
--- a/Assets/Scripts/Components/StartMatchTrigger.cs
+++ b/Assets/Scripts/Components/StartMatchTrigger.cs
@@ -6,19 +6,16 @@ using UnityEngine;
/// AUTHORITY's avatar stands inside it, it asks the to start the match.
///
/// Detection POLLS a server-side physics overlap every frame instead of relying on OnTriggerEnter.
-/// Why: the authority can be a REMOTE client (a dedicated server with no host player hands authority to
-/// the first client). On the server that client's avatar is a NetworkTransform-driven replica - its
-/// CharacterController is disabled () and it carries no Rigidbody, so it
-/// is moved by direct transform writes. Unity only sends OnTriggerEnter when one of the two colliders has
-/// a Rigidbody (or is a CharacterController moving via Move()); a transform-moved non-Rigidbody collider
-/// against a static trigger fires nothing. That is exactly why a host's own (Move()-driven) avatar tripped
-/// the trigger but a remote authority never did. An explicit overlap query finds the avatar's (baked)
-/// collider regardless of HOW it is moved, so start works for a host and a dedicated server alike.
+/// Why: the authority can be a REMOTE client, whose server-side avatar is a NetworkTransform-driven
+/// replica - CharacterController disabled (), no Rigidbody, moved by
+/// direct transform writes. Unity only sends OnTriggerEnter when one of the two colliders has a
+/// Rigidbody (or is a CharacterController moving via Move()), so a host's own (Move()-driven) avatar
+/// tripped the trigger but a remote authority never did. An explicit overlap query finds the avatar's
+/// (baked) collider regardless of HOW it is moved.
///
-/// Server-gated: pure clients query nothing. Only the authority avatar starts the match; a passing
-/// non-authority avatar is ignored. Runs only while in the lobby phase and stops once it has started a
-/// match. The trigger lives in the additively-loaded Lobby and is destroyed when the lobby unloads, so a
-/// re-match (back in the lobby) gets a fresh instance with its guard reset.
+/// Server-gated: pure clients query nothing. Only the authority avatar starts the match, only during
+/// the lobby phase, once. The trigger lives in the additively-loaded Lobby and is destroyed when the
+/// lobby unloads, so a re-match gets a fresh instance with its guard reset.
///
[RequireComponent(typeof(Collider))]
[DisallowMultipleComponent]
diff --git a/Assets/Scripts/Components/UI/DraggablePanel.cs b/Assets/Scripts/Components/UI/DraggablePanel.cs
index 481be5c..9a0fc85 100644
--- a/Assets/Scripts/Components/UI/DraggablePanel.cs
+++ b/Assets/Scripts/Components/UI/DraggablePanel.cs
@@ -3,15 +3,9 @@ using UnityEngine.UI;
using UnityEngine.EventSystems;
///
-/// Makes a UI panel draggable. Attach to the panel itself: it follows the pointer from any of the panel's
-/// non-interactive surface (title bar, padding, labels, background) but ignores presses that begin on a
-/// widget - a (toggle, slider, ...) or a (the
-/// colour-picker field) - so those keep their own click/drag behaviour. It keeps a slice of the panel on
-/// screen and raises it above its siblings while interacting.
-///
-/// Works under any canvas scale (it tracks the pointer in the drag area's local space and applies the
-/// movement delta) and under any anchor/pivot (the on-screen clamp is derived from the panel's real
-/// corners in its parent, not from a fixed top-left assumption).
+/// Makes a UI panel draggable from any non-interactive surface; presses that begin on a widget
+/// ( or ) keep their own click/drag behaviour.
+/// Keeps a slice of the panel on screen and raises it above siblings; canvas-scale/anchor/pivot-agnostic.
///
[RequireComponent(typeof(RectTransform))]
public class DraggablePanel : MonoBehaviour, IPointerDownHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
@@ -40,8 +34,6 @@ public class DraggablePanel : MonoBehaviour, IPointerDownHandler, IBeginDragHand
public void OnPointerDown(PointerEventData eventData)
{
- // Raise the panel when pressing its own surface. (A press that lands on a widget is consumed by
- // that widget's own handler and never reaches us, so this rarely needs the guard - but it is cheap.)
if (!StartedOnWidget(eventData)) BringToFront();
}
diff --git a/Assets/Scripts/Components/UI/HsvColorPicker.cs b/Assets/Scripts/Components/UI/HsvColorPicker.cs
index add6353..f56a00f 100644
--- a/Assets/Scripts/Components/UI/HsvColorPicker.cs
+++ b/Assets/Scripts/Components/UI/HsvColorPicker.cs
@@ -3,17 +3,11 @@ using UnityEngine;
using UnityEngine.UI;
///
-/// Compact runtime HSV colour picker: a saturation/value square, a hue bar and a live swatch. The
-/// square is a pure-hue base with two static gradient overlays on top (white-out -> saturation,
-/// black-out -> value), so only the base tint changes per hue - no per-frame texture rebuilds. Hue is
-/// never lost while dragging the square (the square only edits S/V, the bar only edits H). Alpha is
-/// edited separately by a standalone slider in the HUD, not by this picker;
-/// still stores the incoming alpha so round-trips it.
-///
-/// The child structure (stable names) comes from the baked HUD prefab.
-/// (re)assigns the runtime gradient sprites (which a prefab cannot store) and (re)hooks the drag
-/// relays. A leftover alpha bar from a pre-split prefab is hidden by , so no
-/// re-bake is required.
+/// Runtime HSV colour picker: SV square (pure-hue base + static gradient overlays, so no per-frame
+/// texture rebuilds; edits only S/V) plus hue bar (edits only H), so hue is never lost while dragging.
+/// Alpha is edited by a separate HUD slider, but stores it so
+/// round-trips it. Child structure (stable names) comes from the baked HUD prefab;
+/// (re)assigns the runtime gradient sprites (which a prefab cannot store) and (re)hooks the drag relays.
///
public class HsvColorPicker : MonoBehaviour
{
@@ -49,8 +43,7 @@ public class HsvColorPicker : MonoBehaviour
///
/// Re-finds the child widgets, (re)assigns the runtime gradient sprites and (re)hooks the drag
- /// relays. Idempotent (unsubscribe-then-subscribe), so it is safe whether the structure was just
- /// built in code or instantiated from a prefab. Call before .
+ /// relays. Idempotent. Call before .
///
public void Rebind()
{
@@ -87,7 +80,6 @@ public class HsvColorPicker : MonoBehaviour
Refresh();
}
- // Idempotently hooks the handler to the surface's drag relay (unsubscribe-then-subscribe).
private static void Rehook(Component surface, Action handler)
{
if (surface.TryGetComponent(out UiPointerSurface pointer))
diff --git a/Assets/Scripts/Components/UI/PaintHud/PaintHud.Library.cs b/Assets/Scripts/Components/UI/PaintHud/PaintHud.Library.cs
index 183e5fd..2b3614c 100644
--- a/Assets/Scripts/Components/UI/PaintHud/PaintHud.Library.cs
+++ b/Assets/Scripts/Components/UI/PaintHud/PaintHud.Library.cs
@@ -5,14 +5,10 @@ using UnityEngine;
using UnityEngine.UI;
///
-/// Binder for the "Paintings" library panel. The panel's visual layout (Panel_Paintings, the scrollable
-/// list, an entry template, and the Save / Confirm dialogs) is authored inside the PaintHudPanel layout
-/// prefab - this code only finds those objects by their stable names and wires the behaviour, exactly like
-/// does for the brush widgets. The list is populated by cloning the authored
-/// Lib_EntryTemplate per saved slot. Save opens a name dialog (an existing name is overwritten, with a
-/// warning shown while typing); Load applies the selected slot immediately; Delete goes through a
-/// confirmation dialog. Loading replaces the current painting and, when networked, re-broadcasts the
-/// loaded state to every other peer.
+/// Binder for the "Paintings" library panel, authored in the PaintHudPanel layout prefab and wired here
+/// by stable names (like for the brush widgets). The list clones Lib_EntryTemplate
+/// per saved slot; Save overwrites by name (warned while typing), Load applies immediately (re-broadcast
+/// to peers when networked), Delete confirms.
///
public partial class PaintHud
{
diff --git a/Assets/Scripts/Components/UI/PaintHud/PaintHud.cs b/Assets/Scripts/Components/UI/PaintHud/PaintHud.cs
index 5793510..343fa93 100644
--- a/Assets/Scripts/Components/UI/PaintHud/PaintHud.cs
+++ b/Assets/Scripts/Components/UI/PaintHud/PaintHud.cs
@@ -1,20 +1,14 @@
using System;
using UnityEngine;
using UnityEngine.UI;
-using UnityEngine.EventSystems;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem.UI;
#endif
///
-/// In-game paint UI. Shows its panels only while the active is
-/// (follows the manager's ModeChanged) and binds its widgets directly to the
-/// live fields, so edits apply immediately.
-///
-/// The layout (independently draggable sub-panels per channel + a global brush card) comes from the
-/// authored prefab (). The BINDINGS live in code and are (re)applied at
-/// runtime by against stable widget names - the prefab stores only the
-/// visual layout, never behaviour.
+/// In-game paint UI. Visible only while the active is ;
+/// binds its widgets directly to the live fields. The prefab stores only the
+/// visual layout; the bindings live in code, (re)applied by against stable widget names.
///
[DisallowMultipleComponent]
public partial class PaintHud : MonoBehaviour
@@ -119,7 +113,6 @@ public partial class PaintHud : MonoBehaviour
// (in BindPainter) overwrites them. The reset button restores exactly these.
CaptureAuthoredLayout();
- // The paint library now lives inside the authored layout prefab; bind its widgets like the brush panel.
BindLibrary(_panel ? _panel.transform : null);
// Bind as soon as a painter exists. With networked spawn-on-connect there is none at scene
@@ -443,11 +436,8 @@ public partial class PaintHud : MonoBehaviour
return s;
}
- ///
- /// Attaches (idempotently) a to the whole panel so it can be dragged
- /// from any of its non-interactive surface (header, padding, labels, background); the component
- /// ignores presses that start on a widget (toggle, slider, colour picker).
- ///
+ /// Attaches (idempotently) a so the whole panel can be
+ /// dragged from any non-widget surface.
private void MakeDraggable(Transform root, string panelName)
{
var panel = GeometryUtil.FindDeep(root, panelName) as RectTransform;
diff --git a/Assets/Scripts/Components/UI/SliderCurve.cs b/Assets/Scripts/Components/UI/SliderCurve.cs
index f219ab7..80defcf 100644
--- a/Assets/Scripts/Components/UI/SliderCurve.cs
+++ b/Assets/Scripts/Components/UI/SliderCurve.cs
@@ -3,19 +3,10 @@ using UnityEngine;
using UnityEngine.UI;
///
-/// Reshapes a UI 's response with an so the value runs
-/// non-linearly between the slider's authored minValue/maxValue. The curve maps the handle
-/// position (x, 0..1 - linear in screen space) to a value fraction (y, 0..1) that is then scaled to the
-/// slider's range. The slider's own min/max stays the single source of truth for the range; this only
-/// bends the mapping inside it - so dropping the component onto an existing slider needs no range re-entry.
-///
-/// is the curved output, positions the handle to show a given
-/// output (inverting the curve), and raises the curved value whenever the
-/// handle moves - so the component is usable both standalone (wire ) and from
-/// code that drives the raw slider (read ).
-///
-/// The curve should be monotonic increasing (the default linear is); the inverse used by
-/// assumes that.
+/// Reshapes a UI 's response with an mapping handle
+/// position (x, 0..1) to a value fraction (y, 0..1) scaled to the slider's own minValue/maxValue, which
+/// stays the single source of truth for the range. The curve must be monotonic increasing: the inverse
+/// used by assumes that.
///
[RequireComponent(typeof(Slider))]
[DisallowMultipleComponent]
diff --git a/Assets/Scripts/Components/UI/UiPointerSurface.cs b/Assets/Scripts/Components/UI/UiPointerSurface.cs
index c3bb9d5..883e631 100644
--- a/Assets/Scripts/Components/UI/UiPointerSurface.cs
+++ b/Assets/Scripts/Components/UI/UiPointerSurface.cs
@@ -3,10 +3,9 @@ using UnityEngine;
using UnityEngine.EventSystems;
///
-/// Turns a pointer press/drag on a UI graphic into a normalized (x, y) position in
-/// [0,1] within its RectTransform (origin bottom-left). Added in code to the colour
-/// picker's saturation/value field and hue bar so they react to both clicking and
-/// dragging. The graphic on the same object must be a raycast target.
+/// Turns a pointer press/drag on a UI graphic into a normalized (x, y) position in [0,1] within its
+/// RectTransform (origin bottom-left). Used by the colour picker's SV field and hue bar. The graphic
+/// on the same object must be a raycast target.
///
[RequireComponent(typeof(RectTransform))]
public class UiPointerSurface : MonoBehaviour, IPointerDownHandler, IDragHandler
diff --git a/Assets/Scripts/Components/VHACDColliderBaker.cs b/Assets/Scripts/Components/VHACDColliderBaker.cs
index 2cc2691..50077f0 100644
--- a/Assets/Scripts/Components/VHACDColliderBaker.cs
+++ b/Assets/Scripts/Components/VHACDColliderBaker.cs
@@ -9,13 +9,13 @@ using UnityEditor.SceneManagement;
///
/// Editor tool that runs V-HACD (approximate convex decomposition via the native
/// plugin) on an environment mesh and bakes the resulting convex hulls into SAVED mesh assets + child
-/// MeshCollider GameObjects — the exact same output plumbing as ,
-/// only the decomposition backend differs (V-HACD's voxel solver instead of the surface-clustering
+/// MeshCollider GameObjects — the same output plumbing as , only
+/// the decomposition backend differs (V-HACD's voxel solver instead of the surface-clustering
/// heuristic). Tune the decomposition on the sibling component (its m_parameters),
/// then run "Bake VHACD Colliders".
///
/// The hulls are real child GameObjects referencing hull-mesh assets under
-/// Assets/Generated/VHACDColliders, so they survive domain reload / play-mode / scene save
+/// Assets/Generated/VHACDColliders, so they survive domain reload / play mode / scene save
/// (unlike an in-memory-only bake).
///
[DisallowMultipleComponent]
@@ -143,7 +143,7 @@ public class VHACDColliderBaker : MonoBehaviour
{
string raw;
PrefabStage stage = PrefabStageUtility.GetPrefabStage(gameObject);
- if (stage != null)
+ if (stage)
{
string ap = stage.assetPath;
if (ap.StartsWith("Assets/")) ap = ap.Substring("Assets/".Length);
diff --git a/Assets/Scripts/GameMode/FirstPersonMode.cs b/Assets/Scripts/GameMode/FirstPersonMode.cs
index 982a559..21028da 100644
--- a/Assets/Scripts/GameMode/FirstPersonMode.cs
+++ b/Assets/Scripts/GameMode/FirstPersonMode.cs
@@ -2,10 +2,9 @@ using UnityEngine;
using Unity.Netcode;
///
-/// First-person mode. The player keeps walking (WASD, camera-relative) but the camera moves to the
-/// player's eye (the CameraTarget) with free mouse-look, and the player's mesh is hidden so the body
-/// isn't seen from the inside. The orbit camera input is disabled while here so it doesn't fight for
-/// the cursor; the drives its own dedicated CinemachineCamera.
+/// First-person mode. The player keeps walking, but the camera moves to the eye (CameraTarget) with
+/// free mouse-look and the player's mesh is hidden. Orbit camera input is disabled so it doesn't fight
+/// for the cursor; the drives its own dedicated CinemachineCamera.
///
public class FirstPersonMode : GameMode
{
@@ -13,7 +12,6 @@ public class FirstPersonMode : GameMode
public override void Enter()
{
- // Player stays fully controllable; only the view changes.
SetPlayerControl(true);
SetPainting(false);
SetOrbitInputEnabled(false);
@@ -113,16 +111,16 @@ public class FirstPersonMode : GameMode
for (int i = 0; i < count; i++)
{
var netObj = _hits[i].collider.GetComponentInParent();
- if (netObj && self && netObj == self) continue; // skip own body
+ if (netObj && self && netObj == self) continue;
- end = _hits[i].point; // first non-self solid hit stops the beam here (wall or avatar)
+ end = _hits[i].point;
if (netObj && netObj.IsPlayerObject)
{
var setup = netObj.GetComponent();
if (setup && setup.Role == PlayerRole.Hider && !setup.IsFound)
match.ReportHit(netObj.OwnerClientId);
}
- break; // the first non-self hit decides the shot
+ break;
}
}
diff --git a/Assets/Scripts/GameMode/FreeCamMode.cs b/Assets/Scripts/GameMode/FreeCamMode.cs
index 8294c94..95d82e8 100644
--- a/Assets/Scripts/GameMode/FreeCamMode.cs
+++ b/Assets/Scripts/GameMode/FreeCamMode.cs
@@ -1,8 +1,7 @@
///
-/// Free-fly camera mode. The player is frozen in place, painting is off, and a dedicated
-/// freecam ( + its own CinemachineCamera) takes over: fly the
-/// camera through the map with WASD/Q/E and the mouse, colliding with world geometry like the
-/// player. The orbit camera input is disabled while here so it doesn't fight for the cursor.
+/// Free-fly camera mode. The player is frozen and painting is off while a dedicated freecam
+/// ( + its own CinemachineCamera) flies through the map, colliding
+/// with world geometry. Orbit camera input is disabled so it doesn't fight for the cursor.
///
public class FreeCamMode : GameMode
{
diff --git a/Assets/Scripts/GameMode/GameMode.cs b/Assets/Scripts/GameMode/GameMode.cs
index 08effe0..6f17679 100644
--- a/Assets/Scripts/GameMode/GameMode.cs
+++ b/Assets/Scripts/GameMode/GameMode.cs
@@ -32,7 +32,6 @@ public abstract class GameMode
}
}
- /// Toggles painting on the mesh painter.
protected void SetPainting(bool on)
{
if (Manager.Painter)
diff --git a/Assets/Scripts/GameMode/GameModeManager.cs b/Assets/Scripts/GameMode/GameModeManager.cs
index bb364ce..21ba78f 100644
--- a/Assets/Scripts/GameMode/GameModeManager.cs
+++ b/Assets/Scripts/GameMode/GameModeManager.cs
@@ -107,7 +107,7 @@ public class GameModeManager : MonoBehaviour
if (!freeCam) freeCam = FindFirstObjectByType(FindObjectsInactive.Include);
if (!firstPerson) firstPerson = FindFirstObjectByType(FindObjectsInactive.Include);
- BuildModes();
+ BuildModes(null);
}
private void Start()
@@ -125,8 +125,6 @@ public class GameModeManager : MonoBehaviour
/// Registers the modes for the current context. Without a role (no networked avatar yet,
/// or a non-networked scene) the full set is built from whichever rigs are present. With a role the
/// set is scoped to that side and ordered so index 0 is its intended start mode. Add new modes here.
- private void BuildModes() => BuildModes(null);
-
private void BuildModes(PlayerRole? role)
{
_modes.Clear();
@@ -480,7 +478,7 @@ public class GameModeManager : MonoBehaviour
for (int i = 0; i < avatars.Count; i++)
{
var avatar = avatars[i];
- if (!avatar || avatar.IsOwner) continue; // skip the local player's own avatar
+ if (!avatar || avatar.IsOwner) continue;
var col = avatar.BodyCollider;
if (col && col.enabled) Physics.IgnoreCollision(capsule, col, ignore);
}
diff --git a/Assets/Scripts/Helper/GameSettings.cs b/Assets/Scripts/Helper/GameSettings.cs
index 6dd8271..e9110b5 100644
--- a/Assets/Scripts/Helper/GameSettings.cs
+++ b/Assets/Scripts/Helper/GameSettings.cs
@@ -2,14 +2,10 @@ using System;
using System.IO;
using UnityEngine;
-/// Shared JSON-backed store for the player-facing game settings, written human-readable
-/// to /Settings.json. Replaces the former per-value PlayerPrefs storage.
-/// Loaded lazily on first access and cached in memory (reads are plain field access, so the
-/// per-frame Multiplier reads from the camera controllers never touch the disk). Each change is
-/// written immediately (settings edits are rare user actions), so a hard-killed build keeps them.
-///
-/// The typed domain facades (MouseSensitivity, MasterVolume) own the ranges/clamping and read/write
-/// through here; this class stays domain-agnostic and only persists the raw values.
+/// Shared JSON-backed store for player-facing game settings (/Settings.json).
+/// Loaded lazily and cached in memory, so per-frame reads never touch the disk; each change is
+/// written immediately (settings edits are rare), so a hard-killed build keeps them. The typed
+/// facades (MouseSensitivity, MasterVolume) own the ranges/clamping; this class only persists raw values.
public static class GameSettings
{
private const string FileName = "Settings.json";
diff --git a/Assets/Scripts/Helper/MasterVolume.cs b/Assets/Scripts/Helper/MasterVolume.cs
index e40c772..cfcc61c 100644
--- a/Assets/Scripts/Helper/MasterVolume.cs
+++ b/Assets/Scripts/Helper/MasterVolume.cs
@@ -12,7 +12,7 @@ public static class MasterVolume
/// The default is the level currently authored on the mixer (its exposed-parameter value
/// converted back to linear), so a fresh install starts at the mixer's own setting instead of
- /// an arbitrary constant. Falls back to FallbackDefault if the mixer/param is unavailable.
+ /// an arbitrary constant. Falls back to 0.5 if the mixer/param is unavailable.
public static float Default => TryReadMixerLinear(out float linear) ? linear : .5f;
// Must match the exposed parameter name authored on the AudioMixer asset.
diff --git a/Assets/Scripts/Network/Debug/NetworkBootstrap.cs b/Assets/Scripts/Network/Debug/NetworkBootstrap.cs
index a894b5f..db5a165 100644
--- a/Assets/Scripts/Network/Debug/NetworkBootstrap.cs
+++ b/Assets/Scripts/Network/Debug/NetworkBootstrap.cs
@@ -5,12 +5,9 @@ using UnityEngine.InputSystem;
#endif
///
-/// Dev-only connection harness for local multiplayer testing across the junction-mirror clones: one
-/// instance clicks Host and the others click Client (loopback 127.0.0.1 via the configured
-/// UnityTransport). Everyone spawns as the neutral Player prefab into the Lobby; roles (Hunter/Hider)
-/// are rolled and applied by when the match starts, not here. Hotkeys:
-/// H = Host, C = Client, J = Server, K = Shutdown. Purely a testing scaffold - replace with real
-/// lobby/menu UI later. Attach to the same GameObject as the NetworkManager.
+/// Dev-only connection harness for local multiplayer testing: Host/Client/Server over loopback via the
+/// configured UnityTransport (hotkeys H/C/J, K = Shutdown). Roles are rolled by
+/// at match start, not here. Testing scaffold only - replace with real lobby/menu UI later.
///
[RequireComponent(typeof(NetworkManager))]
[DisallowMultipleComponent]
@@ -41,7 +38,7 @@ public class NetworkBootstrap : MonoBehaviour
if (TypingGuard.IsTyping) return; // don't fire H/C/J/K while typing in a text field
#if ENABLE_INPUT_SYSTEM
var kb = Keyboard.current;
- if (kb == null || _net == null) return;
+ if (kb == null || !_net) return;
if (!_net.IsListening)
{
if (kb.hKey.wasPressedThisFrame) _net.StartHost();
@@ -57,7 +54,7 @@ public class NetworkBootstrap : MonoBehaviour
private void OnGUI()
{
- if (!showGui || _net == null) return;
+ if (!showGui || !_net) return;
GUILayout.BeginArea(new Rect(10, 10, 220, 160), GUI.skin.box);
if (!_net.IsListening)
diff --git a/Assets/Scripts/Network/MatchManager.cs b/Assets/Scripts/Network/MatchManager.cs
index 8210a95..9cb5b92 100644
--- a/Assets/Scripts/Network/MatchManager.cs
+++ b/Assets/Scripts/Network/MatchManager.cs
@@ -307,7 +307,7 @@ public class MatchManager : MonoBehaviour
// Disable NGO auto-spawn (decided purely by PlayerPrefab != null); keep the reference and spawn
// manually once a floored scene exists. Runtime-only mutation - no asset diff.
- if (_net != null)
+ if (_net)
{
_playerPrefab = _net.NetworkConfig.PlayerPrefab;
_net.NetworkConfig.PlayerPrefab = null;
@@ -337,7 +337,6 @@ public class MatchManager : MonoBehaviour
if (Instance == this) Instance = null;
}
- // Unhooks the per-session callbacks and named-message handlers (shared by OnDisable and OnNetStopped).
private void UnhookSessionCallbacks()
{
_net.OnClientConnectedCallback -= OnClientConnected;
@@ -455,7 +454,7 @@ public class MatchManager : MonoBehaviour
private void HookSceneManager()
{
- if (_hooked || _net == null || _net.SceneManager == null) return;
+ if (_hooked || !_net || _net.SceneManager == null) return;
_net.SceneManager.OnSceneEvent += OnSceneEvent;
_hooked = true;
}
@@ -483,7 +482,7 @@ public class MatchManager : MonoBehaviour
// the load/respawn routine covers it (the PlayerObject guard in SpawnFor prevents a double spawn).
private void OnClientConnected(ulong clientId)
{
- if (_net == null || !_net.IsServer) return;
+ if (!_net || !_net.IsServer) return;
// Dedicated-server case: the first client to connect becomes the match authority (a host already
// claimed it at server start). Told via named message so it reads Esc locally.
@@ -514,7 +513,7 @@ public class MatchManager : MonoBehaviour
// dedicated-server match stays controllable.
private void OnClientDisconnected(ulong clientId)
{
- if (_net == null || !_net.IsServer) return;
+ if (!_net || !_net.IsServer) return;
// A hider that leaves mid-match can never be found - drop it so "all hiders found" can still trigger.
_pendingHiders.Remove(clientId);
@@ -545,7 +544,7 @@ public class MatchManager : MonoBehaviour
// the server, so it records its own identity directly; a pure client reports it to the server by message.
private void OnLocalClientConnected(ulong clientId)
{
- if (_net == null || clientId != _net.LocalClientId) return;
+ if (!_net || clientId != _net.LocalClientId) return;
if (_net.IsServer) RegisterIdentity(_net.LocalClientId, PersistentPlayerId.Value);
else SendIdentityToServer(PersistentPlayerId.Value);
@@ -554,7 +553,7 @@ public class MatchManager : MonoBehaviour
// Client -> server: report this install's persistent id so the server can map it to our ephemeral clientId.
private void SendIdentityToServer(string persistentId)
{
- var cmm = _net != null ? _net.CustomMessagingManager : null;
+ var cmm = _net ? _net.CustomMessagingManager : null;
if (cmm == null || string.IsNullOrEmpty(persistentId)) return;
// Grows up to the cap if a future id source is longer than the initial buffer (persistent ids are short).
using (var writer = new FastBufferWriter(256, Allocator.Temp, 1024))
@@ -568,7 +567,7 @@ public class MatchManager : MonoBehaviour
private void OnIdentityMessage(ulong senderClientId, FastBufferReader reader)
{
reader.ReadValueSafe(out string persistentId);
- if (_net == null || !_net.IsServer) return;
+ if (!_net || !_net.IsServer) return;
RegisterIdentity(senderClientId, persistentId);
}
@@ -605,8 +604,8 @@ public class MatchManager : MonoBehaviour
/// Safe to call repeatedly (only acts once, while still in the lobby phase).
public void StartMatch()
{
- if (_net == null || !_net.IsServer) return;
- if (Phase != MatchPhase.Lobby) return; // double-start / not-in-lobby guard
+ if (!_net || !_net.IsServer) return;
+ if (Phase != MatchPhase.Lobby) return;
if (!hunterPrefab || !hiderPrefab)
{
Debug.LogError("[MatchManager] Role prefabs not assigned - cannot start match.", this);
@@ -730,7 +729,6 @@ public class MatchManager : MonoBehaviour
}
}
- // Waits up to 'seconds' of server real time, returning early the moment 'until' (if supplied) is true.
private IEnumerator WaitPhase(float seconds, System.Func until)
{
_skipPhase = false; // drop a stale debug-skip from the previous phase; only this phase may set it
@@ -760,7 +758,7 @@ public class MatchManager : MonoBehaviour
[ContextMenu("Next Sub-Phase (Debug)")]
public void DebugSkipToNextSubPhase()
{
- if (_net == null) return;
+ if (!_net) return;
if (_net.IsServer) { DebugSkipServer(); return; }
if (_isLocalAuthority) SendEmptyNamedMessage(MsgDebugSkip, NetworkManager.ServerClientId);
}
@@ -768,7 +766,7 @@ public class MatchManager : MonoBehaviour
// Server-only. Flags the running phase-wait loop to break early so RunMatchPhases moves to the next sub-phase.
private void DebugSkipServer()
{
- if (_net == null || !_net.IsServer) return;
+ if (!_net || !_net.IsServer) return;
if (_subPhase == MatchSubPhase.None) return; // nothing running to skip
_skipPhase = true;
Debug.Log($"[MatchManager] DEBUG: skipping the rest of {_subPhase}.", this);
@@ -777,7 +775,7 @@ public class MatchManager : MonoBehaviour
// Server handler: an authority client asked to skip to the next sub-phase.
private void OnDebugSkipMessage(ulong senderClientId, FastBufferReader reader)
{
- if (_net == null || !_net.IsServer) return;
+ if (!_net || !_net.IsServer) return;
if (!IsMatchAuthority(senderClientId)) return;
DebugSkipServer();
}
@@ -859,7 +857,7 @@ public class MatchManager : MonoBehaviour
// applied paint cutoff - exactly the condition the per-client reveal uses to un-hide the body.
private bool LocalClientReady(List ids)
{
- var sm = _net != null ? _net.SpawnManager : null;
+ var sm = _net ? _net.SpawnManager : null;
if (sm == null) return false;
for (int i = 0; i < ids.Count; i++)
{
@@ -873,7 +871,7 @@ public class MatchManager : MonoBehaviour
// Server -> every other client: the target ids + holding phase + generation for this barrier round.
private void BroadcastBeginLoad(MatchSubPhase activePhase)
{
- var cmm = _net != null ? _net.CustomMessagingManager : null;
+ var cmm = _net ? _net.CustomMessagingManager : null;
if (cmm == null) return;
_loadRecipients.Clear();
@@ -932,7 +930,7 @@ public class MatchManager : MonoBehaviour
// Client -> server: this machine finished loading (echoes the generation so a stale reply is ignored).
private void SendReadyToServer(int generation)
{
- var cmm = _net != null ? _net.CustomMessagingManager : null;
+ var cmm = _net ? _net.CustomMessagingManager : null;
if (cmm == null) return;
using (var writer = new FastBufferWriter(sizeof(int), Allocator.Temp))
{
@@ -945,19 +943,17 @@ public class MatchManager : MonoBehaviour
private void OnReadyMessage(ulong senderClientId, FastBufferReader reader)
{
reader.ReadValueSafe(out int generation);
- if (_net == null || !_net.IsServer) return;
+ if (!_net || !_net.IsServer) return;
if (generation != _loadGeneration) return; // stale ack from a previous barrier round
_readyClients.Add(senderClientId);
}
- // Server-only. Applies the sub-phase to the server's own mirror and broadcasts it to every other client.
private void SetPhaseServer(MatchSubPhase phase, float duration)
{
ApplyPhaseLocal(phase, duration, matchMode);
BroadcastPhase(phase, duration, matchMode, 0, true);
}
- // Sets this machine's sub-phase mirror + countdown + mode (followers poll LocalSubPhase).
// Shared by the server's SetPhaseServer and a client's OnPhaseMessage handler.
private void ApplyPhaseLocal(MatchSubPhase phase, float duration, MatchMode mode)
{
@@ -987,7 +983,7 @@ public class MatchManager : MonoBehaviour
// (which applied it directly in SetPhaseServer, so it must not double-apply via loopback).
private void BroadcastPhase(MatchSubPhase phase, float duration, MatchMode mode, ulong targetClient, bool toAll)
{
- var cmm = _net != null ? _net.CustomMessagingManager : null;
+ var cmm = _net ? _net.CustomMessagingManager : null;
if (cmm == null) return;
using (var writer = new FastBufferWriter(sizeof(byte) + sizeof(float) + sizeof(byte), Allocator.Temp))
{
@@ -1023,7 +1019,7 @@ public class MatchManager : MonoBehaviour
// locally (no loopback message, mirroring SetPhaseServer). Hiders and spectators never receive a ping.
private void BroadcastRadarPings()
{
- if (_net == null || !_net.IsServer || _pendingHiders.Count == 0) return;
+ if (!_net || !_net.IsServer || _pendingHiders.Count == 0) return;
_radarPingIds.Clear();
foreach (ulong hiderClientId in _pendingHiders)
@@ -1067,7 +1063,7 @@ public class MatchManager : MonoBehaviour
/// directly. No-op outside the Seek phase.
public void RequestManualPing()
{
- if (_net == null || _subPhase != MatchSubPhase.Seek) return;
+ if (!_net || _subPhase != MatchSubPhase.Seek) return;
if (_net.IsServer) { ServerManualPing(_net.LocalClientId); return; }
SendEmptyNamedMessage(MsgManualPing, NetworkManager.ServerClientId);
@@ -1077,7 +1073,7 @@ public class MatchManager : MonoBehaviour
// ServerManualPing validates it is a live hider during Seek.
private void OnManualPingMessage(ulong senderClientId, FastBufferReader reader)
{
- if (_net == null || !_net.IsServer) return;
+ if (!_net || !_net.IsServer) return;
ServerManualPing(senderClientId);
}
@@ -1099,7 +1095,7 @@ public class MatchManager : MonoBehaviour
private void SpawnRadarBlipsLocal()
{
var radar = Radar.Instance;
- var spawnMgr = _net != null ? _net.SpawnManager : null;
+ var spawnMgr = _net ? _net.SpawnManager : null;
if (!radar || spawnMgr == null) return;
for (int i = 0; i < _radarPingIds.Count; i++)
if (spawnMgr.SpawnedObjects.TryGetValue(_radarPingIds[i], out var no) && no)
@@ -1113,7 +1109,7 @@ public class MatchManager : MonoBehaviour
reader.ReadValueSafe(out float lifetime);
reader.ReadValueSafe(out int count);
var radar = Radar.Instance;
- var spawnMgr = _net != null ? _net.SpawnManager : null;
+ var spawnMgr = _net ? _net.SpawnManager : null;
for (int i = 0; i < count; i++)
{
reader.ReadValueSafe(out ulong netObjId);
@@ -1128,7 +1124,7 @@ public class MatchManager : MonoBehaviour
/// applies directly. No-op outside the seek phase.
public void ReportHit(ulong targetClientId)
{
- if (_net == null || _subPhase != MatchSubPhase.Seek) return;
+ if (!_net || _subPhase != MatchSubPhase.Seek) return;
if (_net.IsServer)
{
@@ -1152,7 +1148,7 @@ public class MatchManager : MonoBehaviour
private void OnReportHitMessage(ulong senderClientId, FastBufferReader reader)
{
reader.ReadValueSafe(out ulong targetClientId);
- if (_net == null || !_net.IsServer) return;
+ if (!_net || !_net.IsServer) return;
if (_subPhase != MatchSubPhase.Seek) return;
if (!_pendingHiders.Contains(targetClientId)) return; // not a live (un-found) hider
if (!IsHunter(senderClientId)) return; // only a hunter may shoot
@@ -1186,7 +1182,7 @@ public class MatchManager : MonoBehaviour
if (hitRevealDelay > 0f) yield return new WaitForSeconds(hitRevealDelay);
_revealingHiders.Remove(hiderClientId);
- if (_net == null || !_net.IsServer) yield break;
+ if (!_net || !_net.IsServer) yield break;
_pendingHiders.Remove(hiderClientId);
@@ -1280,7 +1276,6 @@ public class MatchManager : MonoBehaviour
_conversionRecords.Clear();
}
- // Server-only. Despawns every live conversion statue (match ending / back to lobby) and drops the list.
private void DespawnConversionStatues()
{
for (int i = 0; i < _statues.Count; i++)
@@ -1291,7 +1286,6 @@ public class MatchManager : MonoBehaviour
_statues.Clear();
}
- // True if the client's current avatar is a Hunter role variant (used to gate shooting server-side).
private bool IsHunter(ulong clientId)
{
if (!_net.ConnectedClients.TryGetValue(clientId, out var c) || !c.PlayerObject) return false;
@@ -1303,7 +1297,7 @@ public class MatchManager : MonoBehaviour
/// neutral lobby Player (including any spectator-held mid-match joiners). Only acts while in a match.
public void EndMatch()
{
- if (_net == null || !_net.IsServer) return;
+ if (!_net || !_net.IsServer) return;
if (Phase != MatchPhase.InMatch) return; // only endable mid-match
// Stop the sub-phase machine if the match is being ended early (authority's Back-to-Lobby) and clear
@@ -1329,8 +1323,6 @@ public class MatchManager : MonoBehaviour
Debug.Log("[MatchManager] Back in lobby.", this);
}
- // Unloads one gameplay scene (if loaded), then additively loads the next (shared by the match
- // start/end sequences).
private IEnumerator SwapScenes(string unloadSceneName, string loadSceneName)
{
var sm = _net.SceneManager;
@@ -1366,9 +1358,8 @@ public class MatchManager : MonoBehaviour
_net.SceneManager.OnSceneEvent -= OnEvt;
}
- // Despawns each connected client's current avatar (if any) and spawns the prefab chosen by
- // prefabFor at the given position. Used for both lobby->match (role variants) and match->lobby
- // (neutral Player); the latter also gives spectator-held joiners their first avatar.
+ // Used for both lobby->match (role variants) and match->lobby (neutral Player); the latter also
+ // gives spectator-held joiners their first avatar.
private void RespawnAll(System.Func prefabFor, Vector3 at)
{
var ids = new List(_net.ConnectedClientsIds);
@@ -1437,7 +1428,7 @@ public class MatchManager : MonoBehaviour
{
get
{
- if (_net == null) return false;
+ if (!_net) return false;
if (_net.IsServer) return IsMatchAuthority(_net.LocalClientId);
return _isLocalAuthority;
}
@@ -1448,7 +1439,7 @@ public class MatchManager : MonoBehaviour
/// the authority or no match is running.
public void RequestEndMatch()
{
- if (_net == null) return;
+ if (!_net) return;
if (_net.IsServer) { EndMatch(); return; } // EndMatch itself guards Phase == InMatch
if (_isLocalAuthority) RequestEndMatchFromServer();
}
@@ -1480,7 +1471,7 @@ public class MatchManager : MonoBehaviour
// so a single dummy byte is written).
private void SendEmptyNamedMessage(string messageName, ulong targetClientId)
{
- var cmm = _net != null ? _net.CustomMessagingManager : null;
+ var cmm = _net ? _net.CustomMessagingManager : null;
if (cmm == null) return;
using (var writer = new FastBufferWriter(sizeof(byte), Allocator.Temp))
{
@@ -1506,7 +1497,7 @@ public class MatchManager : MonoBehaviour
// Server handler: an authority client asked to end the match.
private void OnRequestEndMatchMessage(ulong senderClientId, FastBufferReader reader)
{
- if (_net == null || !_net.IsServer) return;
+ if (!_net || !_net.IsServer) return;
if (!IsMatchAuthority(senderClientId)) return;
EndMatch(); // guards Phase == InMatch itself
}
diff --git a/Assets/Scripts/Network/MatchStatue.cs b/Assets/Scripts/Network/MatchStatue.cs
index f5081c0..a32b5c9 100644
--- a/Assets/Scripts/Network/MatchStatue.cs
+++ b/Assets/Scripts/Network/MatchStatue.cs
@@ -5,25 +5,16 @@ using Chameleon.Components;
///
/// A frozen, painted "statue" of a hider at the exact spot / stance / painting it held the moment it was
-/// CONVERTED into a hunter (Conversion mode only). captures those on the
-/// conversion and spawns one statue per converted hider at the start of the Resolution phase, so every
-/// player sees where each hider was caught as a faithful representation (same skinned body, camo painting
-/// and fur) - independent of the now-hunter avatar, which has long since moved on.
+/// CONVERTED into a hunter (Conversion mode only); captures those on the
+/// conversion and spawns one statue per converted hider at Resolution start. Purely presentational (no
+/// controller / collision / role) and a dedicated prefab, NOT a avatar -
+/// so it never pollutes the scoreboard, per-client visibility matrix or resolution X-ray reveal.
///
-/// Purely presentational: no controller, no collision, no gameplay role. It is a dedicated prefab (NOT a
-/// avatar), so it never pollutes the scoreboard, per-client visibility
-/// matrix or resolution X-ray reveal - those all key off NetworkPlayerSetup, which the statue lacks.
-///
-/// Server-owned. Three replicated ingredients rebuild it identically on every client:
-///
-/// - Transform - the server Instantiates it at the captured pos/rot; the (server-authoritative)
-/// NetworkTransform replicates that.
-/// - Painting - rides the existing snapshot transfer; this object
-/// drives the push explicitly (server loads the captured snapshot, then broadcasts it), and PlayerPaintSync
-/// suppresses its own auto pull/publish for a statue receiver so the two never race.
-/// - Stance - a single replicated pose index the pose controller reproduces (network-driven),
-/// then freezes.
-///
+/// Server-owned; three replicated ingredients rebuild it identically on every client: transform
+/// (server-authoritative NetworkTransform), painting (rides the existing
+/// snapshot transfer, pushed explicitly by the server - PlayerPaintSync suppresses its own auto
+/// pull/publish for a statue receiver so the two never race) and stance (a replicated pose index the
+/// pose controller reproduces, then freezes).
///
[DisallowMultipleComponent]
public class MatchStatue : NetworkBehaviour
diff --git a/Assets/Scripts/Network/NetworkPlayerSetup.cs b/Assets/Scripts/Network/NetworkPlayerSetup.cs
index 2eb60ac..7d41d84 100644
--- a/Assets/Scripts/Network/NetworkPlayerSetup.cs
+++ b/Assets/Scripts/Network/NetworkPlayerSetup.cs
@@ -7,7 +7,6 @@ using Chameleon.Components;
/// Per-avatar network glue on the spawned player. The local owner binds the scene singletons
/// (GameModeManager, cameras) to itself via ; remote
/// avatars stop simulating so their transform is driven purely by the NetworkTransform replication.
-/// Position-sync milestone: remote avatars still show their mesh/idle pose, just no local control.
///
[RequireComponent(typeof(PlayerCharacterController))]
[DisallowMultipleComponent]
@@ -297,7 +296,6 @@ public class NetworkPlayerSetup : NetworkBehaviour
SetBodyRenderers(visible);
}
- // Toggles every body renderer and records the applied state for the change-guard above.
private void SetBodyRenderers(bool visible)
{
if (_bodyRenderers != null)
diff --git a/Assets/Scripts/Network/OnlineSessionManager.cs b/Assets/Scripts/Network/OnlineSessionManager.cs
index 61f2530..755ea30 100644
--- a/Assets/Scripts/Network/OnlineSessionManager.cs
+++ b/Assets/Scripts/Network/OnlineSessionManager.cs
@@ -9,25 +9,17 @@ using Unity.Services.Multiplayer;
using UnityEngine;
///
-/// Online hosting/joining over Unity Gaming Services so players can connect across the internet
-/// without port forwarding. Uses the unified Multiplayer Services "Sessions" API, which bundles
-/// Relay (NAT-free messaging via Unity's relay servers) + Lobby (the public server list) + anonymous
-/// Authentication behind single calls, and configures + starts the NGO transport itself - so we do
-/// NOT call /StartClient here; CreateSessionAsync starts
-/// the host and JoinSession* starts the client.
+/// Online hosting/joining over Unity Gaming Services (unified Multiplayer "Sessions" API: Relay + Lobby +
+/// anonymous Authentication behind single calls). The Sessions API configures and starts the NGO transport
+/// itself, so we do NOT call /StartClient here -
+/// CreateSessionAsync starts the host, JoinSession* starts the client; the usual NetworkManager callbacks
+/// then fire exactly as for loopback hosting, so MatchManager, spawning and authority are unaffected.
+/// Note the UGS "session" (the public server-list entry) is distinct from the in-game "Lobby" scene.
///
-/// Terminology: the UGS "session" is the entry in the public server list (network layer). It is a
-/// different thing from the in-game "Lobby" scene (the pre-match staging area that
-/// loads after the connection is up). This component does not touch the match flow at all - the usual
-/// NetworkManager callbacks fire exactly as they do for loopback hosting, so MatchManager, spawning and
-/// authority are unaffected.
-///
-/// This is the logic layer only: state + async host/refresh/join/disconnect calls, surfaced through the
-/// / events. The uGUI server browser that drives it lives
-/// on its own canvas (); this component carries no UI of its own.
-/// Requires the project to be linked to a UGS cloud project (Project Settings > Services) with Relay,
-/// Lobby and Authentication (Anonymous) enabled; otherwise sign-in fails with a clear status message.
-/// Attach to the same GameObject as the NetworkManager.
+/// Logic layer only, surfaced through the / events; the
+/// uGUI browser driving it is . Requires the project to be linked to a
+/// UGS cloud project (Project Settings > Services) with Relay, Lobby and Authentication (Anonymous)
+/// enabled; otherwise sign-in fails with a clear status message.
///
[RequireComponent(typeof(NetworkManager))]
[DisallowMultipleComponent]
@@ -122,7 +114,7 @@ public class OnlineSessionManager : MonoBehaviour
Changed?.Invoke();
}
- private async System.Threading.Tasks.Task EnsureSignedInAsync()
+ private async Task EnsureSignedInAsync()
{
if (UnityServices.State != ServicesInitializationState.Initialized)
{
@@ -156,7 +148,7 @@ public class OnlineSessionManager : MonoBehaviour
// Shared busy/sign-in/error skeleton around one session operation; the body carries the
// op-specific statuses and state. ownsConnection = the op starts NGO (host/join), so a timeout that
// left the transport half-up must tear it down.
- private async System.Threading.Tasks.Task RunSessionOpAsync(string failLabel, bool ownsConnection, Func body)
+ private async Task RunSessionOpAsync(string failLabel, bool ownsConnection, Func body)
{
SetBusy(true);
try
diff --git a/Assets/Scripts/Network/PlayerAnimationSync.cs b/Assets/Scripts/Network/PlayerAnimationSync.cs
index 5dbb55c..fed93d9 100644
--- a/Assets/Scripts/Network/PlayerAnimationSync.cs
+++ b/Assets/Scripts/Network/PlayerAnimationSync.cs
@@ -2,19 +2,11 @@ using Unity.Netcode;
using UnityEngine;
///
-/// Replicates the visual body state that the disabled-on-remote controller / pose system would
-/// otherwise drive: the owner publishes its selected pose index, resolved locomotion category
-/// (idle / walk / run / fall) AND the wall-lean / ground-raise / ceiling mesh offset, so a remote
-/// avatar shows the same pose, walk/run/fall animation and leans into walls / raises off the ground
-/// like the owner — not just a sliding, upright idle.
-///
-/// Body position/rotation come from the NetworkTransform; this only carries the small extra state on
-/// top. Pose index and locomotion change rarely (a pose cycle, a locomotion transition), so those
-/// NetworkVariables send a delta only when the value actually changes. The mesh offset is the visual
-/// model's child-local position () — a tiny
-/// vector the owner eases continuously; the remote eases onto the replicated value so it doesn't
-/// tick-step (the body itself is already interpolated by the NetworkTransform).
-///
+/// Replicates the visual body state the disabled-on-remote controller / pose system would otherwise
+/// drive: the owner publishes its pose index, resolved locomotion category and the wall-lean /
+/// ground-raise mesh offset (), so a remote
+/// avatar animates like the owner instead of sliding in an upright idle. Body position/rotation come
+/// from the NetworkTransform; the remote eases onto the replicated mesh offset so it doesn't tick-step.
/// Runs after the PlayerPoseController (execution order 100) so the owner reads this frame's resolved
/// state; remote avatars apply via OnValueChanged / per-frame easing, independent of frame order.
///
diff --git a/Assets/Scripts/Network/PlayerPaintSync.cs b/Assets/Scripts/Network/PlayerPaintSync.cs
index b39a1ba..81c03bf 100644
--- a/Assets/Scripts/Network/PlayerPaintSync.cs
+++ b/Assets/Scripts/Network/PlayerPaintSync.cs
@@ -607,7 +607,7 @@ public class PlayerPaintSync : NetworkBehaviour
CopyChunkInto(_snapBuffer, offset, chunk);
}
- /// Copies a received chunk into the reassembly buffer, ignoring out-of-bounds offsets.
+ // Out-of-bounds offsets are ignored rather than thrown on.
private static void CopyChunkInto(byte[] buffer, int offset, byte[] chunk)
{
if (offset >= 0 && offset + chunk.Length <= buffer.Length)
@@ -693,7 +693,6 @@ public class PlayerPaintSync : NetworkBehaviour
_uploadBuffer = null;
}
- /// All connected client ids except the given one (server-side).
private ulong[] OtherClientIds(ulong except)
{
var ids = NetworkManager.ConnectedClientsIds;
diff --git a/Assets/Scripts/Network/SpectatorSetup.cs b/Assets/Scripts/Network/SpectatorSetup.cs
index 43b6820..dd623d3 100644
--- a/Assets/Scripts/Network/SpectatorSetup.cs
+++ b/Assets/Scripts/Network/SpectatorSetup.cs
@@ -2,12 +2,10 @@ using Unity.Netcode;
using UnityEngine;
///
-/// Per-avatar glue for a mid-match SPECTATOR. The spectator object is deliberately empty - no body, no
-/// renderer, no collider, no controller - because the free-fly camera is a separate, persistent scene
-/// object (in Main) that drives its own rig off Camera.main and needs nothing from this avatar. So the
-/// spectator only has to exist as the owner's player object and flip the local mode set to free-cam-only.
-/// The owner does that here; remote peers do nothing (there is no body to show or simulate). When the
-/// host ends the match, MatchManager respawns this client as a neutral lobby Player.
+/// Per-avatar glue for a mid-match SPECTATOR. The spectator object is deliberately empty (no body,
+/// renderer, collider or controller) - the free-fly camera is a separate, persistent scene object in
+/// Main, so this only has to exist as the owner's player object and flip the local mode set to
+/// free-cam-only; remote peers do nothing. MatchManager respawns it as a lobby Player at match end.
///
[DisallowMultipleComponent]
public class SpectatorSetup : NetworkBehaviour
diff --git a/Assets/Scripts/Network/UI/MatchHud.cs b/Assets/Scripts/Network/UI/MatchHud.cs
index 9fd068f..954b34a 100644
--- a/Assets/Scripts/Network/UI/MatchHud.cs
+++ b/Assets/Scripts/Network/UI/MatchHud.cs
@@ -2,15 +2,12 @@ using TMPro;
using UnityEngine;
///
-/// On-screen match HUD binder: drives the authored MatchHudCanvas widgets from live match state -
-/// the current sub-phase + countdown (top centre) and a hunter crosshair during the seek phase. The visual
-/// layout lives in the scene; this component creates nothing at runtime, it only binds. Attach it to the
-/// MatchHudCanvas and wire the references in the Inspector.
-///
-/// Passive overlay: the authored canvas has no and every graphic
-/// is raycastTarget=false, so it never consumes pointer events, and its sortingOrder (90) sits below PaintHud's
-/// (100) so tool panels always win. There is no change event for the phase/timer, so the values are polled once
-/// per frame; the visible strings are only rebuilt when the displayed second / phase actually changes.
+/// Binds the authored MatchHudCanvas widgets to live match state (sub-phase + countdowns, role
+/// counts, hunter crosshair); creates nothing at runtime. Passive overlay: the canvas has no
+/// and every graphic is raycastTarget=false, and its
+/// sortingOrder (90) sits below PaintHud's (100) so tool panels always win. There is no change event for
+/// the phase/timer, so values are polled per frame; the visible strings are only rebuilt when the
+/// displayed second / phase actually changes.
///
[DisallowMultipleComponent]
public class MatchHud : MonoBehaviour
diff --git a/Assets/Scripts/Network/UI/OnlineSessionBrowserUI.cs b/Assets/Scripts/Network/UI/OnlineSessionBrowserUI.cs
index e9d32c1..678abf5 100644
--- a/Assets/Scripts/Network/UI/OnlineSessionBrowserUI.cs
+++ b/Assets/Scripts/Network/UI/OnlineSessionBrowserUI.cs
@@ -8,24 +8,16 @@ using UnityEngine.InputSystem;
#endif
///
-/// uGUI binding for . Drives the server-browser canvas - Host / Refresh /
-/// pick a row / Join / Disconnect plus a status line - and mirrors the manager's state through its
-/// / events.
-/// Lives on the browser canvas; the session logic stays on the NetworkManager's
-/// .
+/// uGUI binding for : Host / Refresh / row select / Join / Disconnect
+/// plus a status line, mirroring the manager through its /
+/// events; the session logic stays on the manager.
///
-/// Selection model: Refresh fills the list with clones of (each a clickable row
-/// showing name + player count). Clicking a row selects it; Join connects to the selected session. While
-/// connected, only Disconnect is interactable.
-///
-/// Overlay behaviour: the panel is always shown in Main (not connected). Once a connection is up it hides;
-/// in the lobby/map Esc toggles it back as a modal pause overlay (cursor freed, local gameplay suspended via
-/// ). The Disconnect button is context-sensitive: in Main (not
-/// connected) it is an application "Exit" (quits the build, or stops play mode in the editor) so the player
-/// can leave without Alt+F4; on the map it becomes "Back to Lobby" for the match authority (ends the match);
-/// everyone else (in the lobby, or a non-authority client on the map) gets a plain disconnect. Only the
-/// visual is toggled - this binder stays alive on its (always-active) canvas so it
-/// keeps reading Esc while the panel is hidden.
+/// The panel is always shown in Main (not connected); once a connection is up it hides, and in the
+/// lobby/map Esc toggles it back as a modal pause overlay (cursor freed, gameplay suspended via
+/// ). The Disconnect button is context-sensitive: in Main it is an
+/// application "Exit"; on the map it becomes "Back to Lobby" for the match authority (ends the match);
+/// everyone else gets a plain disconnect. Only the visual is toggled - this binder
+/// stays alive on its (always-active) canvas so it keeps reading Esc while the panel is hidden.
///
public class OnlineSessionBrowserUI : MonoBehaviour
{
diff --git a/Assets/Scripts/ScriptableObject/PlayerPose.cs b/Assets/Scripts/ScriptableObject/PlayerPose.cs
index 3eca126..22e52fe 100644
--- a/Assets/Scripts/ScriptableObject/PlayerPose.cs
+++ b/Assets/Scripts/ScriptableObject/PlayerPose.cs
@@ -1,14 +1,9 @@
using UnityEngine;
///
-/// A selectable player pose: an idle held while the player stands still,
-/// plus optional walk and run clips that the pose system plays itself (the Animator stays empty) while
-/// moving on the ground, its own CharacterController capsule dimensions (so a low pose like a ball can
-/// crawl under furniture the default standing capsule is too tall for), and a camera-target y-offset that
-/// frames the pose without moving the body.
-///
-/// Named PlayerPose (not Pose) to avoid shadowing the built-in UnityEngine.Pose struct, mirroring
-/// why the controller is PlayerCharacterController and not just "PlayerController".
+/// A selectable player pose: idle/walk/run/fall clips (played by the pose system itself; the Animator
+/// stays empty), per-pose CharacterController capsule dimensions, and camera-target y-offsets.
+/// Named PlayerPose (not Pose) to avoid shadowing the built-in UnityEngine.Pose struct.
///
[CreateAssetMenu(fileName = "NewPose", menuName = "Chameleon/Pose")]
public class PlayerPose : ScriptableObject