Hunter shot: replicated laser beam + X-ray hit flash; snapshot apply in-place reseed
- Laser.shader + HunterLaserBeam: additive HDR beam, fade in/out over opacity and thickness, code-driven LineRenderer, tunable emission multiplier. - FirstPersonMode: TryShoot computes world endpoint, fires beam with lateral muzzle offset (right) so it isn't dead-center from hunter view. - NetworkPlayerSetup: shot relay RPCs (owner->server->all) + hit-flash RPC (server->all), flash OR-branch in ComputeXRay. - MatchManager: hit reveal broadcasts X-ray flash first, delays hider removal by hitRevealDelay so the glow is visible before the body vanishes. - MeshPainter reseed: in-place fast path avoids full GPU backend teardown on snapshot apply (no ~70MB RT churn), fixes spawn/paint-load hitch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
5b95cb7f56
commit
9290ef650b
10 files changed
+403
-23
No files matched your search
@@ -115,6 +115,12 @@ public class MatchManager : MonoBehaviour
|
||||
[Min(0f)]
|
||||
[SerializeField] private float shootCooldown = 0.4f;
|
||||
|
||||
[Tooltip("Seconds a shot hider stays lit with the X-ray glow before it is actually removed from play " +
|
||||
"(Classic: hidden as found; Conversion: respawned as a hunter). Gives the just-shot flash time to " +
|
||||
"be seen. The hider stays a live target during this window; 0 = remove instantly (no visible flash).")]
|
||||
[Min(0f)]
|
||||
[SerializeField] private float hitRevealDelay = 0.35f;
|
||||
|
||||
[Header("Concealment reveal")]
|
||||
[Tooltip("A hider whose ConcealmentMeter EmbeddedPercent reaches this value (while a match is running) is " +
|
||||
"revealed through walls with the material's X-ray glow, so nobody can hide by burying itself deep " +
|
||||
@@ -192,6 +198,9 @@ public class MatchManager : MonoBehaviour
|
||||
// empties. _phaseRoutine is the running Hide->Seek->Resolution coroutine (stopped if the match is ended
|
||||
// early via the authority's Back-to-Lobby).
|
||||
private readonly HashSet<ulong> _pendingHiders = new HashSet<ulong>();
|
||||
// Server-only. Hiders that were hit and are in their X-ray flash window, not yet removed. Kept out of the
|
||||
// seek-end check by staying in _pendingHiders; this set only dedupes a second shot during the flash.
|
||||
private readonly HashSet<ulong> _revealingHiders = new HashSet<ulong>();
|
||||
// Server-only. Live NGO clientId -> persistent per-install id, reported by each client on connect. The
|
||||
// clientId is ephemeral (reused after a disconnect); the value is stable across that client's restarts.
|
||||
private readonly Dictionary<ulong, string> _persistentIds = new Dictionary<ulong, string>();
|
||||
@@ -398,6 +407,7 @@ public class MatchManager : MonoBehaviour
|
||||
|
||||
// A hider that leaves mid-match can never be found - drop it so "all hiders found" can still trigger.
|
||||
_pendingHiders.Remove(clientId);
|
||||
_revealingHiders.Remove(clientId); // and out of the in-flight flash set (its resolve coroutine no-ops)
|
||||
// Free the ephemeral clientId -> identity mapping (the clientId will be reused by a later joiner).
|
||||
_persistentIds.Remove(clientId);
|
||||
|
||||
@@ -517,6 +527,7 @@ public class MatchManager : MonoBehaviour
|
||||
RespawnAll(id => hunterIds.Contains(id) ? hunterPrefab : hiderPrefab, matchSpawnCenter);
|
||||
|
||||
_pendingHiders.Clear();
|
||||
_revealingHiders.Clear();
|
||||
foreach (ulong id in _net.ConnectedClientsIds)
|
||||
if (!hunterIds.Contains(id)) _pendingHiders.Add(id);
|
||||
|
||||
@@ -656,13 +667,40 @@ public class MatchManager : MonoBehaviour
|
||||
MarkHiderFound(targetClientId);
|
||||
}
|
||||
|
||||
// Server-only. Resolves a hit on a hider for the active mode, and drops it from the pending set; the seek
|
||||
// loop ends the phase early once that empties (no hiders left to find/convert). Classic flags the avatar
|
||||
// found (replicated by its NetworkPlayerSetup -> free-cam); Conversion respawns it as a Hunter.
|
||||
// Server-only. Resolves a hit on a hider: flashes its X-ray glow on every client, then (after hitRevealDelay
|
||||
// so the flash is actually seen) drops it from the pending set and applies the mode outcome - Classic flags
|
||||
// the avatar found (replicated by its NetworkPlayerSetup -> free-cam); Conversion respawns it as a Hunter.
|
||||
// The hider stays in _pendingHiders during the flash so seek doesn't end early on the last hit; the seek
|
||||
// loop ends the phase once _pendingHiders empties (no hiders left to find/convert).
|
||||
private void MarkHiderFound(ulong hiderClientId)
|
||||
{
|
||||
_pendingHiders.Remove(hiderClientId);
|
||||
if (_revealingHiders.Contains(hiderClientId)) return; // already flashing / being resolved
|
||||
|
||||
if (_net.ConnectedClients.TryGetValue(hiderClientId, out var c) && c.PlayerObject)
|
||||
{
|
||||
var setup = c.PlayerObject.GetComponent<NetworkPlayerSetup>();
|
||||
if (setup) setup.FlashHitReveal(hitRevealDelay); // light it up on every client, now
|
||||
}
|
||||
|
||||
_revealingHiders.Add(hiderClientId);
|
||||
StartCoroutine(ResolveHitAfterReveal(hiderClientId));
|
||||
}
|
||||
|
||||
// Server-only. Waits out the reveal flash, then removes the hider and applies the mode outcome. Split from
|
||||
// MarkHiderFound so the glow is visible before the avatar leaves play (Classic hide / Conversion despawn).
|
||||
private IEnumerator ResolveHitAfterReveal(ulong hiderClientId)
|
||||
{
|
||||
if (hitRevealDelay > 0f) yield return new WaitForSeconds(hitRevealDelay);
|
||||
|
||||
_revealingHiders.Remove(hiderClientId);
|
||||
if (_net == null || !_net.IsServer) yield break;
|
||||
|
||||
_pendingHiders.Remove(hiderClientId);
|
||||
|
||||
// A Back-to-Lobby (EndMatch) during the flash tears the match down and respawns everyone; don't then
|
||||
// convert/found a stale avatar.
|
||||
if (Phase == MatchPhase.InMatch &&
|
||||
_net.ConnectedClients.TryGetValue(hiderClientId, out var c) && c.PlayerObject)
|
||||
{
|
||||
if (matchMode == MatchMode.Conversion)
|
||||
ConvertToHunter(hiderClientId, c.PlayerObject);
|
||||
@@ -672,6 +710,7 @@ public class MatchManager : MonoBehaviour
|
||||
if (setup) setup.ServerMarkFound();
|
||||
}
|
||||
}
|
||||
|
||||
string verb = matchMode == MatchMode.Conversion ? "converted to hunter" : "found";
|
||||
Debug.Log($"[MatchManager] Hider {hiderClientId} {verb} - {_pendingHiders.Count} hider(s) left.", this);
|
||||
}
|
||||
|
||||
@@ -115,6 +115,8 @@ public class NetworkPlayerSetup : NetworkBehaviour
|
||||
private bool _lastXRay;
|
||||
private bool _frozenApplied;
|
||||
private bool _lastFrozen;
|
||||
private float _xrayFlashUntil; // Time.time deadline for the just-shot X-ray flash (local per client)
|
||||
private HunterLaserBeam _laser; // shot beam visual, lazily attached on the hunter avatar
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
@@ -361,6 +363,10 @@ public class NetworkPlayerSetup : NetworkBehaviour
|
||||
// The X-ray reveal predicate for this avatar on the local machine.
|
||||
private bool ComputeXRay()
|
||||
{
|
||||
// Just-shot flash: a hunter's hit lights this hider up for a brief window (server-broadcast), on top
|
||||
// of the normal reveal triggers, so the moment of the hit reads before the hider leaves play.
|
||||
if (Time.time < _xrayFlashUntil) return true;
|
||||
|
||||
// Buried-hider reveal: already gated to hider + running match on the owner before it is published.
|
||||
if (_embeddedReveal.Value) return true;
|
||||
|
||||
@@ -369,6 +375,52 @@ public class NetworkPlayerSetup : NetworkBehaviour
|
||||
return ComputeResolutionFreeze();
|
||||
}
|
||||
|
||||
// --- Hunter shot beam ------------------------------------------------------------------------------
|
||||
// Cosmetic and client-trusted (a co-op hide & seek, matching the ReportHit trust model). The local
|
||||
// shooter draws its own beam instantly for responsiveness and relays the world-space segment to the
|
||||
// server, which echoes it to every other client. The RPCs live on the hunter avatar (this
|
||||
// NetworkObject), so they only run on this shooter's instances - other avatars never draw a beam.
|
||||
|
||||
/// <summary>Owner-only. Draws the shot beam locally now and replicates the world-space segment so every
|
||||
/// other client sees the same shot.</summary>
|
||||
public void FireShot(Vector3 start, Vector3 end)
|
||||
{
|
||||
if (!IsOwner) return;
|
||||
EnsureLaser().Fire(start, end);
|
||||
FireShotServerRpc(start, end);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void FireShotServerRpc(Vector3 start, Vector3 end) => FireShotClientRpc(start, end);
|
||||
|
||||
[ClientRpc]
|
||||
private void FireShotClientRpc(Vector3 start, Vector3 end)
|
||||
{
|
||||
if (IsOwner) return; // the shooter already drew it locally
|
||||
EnsureLaser().Fire(start, end);
|
||||
}
|
||||
|
||||
private HunterLaserBeam EnsureLaser()
|
||||
{
|
||||
if (!_laser) _laser = GetComponentInChildren<HunterLaserBeam>(true);
|
||||
if (!_laser) _laser = gameObject.AddComponent<HunterLaserBeam>();
|
||||
return _laser;
|
||||
}
|
||||
|
||||
// --- Hunter hit flash ------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>Server-only. Flashes THIS hider's through-walls X-ray glow on every client for
|
||||
/// <paramref name="seconds"/> (the just-shot feedback), overriding the normal reveal predicate for that
|
||||
/// window. MatchManager delays the actual removal by the same span so the glow is seen before it leaves play.</summary>
|
||||
public void FlashHitReveal(float seconds)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
FlashHitRevealClientRpc(Mathf.Max(0f, seconds));
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
private void FlashHitRevealClientRpc(float seconds) => _xrayFlashUntil = Time.time + seconds;
|
||||
|
||||
// True if THIS machine's local player is a Hunter role variant (so it must not see hiders during hide).
|
||||
// Shared with MatchHud. Called per frame per avatar during hide/seek, so the GetComponent result is
|
||||
// cached per local PlayerObject; a respawn/conversion swaps the object and refreshes the cache.
|
||||
|
||||
Reference in new issue
Block a user