UI/SettingsPanel.cs
using System;
using Sandbox.UI;
/// <summary>
/// 设置页(v0.7.8.16 用户需求):主菜单 [4] SETTINGS 进入的二级界面。
/// 点击行循环取值(同 LobbyPanel 设置区交互):
/// - BLOOM 泛光开关(cr_bloom,NeonCamera.OnUpdate 每帧幂等应用,即时生效)
/// - MUSIC VOLUME 音乐音量五档循环(写 cr_music_volume,GameMusic.Tick 每帧应用到播放句柄)
/// BACK / [4] 回主菜单(与 RoomBrowser 同款显隐与键位)。
/// 纯 C# Panel + 同目录 SettingsPanel.cs.scss。后续新设置往 BuildUi 加行即可。
/// </summary>
public sealed class SettingsPanel : PanelComponent
{
Label _bloom;
Label _music;
bool _built;
bool _shown;
bool _retryOpen; // EnsureWorld 先于 OnStart 调 Show 时重试
protected override void OnStart()
{
base.OnStart();
BuildUi();
Hide();
}
void BuildUi()
{
var root = Panel;
if ( root is null ) return;
if ( _built && _bloom.IsValid() && root.ChildrenCount > 0 ) return;
root.DeleteChildren( true );
root.Style.Position = PositionMode.Absolute;
root.Style.Left = 0f;
root.Style.Top = 0f;
root.Style.Right = 0f;
root.Style.Bottom = 0f;
root.Style.BackgroundColor = new Color( 0.008f, 0.012f, 0.05f, 0.55f );
root.Style.PointerEvents = PointerEvents.All;
UiKit.AttachStyles( root ); // 共享部件样式(.cr-pill 药丸按钮)
var title = new Label() { Classes = "sp-title" };
title.Text = "SETTINGS";
root.AddChild( title );
var hint = new Label() { Classes = "sp-hint" };
hint.Text = "CLICK A SETTING TO CHANGE IT";
root.AddChild( hint );
_bloom = new Label() { Classes = "sp-row" };
_bloom.Style.Position = PositionMode.Absolute;
_bloom.Style.Left = Length.Percent( 50f ); // 600 宽行居中(宽在 scss),任意分辨率自适应
_bloom.Style.MarginLeft = -300f;
_bloom.Style.Width = 600f;
_bloom.Style.Top = 360f;
_bloom.AddEventListener( "onclick", () => OnBloomClicked() ); // 捕获 this:热重载可重映射
root.AddChild( _bloom );
_music = new Label() { Classes = "sp-row" };
_music.Style.Position = PositionMode.Absolute;
_music.Style.Left = Length.Percent( 50f );
_music.Style.MarginLeft = -300f;
_music.Style.Width = 600f;
_music.Style.Top = 430f;
_music.AddEventListener( "onclick", () => OnMusicClicked() );
root.AddChild( _music );
UiKit.PillButton( root, "BACK", "sp-back", () => OnBackClicked() ); // 捕获 this:热重载可重映射
_built = true;
}
/// <summary> 主菜单 [4] 进入 </summary>
public void Show()
{
BuildUi();
if ( !_built )
{
_retryOpen = true; // Panel 未就绪(EnsureWorld 同帧调用),OnUpdate 补开
return;
}
_retryOpen = false;
_shown = true;
Panel.Style.Display = DisplayMode.Flex;
RefreshValues();
}
/// <summary> 收起(BACK / 回菜单) </summary>
public void Hide()
{
_shown = false;
_retryOpen = false;
if ( Panel.IsValid() ) Panel.Style.Display = DisplayMode.None;
}
/// <summary> 页面是否开着(ReconcileUi 终态判定用:开着时主菜单让位) </summary>
public bool IsOpen => _shown;
protected override void OnUpdate()
{
base.OnUpdate();
BuildUi();
if ( _retryOpen && !_shown )
{
Show();
return;
}
if ( !_shown ) return;
if ( Panel.IsValid() && Panel.Style.Display != DisplayMode.Flex ) Panel.Style.Display = DisplayMode.Flex; // 同值守卫(v0.7.8.87)
Mouse.Visibility = MouseVisibility.Visible; // 行/按钮要能点
RefreshValues();
if ( Input.Pressed( "Slot4" ) ) OnBackClicked(); // 与 RoomBrowser 同键位
}
/// <summary> 行文本取当前值(SetText 防抖) </summary>
void RefreshValues()
{
if ( _bloom.IsValid() )
SetText( _bloom, $"BLOOM / GLOW — {( GameConfig.ConvarBloom ? "ON" : "OFF" )}" );
if ( _music.IsValid() )
SetText( _music, $"MUSIC VOLUME — {(int)MathF.Round( GameMusic.Volume * 100f )}%" );
}
static void SetText( Label l, string text )
{
if ( l is null || !l.IsValid() || l.Text == text ) return;
l.Text = text;
}
void OnBloomClicked()
{
GameConfig.ConvarBloom = !GameConfig.ConvarBloom;
RefreshValues();
}
/// <summary> 音乐音量五档循环:0 → 25% → 50% → 75% → 100% → 0 </summary>
void OnMusicClicked()
{
GameMusic.Volume = ( MathF.Round( GameMusic.Volume * 4f ) + 1f ) % 5f / 4f;
RefreshValues();
}
void OnBackClicked()
{
if ( !_shown ) return;
CircleroyaleGame.Current?.CloseSettings();
}
}