diff --git a/Unity/Assets/BundleResources/Prefab/UI/Global/UIGlobalSetting.prefab b/Unity/Assets/BundleResources/Prefab/UI/Global/UIGlobalSetting.prefab new file mode 100644 index 000000000..dd00095f6 --- /dev/null +++ b/Unity/Assets/BundleResources/Prefab/UI/Global/UIGlobalSetting.prefab @@ -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: diff --git a/Unity/Assets/BundleResources/Prefab/UI/Global/UIGlobalSetting.prefab.meta b/Unity/Assets/BundleResources/Prefab/UI/Global/UIGlobalSetting.prefab.meta new file mode 100644 index 000000000..0d1bfcd32 --- /dev/null +++ b/Unity/Assets/BundleResources/Prefab/UI/Global/UIGlobalSetting.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5149219fee234dfaa8477edb443dfff1 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity/Assets/Scripts/TH1_UI/Controller/Global/UIGlobalSettingController.cs b/Unity/Assets/Scripts/TH1_UI/Controller/Global/UIGlobalSettingController.cs new file mode 100644 index 000000000..ee735af09 --- /dev/null +++ b/Unity/Assets/Scripts/TH1_UI/Controller/Global/UIGlobalSettingController.cs @@ -0,0 +1,69 @@ +using TH1_UI.Controller.Base; +using TH1_UI.View.Global; + +namespace TH1_UI.Controller.Global +{ + public class UIGlobalSettingController : ViewController + { + 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()); + } + } +} diff --git a/Unity/Assets/Scripts/TH1_UI/Controller/Global/UIGlobalSettingController.cs.meta b/Unity/Assets/Scripts/TH1_UI/Controller/Global/UIGlobalSettingController.cs.meta new file mode 100644 index 000000000..380fa2a98 --- /dev/null +++ b/Unity/Assets/Scripts/TH1_UI/Controller/Global/UIGlobalSettingController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a351fd2294b4d729bec1d455a9cf8c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity/Assets/Scripts/TH1_UI/View/Global/UIGlobalSettingControls.cs b/Unity/Assets/Scripts/TH1_UI/View/Global/UIGlobalSettingControls.cs new file mode 100644 index 000000000..4b9156779 --- /dev/null +++ b/Unity/Assets/Scripts/TH1_UI/View/Global/UIGlobalSettingControls.cs @@ -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 _onSelected; + + public void Init(UIGlobalSettingPage page, Button button, TMP_Text label, Action 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 _getter; + private Action _setter; + + public void Init(TMP_Text label, Toggle toggle, Func getter, Action 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 _getter; + private Action _setter; + + public void Init(TMP_Text label, Slider slider, TMP_Text valueText, Func getter, Action 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(); + } + } +} diff --git a/Unity/Assets/Scripts/TH1_UI/View/Global/UIGlobalSettingControls.cs.meta b/Unity/Assets/Scripts/TH1_UI/View/Global/UIGlobalSettingControls.cs.meta new file mode 100644 index 000000000..ed4238a5a --- /dev/null +++ b/Unity/Assets/Scripts/TH1_UI/View/Global/UIGlobalSettingControls.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b0e6ccb7ff0f4a22a78dff270581981d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity/Assets/Scripts/TH1_UI/View/Global/UIGlobalSettingView.cs b/Unity/Assets/Scripts/TH1_UI/View/Global/UIGlobalSettingView.cs new file mode 100644 index 000000000..88f0578e4 --- /dev/null +++ b/Unity/Assets/Scripts/TH1_UI/View/Global/UIGlobalSettingView.cs @@ -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 _pages = new Dictionary(); + private readonly Dictionary _tabs = new Dictionary(); + private readonly List _toggleRows = new List(); + private readonly List _sliderRows = new List(); + + 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() == null) + gameObject.AddComponent(); + + if (GetComponent() == null) + gameObject.AddComponent(); + } + + private void BuildRuntimeLayout() + { + var root = GetComponent(); + 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(); + 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(); + 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.color = new Color(0f, 0f, 0f, 0.48f); + var button = go.GetComponent