A static utility that implements a photo mode which hides all non-map game content for screenshots. It sweeps the active scene each frame (when enabled), disables renderers, particle components and world panels not under the map root, and fades screen panels, then restores their state when disabled. It also exposes a console command nz_photo to toggle it.
using Sandbox;
using System.Collections.Generic;
using System.Linq;
namespace NZombies;
/// <summary>
/// PHOTO MODE — the map and nothing else, for screenshots: `nz_photo`. Asked for as *"a command that sets all hud and placed stuff
/// invisible so i only see the map and can take some screenshots"* (2026-09-28), to replace a map's images — its lobby background and
/// its thumbnail, named in `Assets/maps/manifest.json`.
///
/// ⛔ "THE MAP" IS WHAT THE MAP INSTANCE LOADED: every object under `NZMap.Instance`. Everything else that draws is the game's — the
/// placed buyables and barriers, the box, the marker objects, players and the weapon in hand, zombies, pickups, effects — and every
/// screen and world panel is too. Each is switched off, remembered, and put back exactly as it was.
///
/// ⚠️ SWEPT EVERY FRAME WHILE ON (`LobbyMenu.OnUpdate`), so a zombie that spawns or a particle that fires afterwards is hidden too.
/// ⚠️ LIGHTS, FOG AND THE SKY ARE LEFT ALONE: they are how the map looks, not something standing in it.
/// ⚠️ THE EDITOR'S OVERLAYS stop through `NZGame.ShowAuthoringVisuals`, and the pending corners with them.
/// ⚠️ BEST FROM SPECTATE OR CREATIVE — a body to fly, and no zombies clawing at you while you frame a shot.
/// </summary>
public static class MapPhotoMode
{
/// <summary>Is the map on its own?</summary>
public static bool On { get; private set; }
/// <summary>What photo mode switched off, to switch back on — only what was on when it was found.</summary>
static List<Component> Hidden => _hidden ??= new();
static List<Component> _hidden;
/// <summary>Each screen panel faded, with the opacity it had.</summary>
static List<(ScreenPanel Panel, float Opacity)> Faded => _faded ??= new();
static List<(ScreenPanel Panel, float Opacity)> _faded;
/// <summary>Every frame (`LobbyMenu.OnUpdate`): while on, anything new that draws is hidden as well.</summary>
public static void Tick()
{
if ( On ) Sweep();
}
/// <summary>On — the map alone — or off, everything back.</summary>
public static void Set( bool on )
{
if ( on == On ) return;
On = on;
if ( on )
{
Sweep();
return;
}
var back = 0;
foreach ( var c in Hidden )
{
if ( !c.IsValid() ) continue;
c.Enabled = true;
back++;
}
foreach ( var (panel, opacity) in Faded )
if ( panel.IsValid() ) panel.Opacity = opacity;
Log.Info( $"[nz-photo] off — {back} thing(s) and {Faded.Count} screen panel(s) back" );
Hidden.Clear();
Faded.Clear();
}
static void Sweep()
{
var scene = Game.ActiveScene;
if ( !scene.IsValid() ) return;
var map = NZMap.Instance?.GameObject;
foreach ( var r in scene.GetAllComponents<Renderer>().ToList() ) Hide( r, map );
foreach ( var p in scene.GetAllComponents<ParticleEffect>().ToList() ) Hide( p, map );
foreach ( var p in scene.GetAllComponents<ParticleSpriteRenderer>().ToList() ) Hide( p, map );
foreach ( var p in scene.GetAllComponents<ParticleTrailRenderer>().ToList() ) Hide( p, map );
foreach ( var w in scene.GetAllComponents<WorldPanel>().ToList() ) Hide( w, null );
foreach ( var s in scene.GetAllComponents<ScreenPanel>().ToList() )
{
if ( !s.IsValid() || s.Opacity <= 0f ) continue;
Faded.Add( (s, s.Opacity) );
s.Opacity = 0f;
}
}
/// <summary>Off, and remembered — unless it is part of the map, or already off.</summary>
static void Hide( Component c, GameObject map )
{
if ( !c.IsValid() || !c.Enabled ) return;
if ( map.IsValid() && IsUnder( c.GameObject, map ) ) return;
c.Enabled = false;
Hidden.Add( c );
}
static bool IsUnder( GameObject go, GameObject root )
{
for ( var g = go; g is not null; g = g.Parent )
if ( g == root ) return true;
return false;
}
/// <summary>
/// `nz_photo [0|1]` — the map alone, for screenshots: the HUD and every panel faded out, everything the game placed switched off,
/// the editor's markers stopped. Bare, it toggles. `nz_photo 0` puts everything back as it was.
/// </summary>
[ConCmd( "nz_photo" )]
public static void Cmd( int on = -1 )
{
Set( on < 0 ? !On : on != 0 );
if ( On )
Log.Info( $"[nz-photo] ON — the map alone: {Hidden.Count} thing(s) switched off, {Faded.Count} screen panel(s) faded"
+ $"{(NZMap.Instance.IsValid() ? "" : " · ⚠ no map instance found, so NOTHING counts as the map")}"
+ " · nz_photo 0 brings it all back" );
}
}