功能批量实现 优化 服务端 新浮动小游戏框架 新界面UI
This commit is contained in:
@@ -1,28 +1,533 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class gameplayGalgame : MonoBehaviour
|
||||
{
|
||||
[Header("enable galgame so")]
|
||||
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("gal so")]
|
||||
|
||||
[Header("对话数据")]
|
||||
public ingame_galgame_so golSO;
|
||||
[Header("prefabs")]
|
||||
|
||||
[Header("预制体")]
|
||||
public GameObject galgamePrefab;
|
||||
public GameObject where_to_put;
|
||||
|
||||
private string timeType;
|
||||
private string triggerType;
|
||||
private string timelineStandard;
|
||||
[Header("时间轴")]
|
||||
public AudioSource musicTimelineSource;
|
||||
|
||||
[Header("输入")]
|
||||
public bool allowGlobalClickToAdvance = true;
|
||||
public KeyCode keyboardAdvanceKey = KeyCode.Space;
|
||||
public KeyCode keyboardAdvanceKeyAlt = KeyCode.Return;
|
||||
|
||||
private readonly List<RuntimeDialogueEntry> runtimeEntries = new List<RuntimeDialogueEntry>();
|
||||
|
||||
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)
|
||||
if (golSO == null)
|
||||
{
|
||||
timeType = golSO.dialogueType.ToString();
|
||||
triggerType = golSO.triggerMethod.ToString();
|
||||
timelineStandard = golSO.timelineStandard.ToString();
|
||||
Debug.LogWarning("[gameplayGalgame] 没有配置 galgame SO。");
|
||||
return;
|
||||
}
|
||||
else Debug.LogWarning("no gol so");
|
||||
|
||||
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<galPrefab>() : 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user