ui: add inactive global setting panel

This commit is contained in:
daixiawu 2026-06-28 00:53:01 +08:00
parent b8cb845245
commit 08a49c1df2
8 changed files with 1019 additions and 0 deletions

View File

@ -0,0 +1,125 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1000000000000000001
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2000000000000000001}
- component: {fileID: 3000000000000000001}
- component: {fileID: 4000000000000000001}
- component: {fileID: 5000000000000000001}
- component: {fileID: 6000000000000000001}
- component: {fileID: 7000000000000000001}
m_Layer: 5
m_Name: UIGlobalSetting
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &2000000000000000001
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000000000000001}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 1180, y: 720}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &3000000000000000001
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000000000000001}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0ad50f81b1d25c441943c37a89ba23f6, type: 3}
m_Name:
m_EditorClassIdentifier:
_Animator: {fileID: 4000000000000000001}
_ActionOnDisable: 0
--- !u!95 &4000000000000000001
Animator:
serializedVersion: 5
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000000000000001}
m_Enabled: 1
m_Avatar: {fileID: 0}
m_Controller: {fileID: 0}
m_CullingMode: 0
m_UpdateMode: 0
m_ApplyRootMotion: 0
m_LinearVelocityBlending: 0
m_StabilizeFeet: 0
m_WarningMessage:
m_HasTransformHierarchy: 1
m_AllowConstantClipSamplingOptimization: 1
m_KeepAnimatorStateOnDisable: 0
m_WriteDefaultValuesOnDisable: 0
--- !u!225 &5000000000000000001
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000000000000001}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!114 &6000000000000000001
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000000000000001}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e51b9280cf324acb9a977eb172eb62bc, type: 3}
m_Name:
m_EditorClassIdentifier:
_showAnimationClip: {fileID: 0}
_hideAnimationClip: {fileID: 0}
CloseButton: {fileID: 0}
BlockerButton: {fileID: 0}
TabRoot: {fileID: 0}
PageRoot: {fileID: 0}
TitleText: {fileID: 0}
--- !u!114 &7000000000000000001
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000000000000001}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dba07edb0e077114b8d1d67f0a1eaadd, type: 3}
m_Name:
m_EditorClassIdentifier:
GameObjectList: []
GameObjectList2: []
AutoGameObjectList: []
ValueList: []
ClassName:

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5149219fee234dfaa8477edb443dfff1
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,69 @@
using TH1_UI.Controller.Base;
using TH1_UI.View.Global;
namespace TH1_UI.Controller.Global
{
public class UIGlobalSettingController : ViewController<UIGlobalSettingView>
{
public UIGlobalSettingController() { }
protected override void RegisterEventCallback()
{
base.RegisterEventCallback();
if (WindowScript == null) return;
WindowScript.OnBtnCloseClick += _OnBtnCloseClick;
WindowScript.OnBtnRepairRendererClick += _OnBtnRepairRendererClick;
WindowScript.OnBtnBugReportClick += _OnBtnBugReportClick;
WindowScript.OnBtnManageLanguageClick += _OnBtnManageLanguageClick;
}
protected override void UnregisterEventCallback()
{
if (WindowScript != null)
{
WindowScript.OnBtnCloseClick = null;
WindowScript.OnBtnRepairRendererClick = null;
WindowScript.OnBtnBugReportClick = null;
WindowScript.OnBtnManageLanguageClick = null;
}
base.UnregisterEventCallback();
}
protected override void OnOpen()
{
base.OnOpen();
var context = _openParameter is UIGlobalSettingContext settingContext
? settingContext
: UIGlobalSettingContext.OutsideMenu;
WindowScript?.SetContent(context);
}
public override bool Close()
{
WindowScript?.CloseView();
return base.Close();
}
private void _OnBtnCloseClick()
{
Close();
}
private void _OnBtnRepairRendererClick()
{
TH1Renderer.MapRenderer.Instance?.RepairAllRenderers();
}
private void _OnBtnBugReportClick()
{
TH1_Core.Events.EventManager.Publish(new TH1_Core.Events.ShowUIGlobalBugReport());
}
private void _OnBtnManageLanguageClick()
{
TH1_Core.Events.EventManager.Publish(new TH1_Core.Events.ShowUIOutsideMod());
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6a351fd2294b4d729bec1d455a9cf8c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,115 @@
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace TH1_UI.View.Global
{
public class UIGlobalSettingTabMono : MonoBehaviour
{
private UIGlobalSettingPage _page;
private Button _button;
private TMP_Text _label;
private Action<UIGlobalSettingPage> _onSelected;
public void Init(UIGlobalSettingPage page, Button button, TMP_Text label, Action<UIGlobalSettingPage> onSelected)
{
_page = page;
_button = button;
_label = label;
_onSelected = onSelected;
if (_button == null) return;
_button.onClick.RemoveAllListeners();
_button.onClick.AddListener(() => _onSelected?.Invoke(_page));
}
public void SetSelected(bool selected)
{
if (_button != null && _button.targetGraphic != null)
_button.targetGraphic.color = selected ? new Color32(97, 121, 155, 255) : new Color32(58, 66, 84, 255);
if (_label != null)
_label.color = selected ? Color.white : new Color32(190, 197, 208, 255);
}
}
public class UIGlobalSettingToggleRowMono : MonoBehaviour
{
private TMP_Text _label;
private Toggle _toggle;
private Func<bool> _getter;
private Action<bool> _setter;
public void Init(TMP_Text label, Toggle toggle, Func<bool> getter, Action<bool> setter)
{
_label = label;
_toggle = toggle;
_getter = getter;
_setter = setter;
if (_toggle == null) return;
_toggle.onValueChanged.RemoveAllListeners();
_toggle.onValueChanged.AddListener(OnValueChanged);
Refresh();
}
public void Refresh()
{
if (_toggle == null || _getter == null) return;
_toggle.SetIsOnWithoutNotify(_getter());
}
private void OnValueChanged(bool value)
{
_setter?.Invoke(value);
Refresh();
}
}
public class UIGlobalSettingSliderRowMono : MonoBehaviour
{
private TMP_Text _label;
private Slider _slider;
private TMP_Text _valueText;
private Func<float> _getter;
private Action<float> _setter;
public void Init(TMP_Text label, Slider slider, TMP_Text valueText, Func<float> getter, Action<float> setter)
{
_label = label;
_slider = slider;
_valueText = valueText;
_getter = getter;
_setter = setter;
if (_slider == null) return;
_slider.minValue = 0f;
_slider.maxValue = 1f;
_slider.onValueChanged.RemoveAllListeners();
_slider.onValueChanged.AddListener(OnValueChanged);
Refresh();
}
public void Refresh()
{
if (_slider == null || _getter == null) return;
float value = Mathf.Clamp01(_getter());
_slider.SetValueWithoutNotify(value);
RefreshValueText(value);
}
private void OnValueChanged(float value)
{
value = Mathf.Clamp01(value);
_setter?.Invoke(value);
RefreshValueText(value);
}
private void RefreshValueText(float value)
{
if (_valueText != null)
_valueText.text = Mathf.RoundToInt(value * 100f).ToString();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b0e6ccb7ff0f4a22a78dff270581981d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,670 @@
using System;
using System.Collections.Generic;
using Logic.Multilingual;
using TH1_Logic.Config;
using TH1_UI.View.Base;
using TH1_UI.View.Outside;
using TH1Renderer;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace TH1_UI.View.Global
{
public enum UIGlobalSettingContext
{
OutsideMenu,
InGame
}
public enum UIGlobalSettingPage
{
Language,
Audio,
Display,
Game,
Support
}
public class UIGlobalSettingView : View.Base.View
{
public Button CloseButton;
public Button BlockerButton;
public Transform TabRoot;
public Transform PageRoot;
public TMP_Text TitleText;
public ViDelegateAssisstant.Dele OnBtnCloseClick;
public ViDelegateAssisstant.Dele OnBtnRepairRendererClick;
public ViDelegateAssisstant.Dele OnBtnBugReportClick;
public ViDelegateAssisstant.Dele OnBtnManageLanguageClick;
private readonly Dictionary<UIGlobalSettingPage, GameObject> _pages = new Dictionary<UIGlobalSettingPage, GameObject>();
private readonly Dictionary<UIGlobalSettingPage, UIGlobalSettingTabMono> _tabs = new Dictionary<UIGlobalSettingPage, UIGlobalSettingTabMono>();
private readonly List<UIGlobalSettingToggleRowMono> _toggleRows = new List<UIGlobalSettingToggleRowMono>();
private readonly List<UIGlobalSettingSliderRowMono> _sliderRows = new List<UIGlobalSettingSliderRowMono>();
private UIOutsideSelectOptionGroupMono _languageOptionGroup;
private GameObject _moreLanguageModule;
private TMP_Text _nowUsingLanguageText;
private Button _manageLanguageButton;
private Button _repairRendererButton;
private Button _bugReportButton;
private UIGlobalSettingContext _context;
private UIGlobalSettingPage _currentPage = UIGlobalSettingPage.Language;
private bool _builtRuntimeLayout;
protected override void OnInit()
{
base.OnInit();
EnsureLayout();
BindCloseButtons();
}
public void SetContent(UIGlobalSettingContext context)
{
EnsureLayout();
_context = context;
RefreshLanguageOption();
RefreshRows();
RefreshContextVisibility();
SelectPage(_currentPage);
}
public void CloseView()
{
if (_moreLanguageModule != null) _moreLanguageModule.SetActive(false);
}
private void BindCloseButtons()
{
if (CloseButton != null)
{
CloseButton.onClick.RemoveAllListeners();
CloseButton.onClick.AddListener(() => OnBtnCloseClick?.Invoke());
}
if (BlockerButton != null)
{
BlockerButton.onClick.RemoveAllListeners();
BlockerButton.onClick.AddListener(() => OnBtnCloseClick?.Invoke());
}
}
private void RefreshRows()
{
foreach (var row in _toggleRows)
{
if (row == null) continue;
row.Refresh();
}
foreach (var row in _sliderRows)
{
if (row == null) continue;
row.Refresh();
}
}
private void RefreshContextVisibility()
{
SetPageVisible(UIGlobalSettingPage.Support, _context == UIGlobalSettingContext.InGame);
if (_context != UIGlobalSettingContext.InGame && _currentPage == UIGlobalSettingPage.Support)
_currentPage = UIGlobalSettingPage.Language;
}
private void SetPageVisible(UIGlobalSettingPage page, bool visible)
{
if (_tabs.TryGetValue(page, out var tab) && tab != null)
tab.gameObject.SetActive(visible);
}
private void SelectPage(UIGlobalSettingPage page)
{
if (!_pages.ContainsKey(page))
page = UIGlobalSettingPage.Language;
_currentPage = page;
foreach (var kv in _pages)
{
if (kv.Value != null)
kv.Value.SetActive(kv.Key == page);
}
foreach (var kv in _tabs)
{
if (kv.Value != null)
kv.Value.SetSelected(kv.Key == page);
}
}
private void RefreshLanguageOption()
{
if (_languageOptionGroup == null) return;
var lan = MultilingualManager.Instance.GetCurLanguage();
bool useMore = !IsPrimaryLanguage(lan);
uint idx;
if (useMore)
{
idx = 5;
if (ConfigManager.Instance.Config.SecondaryLanguage != lan)
ConfigManager.Instance.Config.SecondaryLanguage = lan;
}
else
{
idx = lan switch
{
MultilingualType.ZH => 0,
MultilingualType.TDZH => 1,
MultilingualType.JP => 2,
MultilingualType.EN => 3,
MultilingualType.KR => 4,
_ => 3
};
}
_languageOptionGroup.Init(idx);
_languageOptionGroup.OnOptionClicked = OnLanguageClicked;
RefreshMoreLanguageModule(useMore);
}
private static bool IsPrimaryLanguage(MultilingualType type)
{
return type == MultilingualType.ZH
|| type == MultilingualType.TDZH
|| type == MultilingualType.JP
|| type == MultilingualType.EN
|| type == MultilingualType.KR;
}
private void RefreshMoreLanguageModule(bool show)
{
if (_moreLanguageModule != null)
_moreLanguageModule.SetActive(show);
if (_nowUsingLanguageText != null)
_nowUsingLanguageText.text = GetLanguageDisplayName(MultilingualManager.Instance.GetCurLanguage());
}
private void OnLanguageClicked(uint idx)
{
if (idx == 5)
{
var secondary = ConfigManager.Instance.Config.SecondaryLanguage;
if (secondary == MultilingualType.None) secondary = MultilingualType.EN;
MultilingualManager.Instance.ChangedMultilingual(secondary);
RefreshMoreLanguageModule(true);
return;
}
RefreshMoreLanguageModule(false);
switch (idx)
{
case 0:
MultilingualManager.Instance.ChangedMultilingual(MultilingualType.ZH);
break;
case 1:
MultilingualManager.Instance.ChangedMultilingual(MultilingualType.TDZH);
break;
case 2:
MultilingualManager.Instance.ChangedMultilingual(MultilingualType.JP);
break;
case 3:
MultilingualManager.Instance.ChangedMultilingual(MultilingualType.EN);
break;
case 4:
MultilingualManager.Instance.ChangedMultilingual(MultilingualType.KR);
break;
}
}
private static string GetLanguageDisplayName(MultilingualType type)
{
return type switch
{
MultilingualType.ZH => "简体中文",
MultilingualType.TDZH => "繁體中文",
MultilingualType.EN => "English",
MultilingualType.JP => "日本語",
MultilingualType.KR => "한국어",
MultilingualType.RU => "Русский",
MultilingualType.ES => "Español",
MultilingualType.PT => "Português",
MultilingualType.FR => "Français",
MultilingualType.DE => "Deutsch",
MultilingualType.ID => "Bahasa Indonesia",
MultilingualType.TH => "ไทย",
MultilingualType.PL => "Polski",
MultilingualType.VI => "Tiếng Việt",
MultilingualType.MS => "Bahasa Melayu",
MultilingualType.UK => "Українська",
MultilingualType.KZ => "Қазақша",
MultilingualType.TR => "Türkçe",
MultilingualType.IT => "Italiano",
MultilingualType.NL => "Nederlands",
MultilingualType.FI => "Suomi",
MultilingualType.SV => "Svenska",
MultilingualType.NO => "Norsk",
MultilingualType.CS => "Čeština",
MultilingualType.HU => "Magyar",
MultilingualType.EL => "Ελληνικά",
MultilingualType.RO => "Română",
MultilingualType.ET => "Eesti",
MultilingualType.LT => "Lietuvių",
MultilingualType.HR => "Hrvatski",
MultilingualType.SR => "Српски",
MultilingualType.SL => "Slovenščina",
MultilingualType.SK => "Slovenčina",
MultilingualType.BE => "Беларуская",
MultilingualType.HE => "עברית",
MultilingualType.BG => "Български",
MultilingualType.UZ => "O'zbekcha",
MultilingualType.KY => "Кыргызча",
MultilingualType.MN => "Монгол",
MultilingualType.AR => "العربية",
MultilingualType.DA => "Dansk",
MultilingualType.TL => "Filipino",
MultilingualType.Custom => "Custom",
_ => type.ToString()
};
}
private void EnsureLayout()
{
if (_builtRuntimeLayout) return;
_builtRuntimeLayout = true;
EnsureRootComponents();
BuildRuntimeLayout();
BindCloseButtons();
}
private void EnsureRootComponents()
{
if (GetComponent<RectTransform>() == null)
gameObject.AddComponent<RectTransform>();
if (GetComponent<CanvasGroup>() == null)
gameObject.AddComponent<CanvasGroup>();
}
private void BuildRuntimeLayout()
{
var root = GetComponent<RectTransform>();
root.anchorMin = new Vector2(0.5f, 0.5f);
root.anchorMax = new Vector2(0.5f, 0.5f);
root.sizeDelta = new Vector2(1180f, 720f);
root.pivot = new Vector2(0.5f, 0.5f);
BlockerButton = CreateFullScreenBlocker();
var panel = CreatePanel(root);
TitleText = CreateText(panel, "Title", "设置", 34, TextAlignmentOptions.Center, new RectSpec(0.5f, 0.5f, 1f, 1f, 0f, -36f, 260f, 52f));
CloseButton = CreateTextButton(panel, "CloseButton", "×", new RectSpec(1f, 1f, 1f, 1f, -46f, -36f, 42f, 42f));
TabRoot = CreateRect(panel, "TabRoot", new RectSpec(0f, 0f, 0f, 1f, 118f, -92f, 180f, 44f));
var tabLayout = TabRoot.gameObject.AddComponent<VerticalLayoutGroup>();
tabLayout.spacing = 8f;
tabLayout.childControlWidth = true;
tabLayout.childControlHeight = false;
tabLayout.childForceExpandWidth = true;
tabLayout.childForceExpandHeight = false;
PageRoot = CreateRect(panel, "PageRoot", new RectSpec(0f, 1f, 0f, 1f, 230f, -96f, -32f, 44f));
AddPage(UIGlobalSettingPage.Language, "语言");
AddPage(UIGlobalSettingPage.Audio, "音频");
AddPage(UIGlobalSettingPage.Display, "显示");
AddPage(UIGlobalSettingPage.Game, "游戏");
AddPage(UIGlobalSettingPage.Support, "支持");
BuildLanguagePage(_pages[UIGlobalSettingPage.Language].transform);
BuildAudioPage(_pages[UIGlobalSettingPage.Audio].transform);
BuildDisplayPage(_pages[UIGlobalSettingPage.Display].transform);
BuildGamePage(_pages[UIGlobalSettingPage.Game].transform);
BuildSupportPage(_pages[UIGlobalSettingPage.Support].transform);
SelectPage(UIGlobalSettingPage.Language);
}
private Button CreateFullScreenBlocker()
{
var go = new GameObject("Blocker", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button));
go.layer = gameObject.layer;
go.transform.SetParent(transform, false);
var rect = go.GetComponent<RectTransform>();
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.offsetMin = new Vector2(-2000f, -1200f);
rect.offsetMax = new Vector2(2000f, 1200f);
var image = go.GetComponent<Image>();
image.color = new Color(0f, 0f, 0f, 0.48f);
var button = go.GetComponent<Button>();
button.transition = Selectable.Transition.None;
button.targetGraphic = image;
return button;
}
private RectTransform CreatePanel(RectTransform parent)
{
var panel = CreateRect(parent, "Panel", new RectSpec(0.5f, 0.5f, 0.5f, 0.5f, 0f, 0f, 1120f, 640f));
var image = panel.gameObject.AddComponent<Image>();
image.color = new Color32(29, 34, 44, 245);
return panel;
}
private void AddPage(UIGlobalSettingPage page, string label)
{
var tabButton = CreateTextButton(TabRoot, page + "Tab", label, new RectSpec(0f, 1f, 1f, 1f, 0f, 0f, 0f, 48f));
var layout = tabButton.gameObject.AddComponent<LayoutElement>();
layout.minHeight = 48f;
layout.preferredHeight = 48f;
var tab = tabButton.gameObject.AddComponent<UIGlobalSettingTabMono>();
tab.Init(page, tabButton, tabButton.GetComponentInChildren<TMP_Text>(true), SelectPage);
_tabs[page] = tab;
var pageRect = CreateRect(PageRoot, page + "Page", new RectSpec(0f, 1f, 0f, 1f, 0f, 0f, 0f, 0f));
var pageLayout = pageRect.gameObject.AddComponent<VerticalLayoutGroup>();
pageLayout.padding = new RectOffset(0, 0, 0, 0);
pageLayout.spacing = 10f;
pageLayout.childControlWidth = true;
pageLayout.childControlHeight = false;
pageLayout.childForceExpandWidth = true;
pageLayout.childForceExpandHeight = false;
_pages[page] = pageRect.gameObject;
}
private void BuildLanguagePage(Transform parent)
{
_languageOptionGroup = CreateLanguageOptionGroup(parent);
_moreLanguageModule = CreateRect(parent, "MoreLanguageModule", new RectSpec()).gameObject;
var layout = _moreLanguageModule.AddComponent<HorizontalLayoutGroup>();
layout.spacing = 12f;
layout.childControlWidth = false;
layout.childControlHeight = true;
layout.childForceExpandWidth = false;
layout.childForceExpandHeight = false;
_moreLanguageModule.AddComponent<LayoutElement>().preferredHeight = 44f;
CreateText(_moreLanguageModule.transform, "NowUsingTitle", "当前语言", 20, TextAlignmentOptions.Left, new RectSpec())
.gameObject.AddComponent<LayoutElement>().preferredWidth = 110f;
_nowUsingLanguageText = CreateText(_moreLanguageModule.transform, "NowUsingLanguage", "", 20, TextAlignmentOptions.Left, new RectSpec());
_nowUsingLanguageText.gameObject.AddComponent<LayoutElement>().preferredWidth = 220f;
_manageLanguageButton = CreateTextButton(_moreLanguageModule.transform, "ManageLanguageButton", "管理", new RectSpec());
_manageLanguageButton.gameObject.AddComponent<LayoutElement>().preferredWidth = 96f;
_manageLanguageButton.onClick.AddListener(() => OnBtnManageLanguageClick?.Invoke());
_moreLanguageModule.SetActive(false);
}
private void BuildAudioPage(Transform parent)
{
AddSliderRow(parent, "音乐音量", () => ConfigManager.Instance.Config.MusicVolume,
value => ConfigManager.Instance.Config.MusicVolume = value);
AddSliderRow(parent, "音效音量", () => ConfigManager.Instance.Config.AudioVolume,
value => ConfigManager.Instance.Config.AudioVolume = value);
AddToggleRow(parent, "BGM连续播放", () => ConfigManager.Instance.Config.BgmContinuousPlay,
value => ConfigManager.Instance.Config.BgmContinuousPlay = value);
}
private void BuildDisplayPage(Transform parent)
{
AddToggleRow(parent, "护眼模式", () => ConfigManager.Instance.Config.EyeComfortModeEnabled,
value =>
{
ConfigManager.Instance.Config.EyeComfortModeEnabled = value;
VisualSpriteResolver.SetTheme(value ? VisualTheme.EyeComfort : VisualTheme.Default);
MapRenderer.Instance?.RefreshVisualTheme();
});
AddToggleRow(parent, "详细移动提示", () => ConfigManager.Instance.Config.EnhancedMoveHighlightEnabled,
value =>
{
ConfigManager.Instance.Config.EnhancedMoveHighlightEnabled = value;
MapRenderer.Instance?.RefreshAllHighlights();
});
AddDisabledRow(parent, "快速动画", "预留");
}
private void BuildGamePage(Transform parent)
{
AddToggleRow(parent, "关键时刻", () => ConfigManager.Instance.Config.KeyMomentEnabled,
value => ConfigManager.Instance.Config.KeyMomentEnabled = value);
AddToggleRow(parent, "下一回合二次确认", () => ConfigManager.Instance.Config.ConfirmBeforeNextTurn,
value => ConfigManager.Instance.Config.ConfirmBeforeNextTurn = value);
AddToggleRow(parent, "始终显示城市边界", () => ConfigManager.Instance.Config.AlwaysShowCityBorder,
value => ConfigManager.Instance.Config.AlwaysShowCityBorder = value);
AddDisabledRow(parent, "显示跳过行动", "预留");
}
private void BuildSupportPage(Transform parent)
{
_bugReportButton = AddButtonRow(parent, "上传Bug报告", () => OnBtnBugReportClick?.Invoke());
_repairRendererButton = AddButtonRow(parent, "修复画面错误", () => OnBtnRepairRendererClick?.Invoke());
}
private void AddToggleRow(Transform parent, string label, Func<bool> getter, Action<bool> setter)
{
var row = CreateSettingRow(parent, "ToggleRow_" + label);
var title = CreateText(row, "Label", label, 22, TextAlignmentOptions.Left, new RectSpec(0f, 0.6f, 0f, 1f, 16f, 0f, -8f, 0f));
var toggle = CreateToggle(row, "Toggle", new RectSpec(1f, 1f, 0.5f, 0.5f, -86f, 0f, 58f, 34f));
var mono = row.gameObject.AddComponent<UIGlobalSettingToggleRowMono>();
mono.Init(title, toggle, getter, setter);
_toggleRows.Add(mono);
}
private void AddSliderRow(Transform parent, string label, Func<float> getter, Action<float> setter)
{
var row = CreateSettingRow(parent, "SliderRow_" + label);
var title = CreateText(row, "Label", label, 22, TextAlignmentOptions.Left, new RectSpec(0f, 0.36f, 0f, 1f, 16f, 0f, -8f, 0f));
var slider = CreateSlider(row, "Slider", new RectSpec(0.42f, 0.88f, 0.5f, 0.5f, 0f, 0f, 0f, 24f));
var valueText = CreateText(row, "Value", "", 18, TextAlignmentOptions.Right, new RectSpec(0.89f, 1f, 0f, 1f, -8f, 0f, -16f, 0f));
var mono = row.gameObject.AddComponent<UIGlobalSettingSliderRowMono>();
mono.Init(title, slider, valueText, getter, setter);
_sliderRows.Add(mono);
}
private void AddDisabledRow(Transform parent, string label, string state)
{
var row = CreateSettingRow(parent, "DisabledRow_" + label);
CreateText(row, "Label", label, 22, TextAlignmentOptions.Left, new RectSpec(0f, 0.65f, 0f, 1f, 16f, 0f, -8f, 0f));
var stateText = CreateText(row, "State", state, 18, TextAlignmentOptions.Right, new RectSpec(0.66f, 1f, 0f, 1f, 0f, 0f, -16f, 0f));
stateText.color = new Color32(170, 177, 188, 255);
}
private Button AddButtonRow(Transform parent, string label, Action onClick)
{
var row = CreateSettingRow(parent, "ButtonRow_" + label);
CreateText(row, "Label", label, 22, TextAlignmentOptions.Left, new RectSpec(0f, 0.6f, 0f, 1f, 16f, 0f, -8f, 0f));
var button = CreateTextButton(row, "ActionButton", "执行", new RectSpec(1f, 1f, 0.5f, 0.5f, -108f, 0f, 92f, 38f));
button.onClick.AddListener(() => onClick?.Invoke());
return button;
}
private UIOutsideSelectOptionGroupMono CreateLanguageOptionGroup(Transform parent)
{
var row = CreateSettingRow(parent, "LanguageOptionGroupRow");
CreateText(row, "Label", "语言", 22, TextAlignmentOptions.Left, new RectSpec(0f, 0.18f, 0f, 1f, 16f, 0f, -8f, 0f));
var groupRect = CreateRect(row, "LanguageOptionGroup", new RectSpec(0.2f, 1f, 0.5f, 0.5f, 0f, 0f, -16f, 42f));
var group = groupRect.gameObject.AddComponent<UIOutsideSelectOptionGroupMono>();
var bg = CreateRect(groupRect, "SelectBG", new RectSpec(0f, 0f, 0.5f, 0.5f, 0f, 0f, 88f, 36f));
bg.gameObject.AddComponent<Image>().color = new Color32(72, 91, 122, 255);
group.SelectBG = bg;
string[] labels = { "简中", "繁中", "日本語", "English", "한국어", "更多" };
for (int i = 0; i < labels.Length; i++)
{
var button = CreateTextButton(groupRect, "Option" + i, labels[i],
new RectSpec(0f, 0f, 0.5f, 0.5f, 48f + i * 102f, 0f, 92f, 36f));
group.options.Add(button);
}
return group;
}
private RectTransform CreateSettingRow(Transform parent, string name)
{
var row = CreateRect(parent, name, new RectSpec());
var image = row.gameObject.AddComponent<Image>();
image.color = new Color32(43, 49, 63, 220);
var layout = row.gameObject.AddComponent<LayoutElement>();
layout.minHeight = 58f;
layout.preferredHeight = 58f;
return row;
}
private Toggle CreateToggle(Transform parent, string name, RectSpec spec)
{
var root = CreateRect(parent, name, spec);
var bg = root.gameObject.AddComponent<Image>();
bg.color = new Color32(70, 76, 90, 255);
var toggle = root.gameObject.AddComponent<Toggle>();
toggle.targetGraphic = bg;
var checkmark = CreateRect(root, "Checkmark", new RectSpec(0f, 0f, 0.5f, 0.5f, 24f, 0f, 22f, 22f));
checkmark.gameObject.AddComponent<Image>().color = new Color32(126, 187, 118, 255);
toggle.graphic = checkmark.GetComponent<Image>();
return toggle;
}
private Slider CreateSlider(Transform parent, string name, RectSpec spec)
{
var root = CreateRect(parent, name, spec);
var bg = root.gameObject.AddComponent<Image>();
bg.color = new Color32(66, 72, 86, 255);
var fillArea = CreateRect(root, "Fill Area", new RectSpec(0f, 1f, 0f, 1f, 4f, 0f, -4f, 0f));
var fill = CreateRect(fillArea, "Fill", new RectSpec(0f, 1f, 0f, 1f, 0f, 0f, 0f, 0f));
fill.gameObject.AddComponent<Image>().color = new Color32(115, 151, 190, 255);
var handleArea = CreateRect(root, "Handle Slide Area", new RectSpec(0f, 1f, 0f, 1f, 4f, 0f, -4f, 0f));
var handle = CreateRect(handleArea, "Handle", new RectSpec(0f, 0f, 0.5f, 0.5f, 0f, 0f, 18f, 32f));
handle.gameObject.AddComponent<Image>().color = Color.white;
var slider = root.gameObject.AddComponent<Slider>();
slider.minValue = 0f;
slider.maxValue = 1f;
slider.fillRect = fill;
slider.handleRect = handle;
slider.targetGraphic = handle.GetComponent<Image>();
slider.direction = Slider.Direction.LeftToRight;
return slider;
}
private Button CreateTextButton(Transform parent, string name, string text, RectSpec spec)
{
var rect = CreateRect(parent, name, spec);
var image = rect.gameObject.AddComponent<Image>();
image.color = new Color32(68, 76, 98, 255);
var button = rect.gameObject.AddComponent<Button>();
button.targetGraphic = image;
var label = CreateText(rect, "Text", text, 20, TextAlignmentOptions.Center, new RectSpec(0f, 1f, 0f, 1f, 0f, 0f, 0f, 0f));
label.raycastTarget = false;
return button;
}
private TMP_Text CreateText(Transform parent, string name, string text, int fontSize, TextAlignmentOptions alignment, RectSpec spec)
{
var rect = CreateRect(parent, name, spec);
var tmp = rect.gameObject.AddComponent<TextMeshProUGUI>();
tmp.text = text;
tmp.fontSize = fontSize;
tmp.enableAutoSizing = true;
tmp.fontSizeMin = 12;
tmp.fontSizeMax = fontSize;
tmp.color = Color.white;
tmp.alignment = alignment;
tmp.raycastTarget = false;
return tmp;
}
private RectTransform CreateRect(Transform parent, string name, RectSpec spec)
{
var go = new GameObject(name, typeof(RectTransform));
go.layer = gameObject.layer;
go.transform.SetParent(parent, false);
var rect = go.GetComponent<RectTransform>();
spec.Apply(rect);
return rect;
}
private readonly struct RectSpec
{
private readonly bool _hasValue;
private readonly float _anchorMinX;
private readonly float _anchorMaxX;
private readonly float _anchorMinY;
private readonly float _anchorMaxY;
private readonly float _x;
private readonly float _y;
private readonly float _w;
private readonly float _h;
public RectSpec(float anchorMinX, float anchorMaxX, float anchorMinY, float anchorMaxY,
float x, float y, float w, float h)
{
_hasValue = true;
_anchorMinX = anchorMinX;
_anchorMaxX = anchorMaxX;
_anchorMinY = anchorMinY;
_anchorMaxY = anchorMaxY;
_x = x;
_y = y;
_w = w;
_h = h;
}
public void Apply(RectTransform rect)
{
if (!_hasValue)
{
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.offsetMin = Vector2.zero;
rect.offsetMax = Vector2.zero;
rect.pivot = new Vector2(0.5f, 0.5f);
return;
}
rect.anchorMin = new Vector2(_anchorMinX, _anchorMinY);
rect.anchorMax = new Vector2(_anchorMaxX, _anchorMaxY);
rect.pivot = new Vector2(0.5f, 0.5f);
var anchoredPosition = rect.anchoredPosition;
var sizeDelta = rect.sizeDelta;
var offsetMin = rect.offsetMin;
var offsetMax = rect.offsetMax;
if (Mathf.Approximately(_anchorMinX, _anchorMaxX))
{
anchoredPosition.x = _x;
sizeDelta.x = _w;
}
else
{
offsetMin.x = _x;
offsetMax.x = _w;
}
if (Mathf.Approximately(_anchorMinY, _anchorMaxY))
{
anchoredPosition.y = _y;
sizeDelta.y = _h;
}
else
{
offsetMin.y = _h;
offsetMax.y = _y;
}
rect.anchoredPosition = anchoredPosition;
rect.sizeDelta = sizeDelta;
rect.offsetMin = offsetMin;
rect.offsetMax = offsetMax;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e51b9280cf324acb9a977eb172eb62bc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: