Abstract base class for menu stages that host a single ScreenPanel UI. It provides creation of a UI root GameObject positioned and scaled appropriately, exposes FadeAlpha and IsFadingOut state tied to the GameManager transition, and a helper to start the stage transition via the manager.
namespace BlockParty;
/// <summary>
/// Base for the Razor-UI menu stages (title / highscores / score tally). Each menu stage
/// hosts a single screen-space <see cref="ScreenPanel"/> under the stage <see cref="StageBase.Root"/>.
/// Switching to another stage is handled by the shared growing/shrinking square wipe owned by
/// <see cref="GameManager"/> (see <see cref="FadeToStage"/>), so the menus themselves render at
/// full opacity and rely on the overlay for the transition.
/// </summary>
public abstract class MenuStageBase : StageBase
{
/// <summary>Whole-UI opacity, 0..1. Menus are fully opaque — the GameManager square wipe
/// (not a UI crossfade) handles transitions, matching the original. Kept so the Razor panels'
/// existing <c>opacity</c> binding stays valid.</summary>
public float FadeAlpha => 1f;
/// <summary>True while a stage-transition wipe is in progress (input should be locked out).</summary>
protected bool IsFadingOut => Manager.IsTransitioning;
/// <summary>The GameObject carrying the ScreenPanel + screen PanelComponent.</summary>
protected GameObject UiRoot { get; private set; }
protected MenuStageBase( GameManager manager ) : base( manager ) { }
/// <summary>
/// Create the screen-panel host. Add the concrete screen <see cref="PanelComponent"/> to
/// the returned GameObject and wire its stage reference.
/// </summary>
protected GameObject CreateUiRoot( int zIndex = 100 )
{
// GameStage may have left the camera offset mid-screenshake; recentre it for menus.
if ( Manager.Camera is not null )
Manager.Camera.WorldPosition = new Vector3( Arena.WIDTH / 2f, Arena.HEIGHT / 2f, 2000f );
UiRoot = CreateChild( "UI" );
var screen = UiRoot.Components.Create<ScreenPanel>();
screen.ZIndex = zIndex;
// Leave AutoScreenScale on (ConsistentHeight): the root panel computes its scale inside
// the layout pass, so every element is sized correctly on its first frame. (A manual
// post-hoc scale applied after first layout left static elements stuck at scale 1.) UI is
// therefore designed in 1080-reference space, like the engine's built-in panels.
return UiRoot;
}
/// <summary>Begin the square wipe to <paramref name="next"/>; GameManager covers the screen,
/// swaps the stage, then reveals it. Ignored if a wipe is already running.</summary>
public void FadeToStage( StageBase next )
{
Manager.TransitionToStage( next );
}
}