阶段性完成
This commit is contained in:
@@ -1,48 +1,298 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.Localization;
|
||||
using Michsky.MUIP;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Localization;
|
||||
|
||||
namespace Ichni.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings 页面使用的下拉控件。
|
||||
/// <para>
|
||||
/// 本地化枚举项由 <see cref="SetUpLocalized(int,string,string[])"/> 在代码中传入 String Table 名称与
|
||||
/// Entry Key。控件会创建对应的 <see cref="LocalizedString"/>,并在 Michsky 生成 Item 后为每个
|
||||
/// 可见文字绑定 <see cref="LocalizedTMPText"/>;切换语言时不重建列表。
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// 分辨率、语言自称等运行时文本则使用 <see cref="SetUpRuntime"/>,不会附加本地化组件。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class Dropdown : SettingsUIElementBase
|
||||
{
|
||||
public List<string> options;
|
||||
// 仅保存本次初始化创建的运行时引用,不在 Inspector 中维护本地化选项列表。
|
||||
private readonly List<LocalizedString> _localizedOptions = new List<LocalizedString>();
|
||||
|
||||
// 保留原字段以兼容已有的外部读取。静态本地化选项使用稳定的占位名称;实际显示内容由
|
||||
// 动态生成 Item 上的 LocalizedTMPText 管理。
|
||||
[HideInInspector] public List<string> options = new List<string>();
|
||||
public CustomDropdown dropdown;
|
||||
public int selectedIndex;
|
||||
public string selectedOption;
|
||||
|
||||
public void SetUp(int initialValue, List<string> options, string title = "", bool preservePrefabTitle = false)
|
||||
|
||||
private bool _usesLocalizedOptions;
|
||||
private bool _isValueChangeListenerRegistered;
|
||||
private Coroutine _bindGeneratedItemsCoroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 使用代码提供的 String Table 与 Entry Key 初始化静态枚举选项。
|
||||
/// <paramref name="entryKeys"/> 的顺序即设置枚举的索引顺序,必须保持稳定。
|
||||
/// 标题和说明仍由其 TMP 对象上的 <see cref="LocalizedTMPText"/> 独立管理。
|
||||
/// </summary>
|
||||
public void SetUpLocalized(int initialValue, string tableCollection, params string[] entryKeys)
|
||||
{
|
||||
base.SetUp(title, preservePrefabTitle: preservePrefabTitle);
|
||||
|
||||
this.options = options;
|
||||
this.dropdown.items = new List<CustomDropdown.Item>();
|
||||
foreach (string option in options)
|
||||
base.SetUp();
|
||||
|
||||
ClearLocalizedBindings();
|
||||
ClearLocalizedOptionDefinitions();
|
||||
_usesLocalizedOptions = true;
|
||||
|
||||
foreach (string entryKey in entryKeys)
|
||||
{
|
||||
dropdown.items.Add(new CustomDropdown.Item { itemName = option });
|
||||
_localizedOptions.Add(new LocalizedString(tableCollection, entryKey));
|
||||
}
|
||||
|
||||
dropdown.onValueChanged.AddListener(value =>
|
||||
{
|
||||
SetValue(value);
|
||||
updateValueAction?.Invoke();
|
||||
});
|
||||
|
||||
|
||||
BuildLocalizedOptionPlaceholders();
|
||||
EnsureValueChangeListener();
|
||||
SetValue(initialValue);
|
||||
ApplyOptionsToDropdown();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用运行时生成的纯文本选项初始化下拉框。
|
||||
/// 仅用于分辨率、语言自称等不应随当前 Locale 再次翻译的内容。
|
||||
/// </summary>
|
||||
public void SetUpRuntime(int initialValue, IReadOnlyList<string> runtimeOptions)
|
||||
{
|
||||
base.SetUp();
|
||||
|
||||
ClearLocalizedBindings();
|
||||
ClearLocalizedOptionDefinitions();
|
||||
_usesLocalizedOptions = false;
|
||||
options = runtimeOptions == null ? new List<string>() : new List<string>(runtimeOptions);
|
||||
EnsureValueChangeListener();
|
||||
SetValue(initialValue);
|
||||
ApplyOptionsToDropdown();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_usesLocalizedOptions)
|
||||
{
|
||||
QueueGeneratedItemBinding();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
StopGeneratedItemBinding();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
StopGeneratedItemBinding();
|
||||
ClearLocalizedOptionDefinitions();
|
||||
}
|
||||
|
||||
public int GetOptionIndex(string option)
|
||||
{
|
||||
return options.IndexOf(option);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 只更新当前选择状态,不调用 <see cref="updateValueAction"/>。
|
||||
/// 因此语言切换和 Dropdown 列表重建都不会重新应用玩家的设置。
|
||||
/// </summary>
|
||||
public void SetValue(int index)
|
||||
{
|
||||
selectedIndex = index;
|
||||
if (options == null || options.Count == 0)
|
||||
{
|
||||
selectedIndex = 0;
|
||||
selectedOption = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
selectedIndex = Mathf.Clamp(index, 0, options.Count - 1);
|
||||
selectedOption = options[selectedIndex];
|
||||
dropdown.selectedItemIndex = selectedIndex;
|
||||
dropdown.UpdateItemLayout();
|
||||
|
||||
if (_usesLocalizedOptions)
|
||||
{
|
||||
BindSelectedLocalizedText();
|
||||
}
|
||||
else if (dropdown.selectedText != null)
|
||||
{
|
||||
dropdown.selectedText.text = selectedOption;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureValueChangeListener()
|
||||
{
|
||||
if (_isValueChangeListenerRegistered)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dropdown.onValueChanged.AddListener(OnDropdownValueChanged);
|
||||
_isValueChangeListenerRegistered = true;
|
||||
}
|
||||
|
||||
private void OnDropdownValueChanged(int index)
|
||||
{
|
||||
SetValue(index);
|
||||
updateValueAction?.Invoke();
|
||||
}
|
||||
|
||||
private void BuildLocalizedOptionPlaceholders()
|
||||
{
|
||||
options = new List<string>(_localizedOptions.Count);
|
||||
for (int index = 0; index < _localizedOptions.Count; index++)
|
||||
{
|
||||
// Michsky 需要 itemName 创建 Item;真正可见的内容会在创建完成后被 LocalizedTMPText 覆盖。
|
||||
options.Add($"LocalizedOption_{index}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyOptionsToDropdown()
|
||||
{
|
||||
if (options == null || options.Count == 0)
|
||||
{
|
||||
dropdown.items = new List<CustomDropdown.Item>();
|
||||
return;
|
||||
}
|
||||
|
||||
SetValue(selectedIndex);
|
||||
dropdown.items = new List<CustomDropdown.Item>(options.Count);
|
||||
foreach (string option in options)
|
||||
{
|
||||
dropdown.items.Add(new CustomDropdown.Item { itemName = option });
|
||||
}
|
||||
|
||||
if (dropdown.gameObject.activeInHierarchy)
|
||||
{
|
||||
if (dropdown.isOn)
|
||||
{
|
||||
dropdown.Animate();
|
||||
}
|
||||
|
||||
dropdown.SetupDropdown();
|
||||
}
|
||||
else
|
||||
{
|
||||
dropdown.UpdateItemLayout();
|
||||
}
|
||||
|
||||
if (_usesLocalizedOptions)
|
||||
{
|
||||
QueueGeneratedItemBinding();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待一帧,确保 Michsky 已销毁旧 Item 并完成当前批次的实例化,再为每个 Item 绑定引用。
|
||||
/// 这样不会保留已销毁 Item 的语言监听,也避免把引用绑定到上一批列表对象。
|
||||
/// </summary>
|
||||
private void QueueGeneratedItemBinding()
|
||||
{
|
||||
if (!isActiveAndEnabled || _bindGeneratedItemsCoroutine != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_bindGeneratedItemsCoroutine = StartCoroutine(BindGeneratedItemsNextFrame());
|
||||
}
|
||||
|
||||
private IEnumerator BindGeneratedItemsNextFrame()
|
||||
{
|
||||
yield return null;
|
||||
_bindGeneratedItemsCoroutine = null;
|
||||
BindSelectedLocalizedText();
|
||||
|
||||
if (dropdown.itemParent == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
int itemCount = Mathf.Min(dropdown.itemParent.childCount, _localizedOptions.Count);
|
||||
for (int index = 0; index < itemCount; index++)
|
||||
{
|
||||
TMP_Text itemText = dropdown.itemParent.GetChild(index).GetComponentInChildren<TMP_Text>(true);
|
||||
BindLocalizedText(itemText, _localizedOptions[index]);
|
||||
}
|
||||
}
|
||||
|
||||
private void BindSelectedLocalizedText()
|
||||
{
|
||||
if (!_usesLocalizedOptions || _localizedOptions.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int optionIndex = Mathf.Clamp(selectedIndex, 0, _localizedOptions.Count - 1);
|
||||
BindLocalizedText(dropdown.selectedText, _localizedOptions[optionIndex]);
|
||||
}
|
||||
|
||||
private static void BindLocalizedText(TMP_Text text, LocalizedString reference)
|
||||
{
|
||||
if (text == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LocalizedTMPText localizedText = text.GetComponent<LocalizedTMPText>();
|
||||
if (localizedText == null)
|
||||
{
|
||||
localizedText = text.gameObject.AddComponent<LocalizedTMPText>();
|
||||
}
|
||||
|
||||
localizedText.SetStringReference(reference);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将本 Dropdown 先前动态附加的本地化引用清空。
|
||||
/// 同一控件改用运行时文本列表时必须执行此步骤,否则旧 Locale 回调会覆盖新的原始文本。
|
||||
/// </summary>
|
||||
private void ClearLocalizedBindings()
|
||||
{
|
||||
ClearLocalizedBinding(dropdown.selectedText);
|
||||
|
||||
if (dropdown.itemParent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Transform item in dropdown.itemParent)
|
||||
{
|
||||
ClearLocalizedBinding(item.GetComponentInChildren<TMP_Text>(true));
|
||||
}
|
||||
}
|
||||
|
||||
private static void ClearLocalizedBinding(TMP_Text text)
|
||||
{
|
||||
LocalizedTMPText localizedText = text == null ? null : text.GetComponent<LocalizedTMPText>();
|
||||
if (localizedText != null)
|
||||
{
|
||||
localizedText.SetStringReference(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearLocalizedOptionDefinitions()
|
||||
{
|
||||
// LocalizedString 的订阅由每个 LocalizedTMPText 持有;ClearLocalizedBindings 已经解除它们。
|
||||
// 当前 Unity Localization 版本没有公开的 LocalizedString.Dispose,因此清空运行时列表即可。
|
||||
_localizedOptions.Clear();
|
||||
}
|
||||
|
||||
private void StopGeneratedItemBinding()
|
||||
{
|
||||
if (_bindGeneratedItemsCoroutine == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StopCoroutine(_bindGeneratedItemsCoroutine);
|
||||
_bindGeneratedItemsCoroutine = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,22 @@ namespace Ichni.UI
|
||||
public TMP_Text keyText;
|
||||
public GameObject waitingForInputCover;
|
||||
|
||||
public void SetUp(string title, string subtitle, string actionName, int bindingIndex)
|
||||
private bool _isClickListenerRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化重绑定行为。标题和说明由 Prefab 上的 LocalizedTMPText 配置;
|
||||
/// <paramref name="actionName"/> 仅用于定位 Input System Action,不是本地化 Key。
|
||||
/// </summary>
|
||||
public void SetUp(string actionName, int bindingIndex)
|
||||
{
|
||||
base.SetUp(title, subtitle);
|
||||
base.SetUp();
|
||||
if (_isClickListenerRegistered)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
button.onClick.AddListener(() => StartRebinding(actionName, bindingIndex));
|
||||
_isClickListenerRegistered = true;
|
||||
}
|
||||
|
||||
// 开始重绑定流程
|
||||
@@ -82,4 +94,4 @@ namespace Ichni.UI
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using I2.Loc;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
@@ -10,9 +7,6 @@ namespace Ichni.UI
|
||||
{
|
||||
public abstract class SettingsUIElementBase : MonoBehaviour
|
||||
{
|
||||
public string title;
|
||||
public string subTitle;
|
||||
|
||||
public Image titleBackground;
|
||||
public TMP_Text titleText;
|
||||
public TMP_Text subTitleText;
|
||||
@@ -21,52 +15,22 @@ namespace Ichni.UI
|
||||
|
||||
/// <summary>
|
||||
/// 初始化设置控件的标题区域。
|
||||
/// <paramref name="preservePrefabTitle"/> 用于已经由 Unity Localization 直接驱动标题的控件:
|
||||
/// 运行时代码不会覆盖或隐藏 Prefab 中的本地化标题。
|
||||
/// <para>
|
||||
/// 标题与说明不再由此基础类写入 I2 Key。请在对应 <see cref="TMP_Text"/> 上配置
|
||||
/// <c>LocalizedTMPText</c>,使静态文案与控件逻辑完全解耦。
|
||||
/// </para>
|
||||
/// <paramref name="preservePrefabTitle"/> 为 true 时保留 Prefab 的标题区域;这是设置页面的默认行为。
|
||||
/// </summary>
|
||||
public virtual void SetUp(string title = "", string subTitle = "", bool preservePrefabTitle = false)
|
||||
public virtual void SetUp(bool preservePrefabTitle = true)
|
||||
{
|
||||
if (preservePrefabTitle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (title != "")
|
||||
{
|
||||
SetTitle(title, subTitle);
|
||||
}
|
||||
else
|
||||
{
|
||||
titleBackground.gameObject.SetActive(false);
|
||||
titleText.gameObject.SetActive(false);
|
||||
subTitleText.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTitle(string title, string subTitle = "")
|
||||
{
|
||||
this.title = title;
|
||||
this.subTitle = subTitle;
|
||||
titleText.gameObject.SetActive(true);
|
||||
var titleLoc = titleText.GetComponent<Localize>();
|
||||
if (titleLoc != null)
|
||||
titleLoc.SetTerm(title);
|
||||
else
|
||||
titleText.text = title;
|
||||
|
||||
if (subTitle != "")
|
||||
{
|
||||
subTitleText.gameObject.SetActive(true);
|
||||
var subLoc = subTitleText.GetComponent<Localize>();
|
||||
if (subLoc != null)
|
||||
subLoc.SetTerm(subTitle);
|
||||
else
|
||||
subTitleText.text = subTitle;
|
||||
}
|
||||
else
|
||||
{
|
||||
subTitleText.gameObject.SetActive(false);
|
||||
}
|
||||
titleBackground.gameObject.SetActive(false);
|
||||
titleText.gameObject.SetActive(false);
|
||||
subTitleText.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,20 +6,27 @@ namespace Ichni.UI
|
||||
{
|
||||
public bool value;
|
||||
public SwitchManager switchManager;
|
||||
private bool _isValueChangeListenerRegistered;
|
||||
|
||||
public void SetUp(bool initialValue, string title = "", bool preservePrefabTitle = false)
|
||||
public void SetUp(bool initialValue, bool preservePrefabTitle = true)
|
||||
{
|
||||
base.SetUp(title, preservePrefabTitle: preservePrefabTitle);
|
||||
base.SetUp(preservePrefabTitle);
|
||||
|
||||
switchManager.onValueChanged.AddListener(isOn =>
|
||||
if (!_isValueChangeListenerRegistered)
|
||||
{
|
||||
value = isOn;
|
||||
updateValueAction?.Invoke();
|
||||
});
|
||||
switchManager.onValueChanged.AddListener(OnSwitchValueChanged);
|
||||
_isValueChangeListenerRegistered = true;
|
||||
}
|
||||
|
||||
SetValue(initialValue);
|
||||
}
|
||||
|
||||
private void OnSwitchValueChanged(bool isOn)
|
||||
{
|
||||
value = isOn;
|
||||
updateValueAction?.Invoke();
|
||||
}
|
||||
|
||||
public void SetValue(bool value)
|
||||
{
|
||||
this.value = value;
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using I2.Loc;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
@@ -10,25 +6,28 @@ namespace Ichni.UI
|
||||
public class TextButton : SettingsUIElementBase
|
||||
{
|
||||
public Button button;
|
||||
public TMP_Text buttonText;
|
||||
public void SetUp(string title, string subTitle, string textContent)
|
||||
private bool _isClickListenerRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化按钮逻辑。标题、说明及按钮文字均由 Prefab 上的 LocalizedTMPText 配置,
|
||||
/// 不再把字符串当作 I2 的 Term 传入。
|
||||
/// </summary>
|
||||
public void SetUp()
|
||||
{
|
||||
base.SetUp(title, subTitle);
|
||||
|
||||
var localize = buttonText.GetComponent<Localize>();
|
||||
if (localize != null)
|
||||
base.SetUp();
|
||||
|
||||
if (_isClickListenerRegistered)
|
||||
{
|
||||
localize.SetTerm(textContent);
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonText.text = textContent;
|
||||
return;
|
||||
}
|
||||
|
||||
button.onClick.AddListener(() =>
|
||||
{
|
||||
updateValueAction?.Invoke();
|
||||
});
|
||||
button.onClick.AddListener(InvokeUpdateValueAction);
|
||||
_isClickListenerRegistered = true;
|
||||
}
|
||||
|
||||
private void InvokeUpdateValueAction()
|
||||
{
|
||||
updateValueAction?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using DG.Tweening;
|
||||
using I2.Loc;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
@@ -27,13 +26,13 @@ namespace Ichni.UI
|
||||
public string suffix;
|
||||
public bool showPositiveMark;
|
||||
|
||||
public void SetUp(int initialValue, int step, string title = "")
|
||||
public void SetUp(int initialValue, int step)
|
||||
{
|
||||
intStep = step;
|
||||
intMin = int.MinValue;
|
||||
intMax = int.MaxValue;
|
||||
|
||||
base.SetUp(title);
|
||||
base.SetUp();
|
||||
|
||||
increaseButton.SetUpButton(IncreaseValue, IncreaseValue);
|
||||
decreaseButton.SetUpButton(DecreaseValue, DecreaseValue);
|
||||
@@ -94,7 +93,7 @@ namespace Ichni.UI
|
||||
{
|
||||
|
||||
valueText.text = prefix + (showPositiveMark && intValue > 0 ? "+" : "") + intValue.ToString() + suffix;
|
||||
var target = new Vector2((float)(intValue - intMin) / (intMax - intMin) * 660, 20);
|
||||
var target = new Vector2((float)(intValue - intMin) / (intMax - intMin) * 560, 20);
|
||||
barImage.rectTransform.sizeDelta = target;
|
||||
updateValueAction?.Invoke();
|
||||
}
|
||||
@@ -106,7 +105,7 @@ namespace Ichni.UI
|
||||
if (LastValue == intValue) return;
|
||||
tween?.Complete();
|
||||
valueText.text = prefix + (showPositiveMark && intValue > 0 ? "+" : "") + intValue.ToString() + suffix;
|
||||
var target = new Vector2((float)(intValue - intMin) / (intMax - intMin) * 660, 20);
|
||||
var target = new Vector2((float)(intValue - intMin) / (intMax - intMin) * 560, 20);
|
||||
tween = barImage.rectTransform.DOSizeDelta(target, 0.2f).SetEase(Ease.OutExpo).Play();
|
||||
LastValue = intValue;
|
||||
updateValueAction?.Invoke();
|
||||
@@ -117,4 +116,4 @@ namespace Ichni.UI
|
||||
return intValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Ichni.Menu
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
mainVolumeModifier.SetUp(gameSettings.masterVolume, 10, "Menu UI/Settings_Master_Volume");
|
||||
mainVolumeModifier.SetUp(gameSettings.masterVolume, 10);
|
||||
mainVolumeModifier.SetMinMax(0, 100);
|
||||
mainVolumeModifier.updateValueAction = () =>
|
||||
{
|
||||
@@ -23,7 +23,7 @@ namespace Ichni.Menu
|
||||
SettingsManager.instance.ApplyAudioSettings();
|
||||
};
|
||||
|
||||
musicVolumeModifier.SetUp(gameSettings.musicVolume, 10, "Menu UI/Settings_Music_Volume");
|
||||
musicVolumeModifier.SetUp(gameSettings.musicVolume, 10);
|
||||
musicVolumeModifier.SetMinMax(0, 100);
|
||||
musicVolumeModifier.updateValueAction = () =>
|
||||
{
|
||||
@@ -31,7 +31,7 @@ namespace Ichni.Menu
|
||||
SettingsManager.instance.ApplyAudioSettings();
|
||||
};
|
||||
|
||||
sfxVolumeModifier.SetUp(gameSettings.soundEffectVolume, 10, "Menu UI/Settings_SFX_Volume");
|
||||
sfxVolumeModifier.SetUp(gameSettings.soundEffectVolume, 10);
|
||||
sfxVolumeModifier.SetMinMax(0, 100);
|
||||
sfxVolumeModifier.updateValueAction = () =>
|
||||
{
|
||||
@@ -39,7 +39,7 @@ namespace Ichni.Menu
|
||||
SettingsManager.instance.ApplyAudioSettings();
|
||||
};
|
||||
|
||||
uiVolumeModifier.SetUp(gameSettings.uiVolume, 10, "Menu UI/Settings_UI_Volume");
|
||||
uiVolumeModifier.SetUp(gameSettings.uiVolume, 10);
|
||||
uiVolumeModifier.SetMinMax(0, 100);
|
||||
uiVolumeModifier.updateValueAction = () =>
|
||||
{
|
||||
@@ -47,8 +47,7 @@ namespace Ichni.Menu
|
||||
SettingsManager.instance.ApplyAudioSettings();
|
||||
};
|
||||
|
||||
inGameAudioEffectsSwitch.SetUp(gameSettings.enableInGameAudioEffects,
|
||||
"Menu UI/Settings_InGame_Audio_Effects");
|
||||
inGameAudioEffectsSwitch.SetUp(gameSettings.enableInGameAudioEffects);
|
||||
inGameAudioEffectsSwitch.updateValueAction = () =>
|
||||
{
|
||||
gameSettings.enableInGameAudioEffects = inGameAudioEffectsSwitch.GetValue();
|
||||
|
||||
@@ -13,19 +13,19 @@ namespace Ichni.Menu
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
debugModeSwitch.SetUp(gameSettings.debugMode, "Menu UI/Settings_Debug_Mode");
|
||||
debugModeSwitch.SetUp(gameSettings.debugMode);
|
||||
debugModeSwitch.updateValueAction = () =>
|
||||
{
|
||||
gameSettings.debugMode = debugModeSwitch.GetValue();
|
||||
};
|
||||
|
||||
judgeTypeSwitch.SetUp(gameSettings.judgeType, "Menu UI/Settings_Judge_Type");
|
||||
judgeTypeSwitch.SetUp(gameSettings.judgeType);
|
||||
judgeTypeSwitch.updateValueAction = () =>
|
||||
{
|
||||
gameSettings.judgeType = judgeTypeSwitch.GetValue();
|
||||
};
|
||||
|
||||
autoPlaySwitch.SetUp(gameSettings.autoPlay, "Menu UI/Settings_Auto_Play");
|
||||
autoPlaySwitch.SetUp(gameSettings.autoPlay);
|
||||
autoPlaySwitch.updateValueAction = () =>
|
||||
{
|
||||
gameSettings.autoPlay = autoPlaySwitch.GetValue();
|
||||
@@ -39,4 +39,4 @@ namespace Ichni.Menu
|
||||
autoPlaySwitch.SetValue(gameSettings.autoPlay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Ichni.UI
|
||||
public Dropdown languageDropdown;
|
||||
public override void Initialize()
|
||||
{
|
||||
offsetEditorButton.SetUp("Menu UI/Settings_Offset_Editor", "", "Menu UI/Settings_Enter");
|
||||
offsetEditorButton.SetUp();
|
||||
offsetEditorButton.updateValueAction = () =>
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
@@ -19,7 +19,10 @@ namespace Ichni.UI
|
||||
MenuManager.instance.settingsUIPage.settingsWindowController.buttonsContainer.gameObject.SetActive(false);
|
||||
};
|
||||
|
||||
languageDropdown.SetUp(SettingsManager.instance.GetLanguageDropdownIndex(), MenuManager.instance.displayLanguageList, "Menu UI/Settings_Language");
|
||||
// 语言列表采用各语言的自称(例如日本語、한국어),不应随当前 Locale 再次翻译。
|
||||
languageDropdown.SetUpRuntime(
|
||||
SettingsManager.instance.GetLanguageDropdownIndex(),
|
||||
MenuManager.instance.displayLanguageList);
|
||||
languageDropdown.updateValueAction = () =>
|
||||
{
|
||||
SettingsManager.instance.SetLanguageByDropdownIndex(languageDropdown.selectedIndex);
|
||||
|
||||
@@ -8,32 +8,34 @@ namespace Ichni.Menu
|
||||
{
|
||||
public class GraphicsSettingsWindow : SettingsWindow
|
||||
{
|
||||
private static readonly List<string> QualityPresetOptions = new List<string>
|
||||
private const string UiTable = "UI";
|
||||
|
||||
private static readonly string[] QualityPresetKeys =
|
||||
{
|
||||
"Performant",
|
||||
"Balanced",
|
||||
"High Fidelity"
|
||||
"ui_settings_quality_performance",
|
||||
"ui_settings_quality_balanced",
|
||||
"ui_settings_quality_high_fidelity"
|
||||
};
|
||||
|
||||
private static readonly List<string> DisplayModeOptions = new List<string>
|
||||
private static readonly string[] BloomQualityKeys =
|
||||
{
|
||||
"Fullscreen",
|
||||
"Borderless Fullscreen",
|
||||
"Windowed"
|
||||
"ui_settings_bloom_off",
|
||||
"ui_settings_bloom_performance",
|
||||
"ui_settings_bloom_standard"
|
||||
};
|
||||
|
||||
private static readonly List<string> BloomQualityOptions = new List<string>
|
||||
private static readonly string[] VfxQualityKeys =
|
||||
{
|
||||
"Off",
|
||||
"Performance",
|
||||
"Standard"
|
||||
"ui_settings_vfx_low",
|
||||
"ui_settings_vfx_medium",
|
||||
"ui_settings_vfx_high"
|
||||
};
|
||||
|
||||
private static readonly List<string> VfxQualityOptions = new List<string>
|
||||
private static readonly string[] DisplayModeKeys =
|
||||
{
|
||||
"Low",
|
||||
"Medium",
|
||||
"High"
|
||||
"ui_settings_display_fullscreen",
|
||||
"ui_settings_display_borderless_fullscreen",
|
||||
"ui_settings_display_windowed"
|
||||
};
|
||||
|
||||
private readonly List<Vector2Int> _availableDisplayResolutions = new List<Vector2Int>();
|
||||
@@ -52,15 +54,14 @@ namespace Ichni.Menu
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
qualityPresetDropdown.SetUp((int)gameSettings.graphicsQualityPreset, QualityPresetOptions,
|
||||
"Menu UI/Settings_Quality_Preset");
|
||||
qualityPresetDropdown.SetUpLocalized((int)gameSettings.graphicsQualityPreset, UiTable, QualityPresetKeys);
|
||||
qualityPresetDropdown.updateValueAction = () =>
|
||||
{
|
||||
gameSettings.graphicsQualityPreset = (GraphicsQualityPreset)qualityPresetDropdown.selectedIndex;
|
||||
SettingsManager.instance.ApplyGraphicSettings();
|
||||
};
|
||||
|
||||
renderScaleModifier.SetUp(gameSettings.renderScaleLevel, 1, "Menu UI/Settings_Render_Scale");
|
||||
renderScaleModifier.SetUp(gameSettings.renderScaleLevel, 1);
|
||||
renderScaleModifier.SetMinMax(0, 5);
|
||||
renderScaleModifier.updateValueAction = () =>
|
||||
{
|
||||
@@ -68,7 +69,7 @@ namespace Ichni.Menu
|
||||
SettingsManager.instance.ApplyGraphicSettings();
|
||||
};
|
||||
|
||||
targetFrameModifier.SetUp(gameSettings.targetFrame, 30, "Menu UI/Settings_Target_Frame");
|
||||
targetFrameModifier.SetUp(gameSettings.targetFrame, 30);
|
||||
targetFrameModifier.SetMinMax(30, 120);
|
||||
targetFrameModifier.updateValueAction = () =>
|
||||
{
|
||||
@@ -119,21 +120,21 @@ namespace Ichni.Menu
|
||||
return;
|
||||
}
|
||||
|
||||
postProcessingSwitch.SetUp(gameSettings.enablePostProcessing, preservePrefabTitle: true);
|
||||
postProcessingSwitch.SetUp(gameSettings.enablePostProcessing);
|
||||
postProcessingSwitch.updateValueAction = () =>
|
||||
{
|
||||
gameSettings.enablePostProcessing = postProcessingSwitch.GetValue();
|
||||
SettingsManager.instance.ApplyPostProcessingSettings();
|
||||
};
|
||||
|
||||
bloomQualityDropdown.SetUp((int)gameSettings.bloomQuality, BloomQualityOptions, preservePrefabTitle: true);
|
||||
bloomQualityDropdown.SetUpLocalized((int)gameSettings.bloomQuality, UiTable, BloomQualityKeys);
|
||||
bloomQualityDropdown.updateValueAction = () =>
|
||||
{
|
||||
gameSettings.bloomQuality = (BloomQuality)bloomQualityDropdown.selectedIndex;
|
||||
SettingsManager.instance.ApplyPostProcessingSettings();
|
||||
};
|
||||
|
||||
vfxQualityDropdown.SetUp((int)gameSettings.vfxQuality, VfxQualityOptions, preservePrefabTitle: true);
|
||||
vfxQualityDropdown.SetUpLocalized((int)gameSettings.vfxQuality, UiTable, VfxQualityKeys);
|
||||
vfxQualityDropdown.updateValueAction = () =>
|
||||
{
|
||||
gameSettings.vfxQuality = (VfxQuality)vfxQualityDropdown.selectedIndex;
|
||||
@@ -145,23 +146,21 @@ namespace Ichni.Menu
|
||||
{
|
||||
BuildDisplayResolutionOptions();
|
||||
|
||||
vSyncSwitch.SetUp(gameSettings.vSyncEnabled, "Menu UI/Settings_VSync");
|
||||
vSyncSwitch.SetUp(gameSettings.vSyncEnabled);
|
||||
vSyncSwitch.updateValueAction = () =>
|
||||
{
|
||||
gameSettings.vSyncEnabled = vSyncSwitch.GetValue();
|
||||
SettingsManager.instance.ApplyDisplaySettings();
|
||||
};
|
||||
|
||||
displayModeDropdown.SetUp((int)gameSettings.displayMode, DisplayModeOptions,
|
||||
"Menu UI/Settings_Display_Mode");
|
||||
displayModeDropdown.SetUpLocalized((int)gameSettings.displayMode, UiTable, DisplayModeKeys);
|
||||
displayModeDropdown.updateValueAction = () =>
|
||||
{
|
||||
gameSettings.displayMode = (GraphicsDisplayMode)displayModeDropdown.selectedIndex;
|
||||
SettingsManager.instance.ApplyDisplaySettings();
|
||||
};
|
||||
|
||||
displayResolutionDropdown.SetUp(GetDisplayResolutionIndex(), _displayResolutionOptions,
|
||||
"Menu UI/Settings_Display_Resolution");
|
||||
displayResolutionDropdown.SetUpRuntime(GetDisplayResolutionIndex(), _displayResolutionOptions);
|
||||
displayResolutionDropdown.updateValueAction = () =>
|
||||
{
|
||||
Vector2Int resolution = _availableDisplayResolutions[displayResolutionDropdown.selectedIndex];
|
||||
|
||||
@@ -25,10 +25,10 @@ namespace Ichni.Menu
|
||||
foreach (RebindActionData data in rebindActions["Keyboard"])
|
||||
{
|
||||
// 在C# 8.0+ 中,可以使用静态匿名函数来避免闭包分配
|
||||
data.rebindButton.SetUp(data.title, data.subtitle, data.actionName, data.bindingIndex);
|
||||
data.rebindButton.SetUp(data.actionName, data.bindingIndex);
|
||||
}
|
||||
|
||||
resetButton.SetUp("Menu UI/Settings_ResetRebinding", "", "Menu UI/Settings_Confirm");
|
||||
resetButton.SetUp();
|
||||
resetButton.updateValueAction = ResetAllBindings;
|
||||
}
|
||||
|
||||
@@ -114,9 +114,6 @@ namespace Ichni.Menu
|
||||
[System.Serializable]
|
||||
public class RebindActionData
|
||||
{
|
||||
public string title;
|
||||
[FormerlySerializedAs("description")] public string subtitle;
|
||||
|
||||
[Tooltip("必须与Input Actions Asset中的Action名称完全匹配")]
|
||||
public string actionName;
|
||||
|
||||
@@ -132,4 +129,4 @@ namespace Ichni.Menu
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user