Freeze players during map load: kill gravity and hide own mesh
- PlayerCharacterController: GravityEnabled toggle. When off, hold vertical velocity at zero and skip steep-slope sliding so a frozen avatar neither falls nor drifts; true is byte-for-byte the old path. - GameModeManager: during Loading, additionally disable gravity and hide the local body (SkinnedMesh + fur shell), re-asserted each frame to catch the mid-load lobby->match respawn. Restored (body shown, gravity on) before ActiveMode.Enter() on release to Hide. - MatchManager: enter Loading *before* SwapScenes so still-bound lobby avatars are frozen before their floor unloads. Fixes the player falling into the void during the lobby->map scene swap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
fba4780c0e
commit
a2c8a2c75d
3 files changed
+74
-9
No files matched your search
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Cinemachine;
|
||||
using Chameleon.Components;
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
@@ -329,14 +330,24 @@ public class GameModeManager : MonoBehaviour
|
||||
// without a camera jump (exactly like SetPaused's resume - the mode was never Exited).
|
||||
if (MovementGated())
|
||||
{
|
||||
if (player) player.ControlEnabled = false;
|
||||
if (player)
|
||||
{
|
||||
player.ControlEnabled = false;
|
||||
player.GravityEnabled = false; // hover in place; don't settle to the floor while loading
|
||||
}
|
||||
if (painter) painter.PaintingEnabled = false;
|
||||
// Hide the own body so the frozen avatar doesn't hang in the half-loaded world. Re-asserted every
|
||||
// frame (not once) because a lobby->match respawn rebinds `player` mid-load, so the fresh avatar
|
||||
// must be hidden too.
|
||||
SetOwnerBodyHidden(true);
|
||||
_loadFrozen = true;
|
||||
return;
|
||||
}
|
||||
if (_loadFrozen)
|
||||
{
|
||||
_loadFrozen = false;
|
||||
if (player) player.GravityEnabled = true; // Enter() doesn't touch gravity, so restore it here
|
||||
SetOwnerBodyHidden(false); // show the body, then let Enter() re-hide it if first-person
|
||||
ActiveMode?.Enter();
|
||||
}
|
||||
|
||||
@@ -382,6 +393,35 @@ public class GameModeManager : MonoBehaviour
|
||||
match.LocalSubPhase == MatchManager.MatchSubPhase.Loading;
|
||||
}
|
||||
|
||||
// Cached body/fur renderers of the currently bound local avatar, re-resolved only when `player` changes
|
||||
// (a lobby->match respawn rebinds mid-load), so the per-frame Loading hide doesn't walk the hierarchy each tick.
|
||||
private PlayerCharacterController _bodyHideOwner;
|
||||
private SkinnedMeshRenderer[] _bodyHideRenderers;
|
||||
private FurShellRenderer[] _bodyHideFur;
|
||||
|
||||
/// <summary>Hides/shows the LOCAL player's own body (skinned mesh + fur) during the pre-match loading freeze,
|
||||
/// so the owner doesn't watch its frozen avatar hang in the half-loaded world. Local-only (renderer.enabled
|
||||
/// is not replicated, so remotes are unaffected - their view is driven by <see cref="NetworkPlayerSetup"/>).
|
||||
/// On show the freeze release re-enters the active mode, so first-person re-applies its own-body self-hide.</summary>
|
||||
private void SetOwnerBodyHidden(bool hidden)
|
||||
{
|
||||
if (player != _bodyHideOwner)
|
||||
{
|
||||
_bodyHideOwner = player;
|
||||
_bodyHideRenderers = player ? player.GetComponentsInChildren<SkinnedMeshRenderer>(true) : null;
|
||||
_bodyHideFur = player ? player.GetComponentsInChildren<FurShellRenderer>(true) : null;
|
||||
}
|
||||
|
||||
if (_bodyHideRenderers != null)
|
||||
foreach (var r in _bodyHideRenderers)
|
||||
if (r) r.enabled = !hidden;
|
||||
// Fur draws via a GPU-instanced camera hook, not a Renderer, so the skinned-mesh toggle misses it;
|
||||
// disabling the component unsubscribes that hook (mirrors FirstPersonController.SetMeshHidden).
|
||||
if (_bodyHideFur != null)
|
||||
foreach (var f in _bodyHideFur)
|
||||
if (f) f.enabled = !hidden;
|
||||
}
|
||||
|
||||
/// <summary>First non-Paint mode index at or after <paramref name="start"/> (wrapping once through the
|
||||
/// cycle); -1 if the set has no non-paint mode (a degenerate paint-only configuration).</summary>
|
||||
private int FirstNonPaintFrom(int start)
|
||||
|
||||
Reference in new issue
Block a user