using System; using System.Collections.Generic; using UnityEngine; public class gameplayGalgame : MonoBehaviour { private ingame_galgame_so.JsonDefinition resolvedDefinition; private sealed class RuntimeDialogueEntry { public int sequence; public ingame_galgame_so.DialogueElement.PositionTag positionTag; public ingame_galgame_so.DialogueText dialogue; public Speaker speaker; } [Header("启用开关")] public bool enableGalgame = false; public bool enableLyricsSO = true; [Header("对话数据")] public ingame_galgame_so golSO; [Header("预制体")] public GameObject galgamePrefab; public GameObject where_to_put; [Header("时间轴")] public AudioSource musicTimelineSource; [Header("输入")] public bool allowGlobalClickToAdvance = true; public KeyCode keyboardAdvanceKey = KeyCode.Space; public KeyCode keyboardAdvanceKeyAlt = KeyCode.Return; private readonly List runtimeEntries = new List(); private galPrefab galController; private GameObject galInstance; private int currentEntryIndex = -1; private bool isRunning; private bool waitingForClick; private bool pauseQueueRequested; private bool entryVisible; private bool advanceRequested; private float sceneStartRealtime; private float entryShownRealtime; private float customTimelineTime; private float cachedTimeScale = 1f; private bool timeActionApplied; public bool IsRunning => isRunning; public bool IsPausedQueue => pauseQueueRequested; public bool HasActiveEntry => isRunning && currentEntryIndex >= 0 && currentEntryIndex < runtimeEntries.Count; private void Awake() { sceneStartRealtime = Time.unscaledTime; } private void Start() { TryPrepareRuntime(); if (ShouldAutoStart()) { StartSequence(); } } private void Update() { if (!isRunning || golSO == null || runtimeEntries.Count == 0) { return; } if (golSO.timelineStandard == ingame_galgame_so.TimelineBase.CustomTimer) { customTimelineTime += Time.unscaledDeltaTime; } if (pauseQueueRequested) { if (advanceRequested || DetectAdvanceInput()) { advanceRequested = false; ResumeSequence(); } return; } RuntimeDialogueEntry entry = GetCurrentEntry(); if (entry == null) { FinishSequence(); return; } if (!entryVisible) { if (GetTimelineTime() >= Mathf.Max(0f, entry.dialogue.appearTime)) { ShowCurrentEntry(entry); } return; } if (waitingForClick) { if (advanceRequested || DetectAdvanceInput()) { advanceRequested = false; ResolveCurrentEntry(entry); } return; } float requiredDuration = Mathf.Max(0f, entry.dialogue.duration); if (Time.unscaledTime - entryShownRealtime >= requiredDuration) { ResolveCurrentEntry(entry); } } public void read_gol_so() { if (golSO == null) { Debug.LogWarning("[gameplayGalgame] 没有配置 galgame SO。"); return; } ResolveDefinition(); if (resolvedDefinition == null) { Debug.LogWarning("[gameplayGalgame] 无法解析 galgame 配置。"); return; } Debug.Log($"[gameplayGalgame] dialogueType={resolvedDefinition.dialogueType}, triggerMethod={resolvedDefinition.triggerMethod}, timeline={resolvedDefinition.timelineStandard}"); } public void StartSequence() { if (!ShouldPlayConfiguredSo()) { return; } TryPrepareRuntime(); if (golSO == null || runtimeEntries.Count == 0) { return; } StopSequenceInternal(false); sceneStartRealtime = Time.unscaledTime; customTimelineTime = 0f; currentEntryIndex = 0; isRunning = true; waitingForClick = false; pauseQueueRequested = false; entryVisible = false; advanceRequested = false; EnsureGalInstance(); if (galController != null) { galController.HideAllModes(); } } public void RestartSequence() { StartSequence(); } public void ResumeSequence() { if (!pauseQueueRequested) { return; } pauseQueueRequested = false; AdvanceToNextEntry(); } public void StopSequence() { StopSequenceInternal(true); } public void RequestAdvance() { advanceRequested = true; } public void SetCustomTimelineTime(float value) { customTimelineTime = Mathf.Max(0f, value); } public void AdvanceCustomTimeline(float delta) { customTimelineTime = Mathf.Max(0f, customTimelineTime + delta); } private bool ShouldAutoStart() { if (!ShouldPlayConfiguredSo() || golSO == null) { return false; } ResolveDefinition(); if (resolvedDefinition == null) { return false; } return resolvedDefinition.triggerMethod == ingame_galgame_so.TriggerMethod.TimerTrigger || resolvedDefinition.triggerMethod == ingame_galgame_so.TriggerMethod.MixedTrigger; } private bool ShouldPlayConfiguredSo() { if (golSO == null) { return false; } ResolveDefinition(); if (resolvedDefinition == null) { return false; } if (resolvedDefinition.dialogueType == ingame_galgame_so.DialogueType.Galgame) { return enableGalgame; } if (resolvedDefinition.dialogueType == ingame_galgame_so.DialogueType.Lyrics) { return enableLyricsSO; } return false; } private void TryPrepareRuntime() { ResolveDefinition(); BuildRuntimeEntries(); EnsureGalInstance(); } private void BuildRuntimeEntries() { runtimeEntries.Clear(); if (golSO == null || resolvedDefinition == null || resolvedDefinition.dialogueList == null) { return; } int sequence = 0; for (int i = 0; i < resolvedDefinition.dialogueList.Count; i++) { ingame_galgame_so.DialogueElement element = resolvedDefinition.dialogueList[i]; if (element == null || element.textList == null) { continue; } for (int j = 0; j < element.textList.Count; j++) { ingame_galgame_so.DialogueText text = element.textList[j]; if (text == null) { continue; } runtimeEntries.Add(new RuntimeDialogueEntry { sequence = sequence++, positionTag = element.positionTag, dialogue = text, speaker = ResolveSpeaker(text.senderIndex) }); } } } private Speaker ResolveSpeaker(int senderIndex) { if (golSO == null || golSO.speakersDataSO == null || golSO.speakersDataSO.speakers == null) { return null; } if (senderIndex < 0 || senderIndex >= golSO.speakersDataSO.speakers.Count) { return null; } return golSO.speakersDataSO.speakers[senderIndex]; } private void EnsureGalInstance() { if (galController != null) { galController.Initialize(RequestAdvance); return; } if (galgamePrefab == null) { return; } Transform parent = where_to_put != null ? where_to_put.transform : transform; if (galInstance == null) { galInstance = Instantiate(galgamePrefab, parent, false); } galController = galInstance != null ? galInstance.GetComponent() : null; if (galController != null) { galController.Initialize(RequestAdvance); } } private RuntimeDialogueEntry GetCurrentEntry() { if (currentEntryIndex < 0 || currentEntryIndex >= runtimeEntries.Count) { return null; } return runtimeEntries[currentEntryIndex]; } private void ShowCurrentEntry(RuntimeDialogueEntry entry) { if (entry == null) { return; } EnsureGalInstance(); RestoreTimeAction(); if (galController != null) { galController.Present(entry.positionTag, entry.dialogue, entry.speaker); bool clickToNext = entry.dialogue.stepMethod == ingame_galgame_so.DialogueText.StepMethod.ClickToNext; galController.SetAdvanceEnabled(clickToNext); } ApplyTimeAction(entry.dialogue); entryVisible = true; entryShownRealtime = Time.unscaledTime; waitingForClick = entry.dialogue.stepMethod == ingame_galgame_so.DialogueText.StepMethod.ClickToNext; } private void ResolveCurrentEntry(RuntimeDialogueEntry entry) { if (entry == null) { FinishSequence(); return; } RestoreTimeAction(); waitingForClick = false; entryVisible = false; switch (entry.dialogue.endAction) { case ingame_galgame_so.DialogueText.EndAction.NextText: AdvanceToNextEntry(); break; case ingame_galgame_so.DialogueText.EndAction.PauseQueue: pauseQueueRequested = true; if (galController != null) { galController.SetAdvanceEnabled(true); } break; case ingame_galgame_so.DialogueText.EndAction.EndPerformance: FinishSequence(); break; default: AdvanceToNextEntry(); break; } } private void AdvanceToNextEntry() { currentEntryIndex++; if (currentEntryIndex >= runtimeEntries.Count) { FinishSequence(); return; } advanceRequested = false; waitingForClick = false; entryVisible = false; } private void FinishSequence() { StopSequenceInternal(true); } private void StopSequenceInternal(bool hideUi) { RestoreTimeAction(); isRunning = false; waitingForClick = false; pauseQueueRequested = false; entryVisible = false; advanceRequested = false; currentEntryIndex = -1; if (hideUi && galController != null) { galController.HideAllModes(); galController.SetAdvanceEnabled(false); } } private void ApplyTimeAction(ingame_galgame_so.DialogueText dialogue) { if (dialogue == null || dialogue.timeAction == ingame_galgame_so.DialogueText.TimeAction.None) { return; } cachedTimeScale = Time.timeScale; timeActionApplied = true; switch (dialogue.timeAction) { case ingame_galgame_so.DialogueText.TimeAction.SlowMotion: Time.timeScale = Mathf.Clamp(dialogue.slowMotionScale, 0f, 1f); break; case ingame_galgame_so.DialogueText.TimeAction.Pause: Time.timeScale = 0f; break; } } private void RestoreTimeAction() { if (!timeActionApplied) { return; } Time.timeScale = cachedTimeScale; timeActionApplied = false; } private float GetTimelineTime() { if (golSO == null) { return 0f; } ResolveDefinition(); if (resolvedDefinition == null) { return 0f; } switch (resolvedDefinition.timelineStandard) { case ingame_galgame_so.TimelineBase.SceneStartTime: return Mathf.Max(0f, Time.unscaledTime - sceneStartRealtime); case ingame_galgame_so.TimelineBase.CustomTimer: return customTimelineTime; case ingame_galgame_so.TimelineBase.MusicTime: if (musicTimelineSource != null) { return Mathf.Max(0f, musicTimelineSource.time); } return Mathf.Max(0f, Time.unscaledTime - sceneStartRealtime); default: return Mathf.Max(0f, Time.unscaledTime - sceneStartRealtime); } } private void ResolveDefinition() { resolvedDefinition = golSO != null ? golSO.GetResolvedDefinition() : null; } private bool DetectAdvanceInput() { if (!allowGlobalClickToAdvance) { return false; } if (Input.GetMouseButtonDown(0)) { return true; } if (Input.touchCount > 0) { Touch touch = Input.GetTouch(0); if (touch.phase == TouchPhase.Began) { return true; } } return Input.GetKeyDown(keyboardAdvanceKey) || Input.GetKeyDown(keyboardAdvanceKeyAlt); } }