feat(huoshan): 接入表达界面序列帧与本地化文字
This commit is contained in:
@@ -32,7 +32,14 @@ namespace AibisDream.FixSystem
|
||||
// 切换到深度表达相机
|
||||
yield return CameraKit.Instance.SwitchCamera(CameraEnum.ExpressionDeep);
|
||||
yield return UIManager.Instance.GetPanel<ScreenTransitionPanel>().FadeOutAsync(0.5f);
|
||||
yield return TimelineCenter.Instance.PlayTimelineAsync("火山表达模块/火山表达深入");
|
||||
if (expressionManager.HasScreenPresentation)
|
||||
{
|
||||
yield return expressionManager.PlayScreenDeepAndWait();
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return TimelineCenter.Instance.PlayTimelineAsync("火山表达模块/火山表达深入");
|
||||
}
|
||||
|
||||
BubbleSlotKit.Instance.LoadBubbles(BubbleSlotEnum.Expression);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace AibisDream
|
||||
[SerializeField] private LanguageParticleManager particleManager;
|
||||
[SerializeField] private GameObject expressionView;
|
||||
[SerializeField] private LogReleasePresentationController logReleasePresentation;
|
||||
[SerializeField] private ExpressionScreenPresentationController screenPresentation;
|
||||
|
||||
[Header("默认配置")]
|
||||
[SerializeField] private List<string> defaultAnxietyPhrases = new List<string>
|
||||
@@ -111,9 +112,15 @@ namespace AibisDream
|
||||
logReleasePresentation = GetComponent<LogReleasePresentationController>();
|
||||
}
|
||||
|
||||
if (screenPresentation == null)
|
||||
{
|
||||
screenPresentation = GetComponent<ExpressionScreenPresentationController>();
|
||||
}
|
||||
|
||||
if (particleManager != null)
|
||||
{
|
||||
particleManager.SetPresentationController(logReleasePresentation);
|
||||
particleManager.SetScreenPresentationController(screenPresentation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +130,7 @@ namespace AibisDream
|
||||
/// </summary>
|
||||
public void OpenView()
|
||||
{
|
||||
screenPresentation?.ResetImmediate();
|
||||
// 先设置所有初始状态再打开
|
||||
if (particleManager != null)
|
||||
{
|
||||
@@ -230,6 +238,16 @@ namespace AibisDream
|
||||
yield return particleManager.WaitUntilExpressionFlowReady();
|
||||
}
|
||||
|
||||
internal IEnumerator PlayScreenDeepAndWait()
|
||||
{
|
||||
if (screenPresentation == null)
|
||||
yield break;
|
||||
|
||||
yield return screenPresentation.PlayDeepAndWait();
|
||||
}
|
||||
|
||||
internal bool HasScreenPresentation => screenPresentation != null;
|
||||
|
||||
/// <summary>
|
||||
/// 从聚焦保持态触发稳定化(概率递增 → 文字锁定 → 排列)
|
||||
/// </summary>
|
||||
@@ -256,6 +274,7 @@ namespace AibisDream
|
||||
/// </summary>
|
||||
public void CloseView()
|
||||
{
|
||||
screenPresentation?.ResetImmediate();
|
||||
ResetScreenGlitchState();
|
||||
ApplyScreenGlitchProperties();
|
||||
|
||||
|
||||
@@ -0,0 +1,404 @@
|
||||
using System.Collections;
|
||||
using AibisDream.Utility;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Localization.Components;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace AibisDream.MiniGame.Language
|
||||
{
|
||||
/// <summary>
|
||||
/// Drives the full-frame Huoshan expression screen while keeping readable text in TMP.
|
||||
/// The sprite sheet uses the original 384x216 Aseprite timing and the existing world-space
|
||||
/// Canvas remains the source of truth for interaction and dynamic language particles.
|
||||
/// </summary>
|
||||
public sealed class ExpressionScreenPresentationController : MonoBehaviour
|
||||
{
|
||||
public enum ScreenState
|
||||
{
|
||||
Reset,
|
||||
Deep,
|
||||
Panel,
|
||||
Output,
|
||||
Floating,
|
||||
Click,
|
||||
FocusHidden
|
||||
}
|
||||
|
||||
private const int DeepFirst = 0;
|
||||
private const int DeepLast = 14;
|
||||
private const int UiFlashFirst = 15;
|
||||
private const int UiFlashLast = 29;
|
||||
private const int StatusRevealFrame = 21;
|
||||
private const int RingFirst = 30;
|
||||
private const int RingLast = 40;
|
||||
private const int OutputFirst = 41;
|
||||
private const int OutputLast = 47;
|
||||
private const int OutputTextRevealFrame = 45;
|
||||
private const int FloatingFrame = 48;
|
||||
private const int ClickFirst = 49;
|
||||
private const int ClickLast = 52;
|
||||
|
||||
private const string ObjectiveLocalizationKey = "huoshan_expression_objective";
|
||||
private const string OutputLocalizationKey = "huoshan_expression_output";
|
||||
|
||||
[Header("Frame Animation")]
|
||||
[SerializeField] private SpriteRenderer screenRenderer;
|
||||
[SerializeField] private Sprite[] frames = new Sprite[53];
|
||||
|
||||
[Header("Existing TMP / Interaction")]
|
||||
[SerializeField] private TMP_Text integrationProgressText;
|
||||
[SerializeField] private TMP_Text interferenceCountText;
|
||||
[SerializeField] private Button outputButton;
|
||||
[SerializeField] private TMP_FontAsset screenFont;
|
||||
|
||||
[Header("Objective TMP (384x216 reference)")]
|
||||
[SerializeField] private Vector2 objectivePosition = new Vector2(-8.18f, -4.08f);
|
||||
[SerializeField] private Vector2 objectiveSize = new Vector2(7.4f, 0.95f);
|
||||
[SerializeField] private float objectiveFontSize = 0.28f;
|
||||
[SerializeField] private Color objectiveColor = new Color(0.78f, 0.91f, 1f, 1f);
|
||||
|
||||
private int playbackGeneration;
|
||||
private TMP_Text objectiveText;
|
||||
private TMP_Text outputText;
|
||||
private LocalizeStringEvent objectiveLocalization;
|
||||
private LocalizeStringEvent outputLocalization;
|
||||
private Material objectiveMaterial;
|
||||
private ScreenState state = ScreenState.Reset;
|
||||
|
||||
public ScreenState State => state;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
ResolveSceneReferences();
|
||||
ConfigureExistingUiAsTextOnly();
|
||||
CreateObjectiveText();
|
||||
BindStaticLocalization();
|
||||
ResetImmediate();
|
||||
}
|
||||
|
||||
private void ResolveSceneReferences()
|
||||
{
|
||||
if (screenRenderer == null)
|
||||
{
|
||||
SpriteRenderer[] renderers = GetComponentsInChildren<SpriteRenderer>(true);
|
||||
foreach (SpriteRenderer candidate in renderers)
|
||||
{
|
||||
if (candidate != null && candidate.gameObject.name == "火山释放log界面")
|
||||
{
|
||||
screenRenderer = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (outputButton != null)
|
||||
{
|
||||
outputText = outputButton.GetComponentInChildren<TMP_Text>(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureExistingUiAsTextOnly()
|
||||
{
|
||||
MakeParentImageTransparent(integrationProgressText);
|
||||
MakeParentImageTransparent(interferenceCountText);
|
||||
|
||||
if (integrationProgressText != null)
|
||||
integrationProgressText.raycastTarget = false;
|
||||
if (interferenceCountText != null)
|
||||
interferenceCountText.raycastTarget = false;
|
||||
|
||||
if (outputButton != null && outputButton.targetGraphic != null)
|
||||
{
|
||||
Color color = outputButton.targetGraphic.color;
|
||||
color.a = 0f;
|
||||
outputButton.targetGraphic.color = color;
|
||||
outputButton.targetGraphic.raycastTarget = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void MakeParentImageTransparent(TMP_Text text)
|
||||
{
|
||||
if (text == null || text.transform.parent == null)
|
||||
return;
|
||||
|
||||
Image image = text.transform.parent.GetComponent<Image>();
|
||||
if (image == null)
|
||||
return;
|
||||
|
||||
Color color = image.color;
|
||||
color.a = 0f;
|
||||
image.color = color;
|
||||
image.raycastTarget = false;
|
||||
}
|
||||
|
||||
private void CreateObjectiveText()
|
||||
{
|
||||
Canvas canvas = integrationProgressText != null
|
||||
? integrationProgressText.GetComponentInParent<Canvas>()
|
||||
: GetComponentInChildren<Canvas>(true);
|
||||
if (canvas == null)
|
||||
{
|
||||
Debug.LogError("[ExpressionScreenPresentation] ExpressView 下未找到 World Space Canvas,任务描述无法创建。");
|
||||
return;
|
||||
}
|
||||
|
||||
Transform existing = canvas.transform.Find("Expression Objective (TMP)");
|
||||
if (existing != null)
|
||||
{
|
||||
objectiveText = existing.GetComponent<TMP_Text>();
|
||||
return;
|
||||
}
|
||||
|
||||
var objectiveObject = new GameObject(
|
||||
"Expression Objective (TMP)",
|
||||
typeof(RectTransform),
|
||||
typeof(CanvasRenderer),
|
||||
typeof(TextMeshProUGUI));
|
||||
objectiveObject.layer = canvas.gameObject.layer;
|
||||
|
||||
RectTransform rect = objectiveObject.GetComponent<RectTransform>();
|
||||
rect.SetParent(canvas.transform, false);
|
||||
rect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
rect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rect.pivot = new Vector2(0f, 0.5f);
|
||||
rect.anchoredPosition = objectivePosition;
|
||||
rect.sizeDelta = objectiveSize;
|
||||
rect.SetAsFirstSibling();
|
||||
|
||||
var text = objectiveObject.GetComponent<TextMeshProUGUI>();
|
||||
text.font = screenFont;
|
||||
if (integrationProgressText != null && integrationProgressText.fontSharedMaterial != null)
|
||||
{
|
||||
objectiveMaterial = new Material(integrationProgressText.fontSharedMaterial)
|
||||
{
|
||||
name = "Huoshan Expression Objective TMP (Runtime)"
|
||||
};
|
||||
text.fontSharedMaterial = objectiveMaterial;
|
||||
}
|
||||
text.fontSize = objectiveFontSize;
|
||||
text.fontStyle = FontStyles.Bold;
|
||||
text.color = objectiveColor;
|
||||
text.alignment = TextAlignmentOptions.Left;
|
||||
text.enableWordWrapping = true;
|
||||
text.overflowMode = TextOverflowModes.Truncate;
|
||||
text.raycastTarget = false;
|
||||
objectiveText = text;
|
||||
}
|
||||
|
||||
private void BindStaticLocalization()
|
||||
{
|
||||
objectiveLocalization = BindLocalizedText(objectiveText, ObjectiveLocalizationKey);
|
||||
outputLocalization = BindLocalizedText(outputText, OutputLocalizationKey);
|
||||
}
|
||||
|
||||
private static LocalizeStringEvent BindLocalizedText(TMP_Text text, string entryKey)
|
||||
{
|
||||
if (text == null)
|
||||
return null;
|
||||
|
||||
LocalizeStringEvent localize = text.GetComponent<LocalizeStringEvent>();
|
||||
if (localize == null)
|
||||
localize = text.gameObject.AddComponent<LocalizeStringEvent>();
|
||||
|
||||
localize.StringReference.SetReference(ConstRef.UITextTable, entryKey);
|
||||
localize.OnUpdateString.RemoveAllListeners();
|
||||
localize.OnUpdateString.AddListener(value => text.text = value);
|
||||
localize.StringReference.RefreshString();
|
||||
return localize;
|
||||
}
|
||||
|
||||
public IEnumerator PlayDeepAndWait()
|
||||
{
|
||||
int generation = BeginPlayback();
|
||||
SetOverlayVisibility(false, false, false);
|
||||
yield return PlayRangeAndWait(DeepFirst, DeepLast, generation);
|
||||
if (generation != playbackGeneration)
|
||||
yield break;
|
||||
state = ScreenState.Deep;
|
||||
}
|
||||
|
||||
public IEnumerator PlayPanelAndWait()
|
||||
{
|
||||
int generation = BeginPlayback();
|
||||
yield return PlayRangeAndWait(UiFlashFirst, UiFlashLast, generation, StatusRevealFrame, ShowStatusAndObjective);
|
||||
if (generation != playbackGeneration)
|
||||
yield break;
|
||||
|
||||
yield return PlayRangeAndWait(RingFirst, RingLast, generation);
|
||||
if (generation != playbackGeneration)
|
||||
yield break;
|
||||
|
||||
ShowStatusAndObjective();
|
||||
state = ScreenState.Panel;
|
||||
}
|
||||
|
||||
public IEnumerator PlayOutputAndWait()
|
||||
{
|
||||
int generation = BeginPlayback();
|
||||
SetOutputTextVisible(false);
|
||||
yield return PlayRangeAndWait(
|
||||
OutputFirst,
|
||||
OutputLast,
|
||||
generation,
|
||||
OutputTextRevealFrame,
|
||||
() => SetOutputTextVisible(true));
|
||||
if (generation != playbackGeneration)
|
||||
yield break;
|
||||
|
||||
SetFrame(FloatingFrame);
|
||||
SetOutputTextVisible(true);
|
||||
state = ScreenState.Floating;
|
||||
}
|
||||
|
||||
public IEnumerator PlayClickAndWait()
|
||||
{
|
||||
int generation = BeginPlayback();
|
||||
SetOutputTextVisible(true);
|
||||
yield return PlayRangeAndWait(ClickFirst, ClickLast, generation);
|
||||
if (generation != playbackGeneration)
|
||||
yield break;
|
||||
state = ScreenState.Click;
|
||||
}
|
||||
|
||||
public void PrepareForNewRound()
|
||||
{
|
||||
StopPlayback();
|
||||
SetFrame(DeepFirst);
|
||||
SetOverlayVisibility(false, false, false);
|
||||
state = ScreenState.Reset;
|
||||
}
|
||||
|
||||
public void HideOverlayForFocus()
|
||||
{
|
||||
SetOverlayVisibility(false, false, false);
|
||||
state = ScreenState.FocusHidden;
|
||||
}
|
||||
|
||||
public void ResetImmediate()
|
||||
{
|
||||
StopPlayback();
|
||||
SetFrame(DeepFirst);
|
||||
SetOverlayVisibility(false, false, false);
|
||||
if (outputButton != null)
|
||||
{
|
||||
outputButton.interactable = false;
|
||||
outputButton.gameObject.SetActive(false);
|
||||
}
|
||||
state = ScreenState.Reset;
|
||||
}
|
||||
|
||||
private int BeginPlayback()
|
||||
{
|
||||
StopPlayback();
|
||||
return playbackGeneration;
|
||||
}
|
||||
|
||||
private IEnumerator PlayRangeAndWait(
|
||||
int firstFrame,
|
||||
int lastFrame,
|
||||
int generation,
|
||||
int revealFrame = -1,
|
||||
System.Action revealAction = null)
|
||||
{
|
||||
for (int frameIndex = firstFrame; frameIndex <= lastFrame; frameIndex++)
|
||||
{
|
||||
if (generation != playbackGeneration)
|
||||
yield break;
|
||||
|
||||
SetFrame(frameIndex);
|
||||
if (frameIndex == revealFrame)
|
||||
revealAction?.Invoke();
|
||||
yield return new WaitForSeconds(GetFrameDuration(frameIndex));
|
||||
}
|
||||
}
|
||||
|
||||
private void SetFrame(int frameIndex)
|
||||
{
|
||||
if (screenRenderer == null || frames == null ||
|
||||
frameIndex < 0 || frameIndex >= frames.Length || frames[frameIndex] == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
screenRenderer.gameObject.SetActive(true);
|
||||
screenRenderer.enabled = true;
|
||||
screenRenderer.sprite = frames[frameIndex];
|
||||
}
|
||||
|
||||
private static float GetFrameDuration(int frameIndex)
|
||||
{
|
||||
switch (frameIndex)
|
||||
{
|
||||
case 0:
|
||||
case 29:
|
||||
case 47:
|
||||
case 48:
|
||||
return 0.3f;
|
||||
case 16:
|
||||
case 17:
|
||||
case 21:
|
||||
case 25:
|
||||
case 49:
|
||||
case 50:
|
||||
case 51:
|
||||
case 52:
|
||||
return 0.05f;
|
||||
default:
|
||||
return 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowStatusAndObjective()
|
||||
{
|
||||
SetOverlayVisibility(true, true, false);
|
||||
}
|
||||
|
||||
private void SetOverlayVisibility(bool statusVisible, bool objectiveVisible, bool outputVisible)
|
||||
{
|
||||
SetStatusTextVisible(integrationProgressText, statusVisible);
|
||||
SetStatusTextVisible(interferenceCountText, statusVisible);
|
||||
if (objectiveText != null)
|
||||
objectiveText.gameObject.SetActive(objectiveVisible);
|
||||
SetOutputTextVisible(outputVisible);
|
||||
}
|
||||
|
||||
private static void SetStatusTextVisible(TMP_Text text, bool visible)
|
||||
{
|
||||
if (text == null)
|
||||
return;
|
||||
|
||||
if (visible)
|
||||
{
|
||||
var canvasGroup = text.GetComponentInParent<CanvasGroup>();
|
||||
if (canvasGroup != null)
|
||||
canvasGroup.alpha = 1f;
|
||||
}
|
||||
|
||||
if (text.transform.parent != null)
|
||||
text.transform.parent.gameObject.SetActive(visible);
|
||||
text.gameObject.SetActive(visible);
|
||||
}
|
||||
|
||||
private void SetOutputTextVisible(bool visible)
|
||||
{
|
||||
if (outputText != null)
|
||||
outputText.gameObject.SetActive(visible);
|
||||
}
|
||||
|
||||
private void StopPlayback()
|
||||
{
|
||||
playbackGeneration++;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (objectiveLocalization != null)
|
||||
objectiveLocalization.OnUpdateString.RemoveAllListeners();
|
||||
if (outputLocalization != null)
|
||||
outputLocalization.OnUpdateString.RemoveAllListeners();
|
||||
if (objectiveMaterial != null)
|
||||
Destroy(objectiveMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e1287ca2a7d4d2d90c1a668c0f5bb82
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -5,7 +5,10 @@ using System.Linq;
|
||||
using DG.Tweening;
|
||||
using TMPro;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Localization;
|
||||
using UnityEngine.Localization.Settings;
|
||||
using AibisDream;
|
||||
using AibisDream.Utility;
|
||||
|
||||
namespace AibisDream.MiniGame.Language
|
||||
{
|
||||
@@ -134,6 +137,7 @@ namespace AibisDream.MiniGame.Language
|
||||
[SerializeField] private Button focusSequenceButton;
|
||||
[Tooltip("火山释放log界面,触发 focus 时对其 SpriteRenderer 做淡出,重新开始时恢复")]
|
||||
[SerializeField] private SpriteRenderer releaseLogSpriteRenderer;
|
||||
[SerializeField] private ExpressionScreenPresentationController screenPresentation;
|
||||
[Tooltip("开场表达panel Timeline 名称(DirectorName 或 DirectorName/AddressableKey),播完才算表达流程完成")]
|
||||
[SerializeField] private string expressionPanelTimelineName = "火山表达模块/火山表达panel";
|
||||
[Tooltip("火山表达确认 Timeline 名称,播完才解锁按钮交互")]
|
||||
@@ -250,6 +254,11 @@ namespace AibisDream.MiniGame.Language
|
||||
private float lieEdgeJitter;
|
||||
private float truthGatherProgress;
|
||||
private LogReleasePresentationController presentationController;
|
||||
private const string IntegrationLocalizationKey = "huoshan_expression_integration";
|
||||
private const string InterferenceLocalizationKey = "huoshan_expression_interference";
|
||||
private string integrationStatusFormat = "{0}% 语言整合完成度";
|
||||
private string interferenceStatusFormat = "{0} 处异常思绪仍在干扰表达";
|
||||
private Coroutine statusLocalizationRoutine;
|
||||
private const string LieCompressionTweenId = "HuoshanLieEdgeCompression";
|
||||
private const string TruthGatherTweenId = "HuoshanTruthGather";
|
||||
private const string FocusInterferenceTweenId = "HuoshanFocusInterference";
|
||||
@@ -286,9 +295,51 @@ namespace AibisDream.MiniGame.Language
|
||||
|
||||
private void Start()
|
||||
{
|
||||
ResolveScreenPresentation();
|
||||
LocalizationSettings.SelectedLocaleChanged += HandleLocaleChanged;
|
||||
statusLocalizationRoutine = StartCoroutine(RefreshStatusLocalization());
|
||||
// Start 中不再自动初始化,等待外部调用 InitializeSystem
|
||||
}
|
||||
|
||||
private void ResolveScreenPresentation()
|
||||
{
|
||||
if (screenPresentation == null)
|
||||
screenPresentation = GetComponentInParent<ExpressionScreenPresentationController>(true);
|
||||
}
|
||||
|
||||
private void HandleLocaleChanged(Locale locale)
|
||||
{
|
||||
if (!isActiveAndEnabled)
|
||||
return;
|
||||
|
||||
if (statusLocalizationRoutine != null)
|
||||
StopCoroutine(statusLocalizationRoutine);
|
||||
statusLocalizationRoutine = StartCoroutine(RefreshStatusLocalization());
|
||||
}
|
||||
|
||||
private IEnumerator RefreshStatusLocalization()
|
||||
{
|
||||
yield return LocalizationSettings.InitializationOperation;
|
||||
|
||||
var integrationOperation = LocalizationSettings.StringDatabase.GetLocalizedStringAsync(
|
||||
ConstRef.UITextTable,
|
||||
IntegrationLocalizationKey);
|
||||
yield return integrationOperation;
|
||||
if (!string.IsNullOrEmpty(integrationOperation.Result))
|
||||
integrationStatusFormat = integrationOperation.Result;
|
||||
|
||||
var interferenceOperation = LocalizationSettings.StringDatabase.GetLocalizedStringAsync(
|
||||
ConstRef.UITextTable,
|
||||
InterferenceLocalizationKey);
|
||||
yield return interferenceOperation;
|
||||
if (!string.IsNullOrEmpty(interferenceOperation.Result))
|
||||
interferenceStatusFormat = interferenceOperation.Result;
|
||||
|
||||
statusLocalizationRoutine = null;
|
||||
if (isInitialized)
|
||||
UpdateStatusUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化系统(可以由外部调用,用于配置游戏参数)
|
||||
/// </summary>
|
||||
@@ -373,6 +424,8 @@ namespace AibisDream.MiniGame.Language
|
||||
if (!isInitialized)
|
||||
yield break;
|
||||
|
||||
ResolveScreenPresentation();
|
||||
screenPresentation?.PrepareForNewRound();
|
||||
StopFocusTransitionRoutine();
|
||||
KillCompletionUiTweens();
|
||||
resolveTotalDurationOverride = -1f;
|
||||
@@ -1286,7 +1339,15 @@ namespace AibisDream.MiniGame.Language
|
||||
|
||||
private IEnumerator PlayConfirmTimelineAndUnlockButton()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(expressionConfirmTimelineName) && TimelineCenter.Instance != null)
|
||||
ResolveScreenPresentation();
|
||||
if (screenPresentation != null)
|
||||
{
|
||||
yield return screenPresentation.PlayOutputAndWait();
|
||||
if (screenPresentation == null ||
|
||||
screenPresentation.State != ExpressionScreenPresentationController.ScreenState.Floating)
|
||||
yield break;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(expressionConfirmTimelineName) && TimelineCenter.Instance != null)
|
||||
{
|
||||
yield return TimelineCenter.Instance.PlayTimelineAsync(expressionConfirmTimelineName);
|
||||
}
|
||||
@@ -1303,6 +1364,29 @@ namespace AibisDream.MiniGame.Language
|
||||
if (completionPhase != CompletionPhase.Completed)
|
||||
return;
|
||||
|
||||
if (focusSequenceButton != null)
|
||||
focusSequenceButton.interactable = false;
|
||||
|
||||
ResolveScreenPresentation();
|
||||
if (screenPresentation != null)
|
||||
{
|
||||
StartCoroutine(PlayClickThenBeginFocus());
|
||||
return;
|
||||
}
|
||||
|
||||
BeginFocusSequence();
|
||||
}
|
||||
|
||||
private IEnumerator PlayClickThenBeginFocus()
|
||||
{
|
||||
yield return screenPresentation.PlayClickAndWait();
|
||||
if (screenPresentation == null ||
|
||||
screenPresentation.State != ExpressionScreenPresentationController.ScreenState.Click)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
screenPresentation.HideOverlayForFocus();
|
||||
BeginFocusSequence();
|
||||
}
|
||||
|
||||
@@ -1837,6 +1921,11 @@ namespace AibisDream.MiniGame.Language
|
||||
presentationController = controller;
|
||||
}
|
||||
|
||||
public void SetScreenPresentationController(ExpressionScreenPresentationController controller)
|
||||
{
|
||||
screenPresentation = controller;
|
||||
}
|
||||
|
||||
public void BeginLieEdgeCompression(float duration, float jitterAmount)
|
||||
{
|
||||
if (completionPhase != CompletionPhase.FocusHolding)
|
||||
@@ -3153,7 +3242,20 @@ namespace AibisDream.MiniGame.Language
|
||||
/// </summary>
|
||||
private IEnumerator PlayExpressionPanelThenReady()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(expressionPanelTimelineName) && TimelineCenter.Instance != null)
|
||||
ResolveScreenPresentation();
|
||||
if (screenPresentation != null)
|
||||
{
|
||||
if (languageDeepPanel1 != null)
|
||||
languageDeepPanel1.SetActive(true);
|
||||
if (languageDeepPanel2 != null)
|
||||
languageDeepPanel2.SetActive(true);
|
||||
|
||||
yield return screenPresentation.PlayPanelAndWait();
|
||||
if (screenPresentation == null ||
|
||||
screenPresentation.State != ExpressionScreenPresentationController.ScreenState.Panel)
|
||||
yield break;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(expressionPanelTimelineName) && TimelineCenter.Instance != null)
|
||||
{
|
||||
// 火山表达 panel timeline 驱动 panel1/2,播放前需先激活(若之前被 fade 过,需重置 alpha)
|
||||
if (languageDeepPanel1 != null)
|
||||
@@ -3301,13 +3403,13 @@ namespace AibisDream.MiniGame.Language
|
||||
{
|
||||
float ratio = CalculateIntegrationRatio();
|
||||
int percent = Mathf.RoundToInt(ratio * 100f);
|
||||
integrationProgressText.text = $"{percent}% 语言整合完成度";
|
||||
integrationProgressText.text = string.Format(integrationStatusFormat, percent);
|
||||
}
|
||||
|
||||
if (interferenceCountText != null)
|
||||
{
|
||||
int interferenceCount = CountInterferenceNodes();
|
||||
interferenceCountText.text = $"{interferenceCount} 处异常思绪仍在干扰表达";
|
||||
interferenceCountText.text = string.Format(interferenceStatusFormat, interferenceCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3409,6 +3511,11 @@ namespace AibisDream.MiniGame.Language
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
LocalizationSettings.SelectedLocaleChanged -= HandleLocaleChanged;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user