阶段性完成

This commit is contained in:
SoulliesOfficial
2026-07-25 13:27:53 -04:00
parent de70870682
commit a34461d31f
121 changed files with 7022 additions and 4111 deletions

View File

@@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using Ichni.Localization;
using Ichni.Menu.UI;
using TMPro;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;
using UnityEngine.UI;
namespace Ichni.Story.UI
@@ -39,15 +36,13 @@ namespace Ichni.Story.UI
[Tooltip("自动在左右各扩展一个刻度周期,保证刻度条循环平移时不会从 Timeline 边缘露出空白。")]
public bool expandTickRepeatRect = true;
[Header("Localization")]
[Tooltip("StoryTimeline 的 Unity Localization String Table。Marker.labelKey 在该 Table 中查找。")]
public TableReference localizationTable;
private sealed class RuntimeMarker
{
public StoryTimelineMarkerDefinition definition;
public StoryBlockView anchorView;
public StoryTimelineMarkerView view;
public string localizedLabel;
public LocalizedString.ChangeHandler localizedLabelChanged;
}
private readonly List<RuntimeMarker> _markers = new List<RuntimeMarker>();
@@ -69,7 +64,6 @@ namespace Ichni.Story.UI
private void OnEnable()
{
LocalizationSettings.SelectedLocaleChanged += OnSelectedLocaleChanged;
SubscribeToStoryScrollRect();
// StoryPage 从对话界面返回后Canvas 与 StoryTree 会在同一帧重新启用。
@@ -79,7 +73,6 @@ namespace Ichni.Story.UI
private void OnDisable()
{
LocalizationSettings.SelectedLocaleChanged -= OnSelectedLocaleChanged;
UnsubscribeFromStoryScrollRect();
}
@@ -124,6 +117,7 @@ namespace Ichni.Story.UI
foreach (RuntimeMarker marker in _markers)
{
UnbindLocalizedLabel(marker);
if (marker.view != null)
{
Destroy(marker.view.gameObject);
@@ -356,16 +350,19 @@ namespace Ichni.Story.UI
StoryTimelineMarkerView view = Instantiate(markerPrefab, markerContainer);
view.name = $"StoryTimelineMarker_{marker.markerId}";
view.SetLabel(GetLocalizedLabel(marker.labelKey));
RuntimeMarker runtimeMarker = new RuntimeMarker
{
definition = marker,
anchorView = anchorView,
view = view
view = view,
localizedLabel = marker.markerId
};
view.BindClick(() => RequestRollback(runtimeMarker));
_markers.Add(runtimeMarker);
// LocalizedString 首次读取是异步的;先显示稳定 Marker ID避免 Prefab 默认文字短暂闪现。
view.SetLabel(runtimeMarker.localizedLabel);
BindLocalizedLabel(runtimeMarker);
return true;
}
@@ -388,48 +385,52 @@ namespace Ichni.Story.UI
return;
}
string markerLabel = GetLocalizedLabel(marker.definition.labelKey);
messageUIPage.ShowSelectionBox(
"重开本章节",
$"将重置“{markerLabel}”所在列及其之后的剧情选择与进度。歌曲成绩、解锁内容、设置和其它章节不会受到影响。",
new List<SelectionBoxOption>
string markerLabel = marker.localizedLabel;
messageUIPage.ShowLocalizedSelectionBox(
new LocalizedPopupText("Message", "system_rollback_chapter_title"),
new LocalizedPopupText("Message", "system_rollback_chapter_content", markerLabel),
new List<LocalizedSelectionBoxOption>
{
new SelectionBoxOption("重开此时间点", () => _treeController.TryRollbackToMarker(marker.definition.markerId)),
new SelectionBoxOption("取消", () => { })
new LocalizedSelectionBoxOption(
new LocalizedPopupText("Message", "system_rollback_confirm"),
() => _treeController.TryRollbackToMarker(marker.definition.markerId)),
new LocalizedSelectionBoxOption(
new LocalizedPopupText("UI", "ui_common_cancel"),
() => { })
});
}
/// <summary>
/// 使用 StoryTimeline 指定的 Unity Localization String Table 解析 Label Key。
/// String Table 或条目尚未配置时回退显示 Key确保开发阶段仍能从 UI 上定位缺失翻译
/// 订阅 Marker 自身的 LocalizedString。LocalizedString 会负责首次异步读取与 Locale
/// 切换后的自动刷新Timeline 不再维护独立的 Table / Key 解析逻辑
/// </summary>
private string GetLocalizedLabel(string labelKey)
private static void BindLocalizedLabel(RuntimeMarker marker)
{
if (string.IsNullOrEmpty(labelKey))
if (marker?.definition?.label == null || marker.definition.label.IsEmpty)
{
return string.Empty;
marker?.view?.SetLabel(marker?.localizedLabel ?? string.Empty);
return;
}
return LocalizationTextService.Resolve(localizationTable, labelKey, labelKey);
marker.localizedLabelChanged = localizedText =>
{
marker.localizedLabel = string.IsNullOrEmpty(localizedText)
? marker.definition.markerId
: localizedText;
if (marker.view != null)
marker.view.SetLabel(marker.localizedLabel);
};
marker.definition.label.StringChanged += marker.localizedLabelChanged;
}
private async void OnSelectedLocaleChanged(Locale _)
private static void UnbindLocalizedLabel(RuntimeMarker marker)
{
foreach (RuntimeMarker marker in _markers)
{
if (string.IsNullOrEmpty(marker.definition.labelKey)) continue;
if (marker?.definition?.label == null || marker.localizedLabelChanged == null)
return;
string localizedLabel = await LocalizationTextService.ResolveAsync(
localizationTable,
marker.definition.labelKey,
marker.definition.labelKey);
// 异步等待结束后,防范对象在此期间被销毁。
if (marker.view != null)
{
marker.view.SetLabel(localizedLabel);
}
}
marker.definition.label.StringChanged -= marker.localizedLabelChanged;
marker.localizedLabelChanged = null;
}
/// <summary>