Freeze hiders (pose + transform) during the resolution phase
While a match is in the resolution sub-phase, hiders that have not been found hold their current pose and position: the pose graph is frozen (mixer speed 0, current frame re-asserted) and owner input is disabled while gravity/grounding keep them planted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
96528524a6
commit
ff034870fc
2 files changed
+59
No files matched your search
@@ -115,6 +115,8 @@ public class NetworkPlayerSetup : NetworkBehaviour
|
||||
private ConcealmentMeter _concealment; // owner-only embedded-percent source (disabled on remotes)
|
||||
private bool _xrayApplied;
|
||||
private bool _lastXRay;
|
||||
private bool _frozenApplied;
|
||||
private bool _lastFrozen;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
@@ -238,6 +240,40 @@ public class NetworkPlayerSetup : NetworkBehaviour
|
||||
UpdateVisibility();
|
||||
UpdateEmbeddedReveal();
|
||||
UpdateXRayReveal();
|
||||
UpdateResolutionFreeze();
|
||||
}
|
||||
|
||||
// Per-client. During the resolution phase every still-in-play hider is frozen in place on every machine:
|
||||
// its pose graph holds the current frame (PlayerPoseController.Frozen) and, on the owning client, its
|
||||
// movement input is cut (ControlEnabled) so the owner-authoritative transform stops and replicates as
|
||||
// frozen. Mirrors the resolution X-ray reveal predicate. The pose flag is change-guarded (toggled only on
|
||||
// a transition); the owner's ControlEnabled is re-asserted every frozen frame so a stray mode toggle can't
|
||||
// quietly re-enable movement during the reveal. Nothing is restored — EndMatch despawns this avatar.
|
||||
private void UpdateResolutionFreeze()
|
||||
{
|
||||
bool freeze = ComputeResolutionFreeze();
|
||||
|
||||
if (freeze && IsOwner)
|
||||
{
|
||||
var controller = GetComponent<PlayerCharacterController>();
|
||||
if (controller) controller.ControlEnabled = false;
|
||||
}
|
||||
|
||||
if (_frozenApplied && freeze == _lastFrozen) return;
|
||||
_lastFrozen = freeze;
|
||||
_frozenApplied = true;
|
||||
|
||||
var pose = GetComponent<PlayerPoseController>();
|
||||
if (pose) pose.Frozen = freeze;
|
||||
}
|
||||
|
||||
// True while THIS avatar is a still-in-play hider during the local resolution phase (the reveal window).
|
||||
private bool ComputeResolutionFreeze()
|
||||
{
|
||||
var match = MatchManager.Instance;
|
||||
return match && match.MatchActive &&
|
||||
match.LocalSubPhase == MatchManager.MatchSubPhase.Resolution &&
|
||||
role == PlayerRole.Hider && !_isFound.Value;
|
||||
}
|
||||
|
||||
// Per-client visibility: each machine decides whether to render THIS avatar's body from the LOCAL
|
||||
|
||||
@@ -156,6 +156,12 @@ public class PlayerPoseController : MonoBehaviour
|
||||
/// <summary>Eased per-pose y-offset for the first-person eye, applied on top of the camera target by the FirstPersonController.</summary>
|
||||
public float FirstPersonCameraYOffset => _firstPersonCameraOffset;
|
||||
|
||||
/// <summary>Holds the avatar on its current pose frame: the Playable graph stops advancing (its bones stay
|
||||
/// exactly where they are) and all per-frame locomotion / rest-reset / capsule / camera work is skipped.
|
||||
/// Set per-client for every still-in-play hider during the resolution reveal so nobody can wiggle in place.
|
||||
/// Clearing it resumes normal playback. Independent of network drive — works on the owner and on remotes.</summary>
|
||||
public bool Frozen { get; set; }
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_player = GetComponent<PlayerCharacterController>();
|
||||
@@ -169,6 +175,23 @@ public class PlayerPoseController : MonoBehaviour
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Resolution freeze: hold the currently-evaluated pose. Keep the graph playing (so it re-asserts the
|
||||
// frozen frame every render, even under Animator culling) but zero the mixer's speed so clip time
|
||||
// stops advancing, and skip everything below so no locomotion/rest-reset moves a bone. The per-clip
|
||||
// speeds PlayPose writes onto the mixer INPUTS are unaffected; only the mixer's own multiplier is
|
||||
// zeroed here and restored on the first un-frozen frame just below.
|
||||
if (Frozen)
|
||||
{
|
||||
if (_graph.IsValid())
|
||||
{
|
||||
if (!_graph.IsPlaying()) _graph.Play();
|
||||
if (_mixer.IsValid()) PlayableExtensions.SetSpeed(_mixer, 0d);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (_mixer.IsValid() && PlayableExtensions.GetSpeed(_mixer) == 0d)
|
||||
PlayableExtensions.SetSpeed(_mixer, 1d);
|
||||
|
||||
// Local control: the cycle key and the sprint reset choose the pose. A network-driven (remote)
|
||||
// avatar skips this — its pose index is pushed in from the owner via SetNetworkState.
|
||||
if (!_networkControlled)
|
||||
|
||||
Reference in new issue
Block a user