using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace Ashwild.EditorTools
{
///
/// Editor-only capture helper: an on/off toggle under Tools ▸ Ashwild that, when enabled, forces the
/// Game view into a borderless fullscreen window as soon as Play Mode starts — so the running game
/// looks exactly like a shipped build (no editor chrome, no toolbar) and is clean to record in OBS.
/// The preference is checkable in the menu and persists per-machine in EditorPrefs; entering fullscreen
/// spawns a dedicated popup Game view on the main display and it is torn down the moment play stops.
///
[InitializeOnLoad]
public static class FullscreenOnPlay
{
#region Constants
///
/// The checkable menu entry that flips the feature on and off.
///
private const string MenuPath = "Tools/Fullscreen On Play (OBS)";
///
/// EditorPrefs key holding the toggle state. Per-user, per-machine — a personal recording setting,
/// deliberately not shared through the project.
///
private const string PrefKey = "Ashwild.FullscreenOnPlay.Enabled";
#endregion
#region State
///
/// The live borderless Game view spawned for fullscreen playback, or null when not in fullscreen.
/// Held so it can be closed again the instant Play Mode ends.
///
private static EditorWindow fullscreenView;
#endregion
#region Initialization
///
/// Hooks the Play Mode state stream once, on editor load and every domain reload, so the fullscreen
/// window follows play/stop regardless of how the user triggered it.
///
static FullscreenOnPlay()
{
EditorApplication.playModeStateChanged -= HandlePlayModeStateChanged;
EditorApplication.playModeStateChanged += HandlePlayModeStateChanged;
}
#endregion
#region Menu
///
/// Flips the toggle and, as a convenience, applies it immediately when already in Play Mode
/// (goes fullscreen right away, or drops back to the windowed Game view).
///
[MenuItem(MenuPath, false, 200)]
private static void Toggle()
{
bool enabled = !IsEnabled;
EditorPrefs.SetBool(PrefKey, enabled);
Menu.SetChecked(MenuPath, enabled);
if (!EditorApplication.isPlaying) return;
if (enabled) EnterFullscreen();
else ExitFullscreen();
}
///
/// Keeps the menu checkmark in sync with the stored preference every time the menu is opened.
///
[MenuItem(MenuPath, true)]
private static bool ToggleValidate()
{
Menu.SetChecked(MenuPath, IsEnabled);
return true;
}
#endregion
#region Play Mode Hook
///
/// Enters fullscreen when play begins (only if the toggle is on) and always tears it down as play
/// ends — so a leftover popup can never survive back into edit mode.
///
private static void HandlePlayModeStateChanged(PlayModeStateChange state)
{
switch (state)
{
case PlayModeStateChange.EnteredPlayMode:
if (IsEnabled) EnterFullscreen();
break;
case PlayModeStateChange.ExitingPlayMode:
ExitFullscreen();
break;
}
}
#endregion
#region Fullscreen Control
///
/// Spawns a dedicated Game view, strips its toolbar and shows it as a borderless popup sized to the
/// main display — the closest the editor gets to a real build's fullscreen. The position is
/// re-asserted next editor tick because ShowPopup can clamp the initial rect. Reuses nothing from
/// the docked layout, so the user's window arrangement is left untouched.
///
private static void EnterFullscreen()
{
if (fullscreenView != null) return;
Type gameViewType = typeof(EditorWindow).Assembly.GetType("UnityEditor.GameView");
if (gameViewType == null)
{
Debug.LogError("[FullscreenOnPlay] Could not resolve UnityEditor.GameView — fullscreen capture unavailable on this Unity version.");
return;
}
fullscreenView = ScriptableObject.CreateInstance(gameViewType) as EditorWindow;
if (fullscreenView == null)
{
Debug.LogError("[FullscreenOnPlay] Failed to create a Game view instance — fullscreen capture aborted.");
return;
}
fullscreenView.titleContent = new GUIContent("Game (Fullscreen)");
SetShowToolbar(fullscreenView, false);
Rect fullscreen = MainDisplayRect();
fullscreenView.ShowPopup();
fullscreenView.position = fullscreen;
fullscreenView.minSize = fullscreen.size;
fullscreenView.maxSize = fullscreen.size;
fullscreenView.Focus();
EditorApplication.delayCall += ReassertPosition;
Debug.Log("[FullscreenOnPlay] Game view is fullscreen for capture. To exit: press your Play shortcut (Ctrl/Cmd+P), or Alt+Tab back to the Unity editor and press Stop.");
}
///
/// Closes the fullscreen popup if one is open. Safe to call when nothing is showing (a no-op),
/// so both the play-stop hook and the live toggle can lean on it unconditionally.
///
private static void ExitFullscreen()
{
EditorApplication.delayCall -= ReassertPosition;
if (fullscreenView == null) return;
fullscreenView.Close();
fullscreenView = null;
}
///
/// Re-applies the fullscreen rect one tick after showing, defeating any clamp ShowPopup applied
/// before the window was fully realized. Guards against the window having been closed meanwhile.
///
private static void ReassertPosition()
{
EditorApplication.delayCall -= ReassertPosition;
if (fullscreenView == null) return;
fullscreenView.position = MainDisplayRect();
fullscreenView.Focus();
}
#endregion
#region Internal Helpers
///
/// The main display's bounds expressed in editor points (pixels ÷ pixelsPerPoint), so the window
/// covers the whole monitor correctly even under OS display scaling on high-DPI screens.
///
private static Rect MainDisplayRect()
{
float pixelsPerPoint = Mathf.Max(1f, EditorGUIUtility.pixelsPerPoint);
Resolution res = Screen.currentResolution;
return new Rect(0f, 0f, res.width / pixelsPerPoint, res.height / pixelsPerPoint);
}
///
/// Hides (or shows) the Game view's own toolbar via its internal 'showToolbar' property so the
/// capture is pure game with no editor UI. Silently degrades if the property is missing on a
/// future Unity version — a visible toolbar is a cosmetic loss, not a failure.
///
private static void SetShowToolbar(EditorWindow gameView, bool visible)
{
PropertyInfo prop = gameView.GetType().GetProperty(
"showToolbar", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (prop != null && prop.CanWrite)
prop.SetValue(gameView, visible);
}
///
/// Whether the fullscreen-on-play toggle is currently enabled.
///
private static bool IsEnabled => EditorPrefs.GetBool(PrefKey, false);
#endregion
}
}