调整 Directive 逻辑并添加章节之间的承接功能
This commit is contained in:
parent
facfe7a437
commit
d557153e28
|
|
@ -100,4 +100,6 @@ crashlytics-build.properties
|
|||
/.omc
|
||||
/.omx
|
||||
/.dotnet_home
|
||||
/.dotnet
|
||||
/.dotnet
|
||||
/.claude
|
||||
/.codex
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
{"session_id":"980cf702-c5aa-462f-a54f-9cf7f5a5882d","transcript_path":"C:\\Users\\September\\.claude\\projects\\D--Learn-GameLearn-UnityProjects-Biography-of-Li-Jian\\980cf702-c5aa-462f-a54f-9cf7f5a5882d.jsonl","cwd":"D:\\Learn\\GameLearn\\UnityProjects\\Biography of Li Jian","model":{"id":"MiniMax-M2.7-highspeed","display_name":"MiniMax-M2.7-highspeed"},"workspace":{"current_dir":"D:\\Learn\\GameLearn\\UnityProjects\\Biography of Li Jian","project_dir":"D:\\Learn\\GameLearn\\UnityProjects\\Biography of Li Jian","added_dirs":[]},"version":"2.1.114","output_style":{"name":"default"},"cost":{"total_cost_usd":0.45526774999999997,"total_duration_ms":743042,"total_api_duration_ms":19399,"total_lines_added":0,"total_lines_removed":0},"context_window":{"total_input_tokens":31113,"total_output_tokens":1126,"context_window_size":200000,"current_usage":{"input_tokens":17274,"output_tokens":521,"cache_creation_input_tokens":0,"cache_read_input_tokens":26409},"used_percentage":22,"remaining_percentage":78},"exceeds_200k_tokens":false}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"timestamp": "2026-04-18T09:34:00.712Z",
|
||||
"backgroundTasks": [],
|
||||
"sessionStartTimestamp": "2026-04-18T09:33:32.703Z",
|
||||
"sessionId": "1b0f5c6b-460e-48ef-9f2b-eb516bfbee3e"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,8 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 99d811b0183246646a2ce8df996f4bca
|
||||
guid: 98380886ef3ddab4e913ead8133a2e42
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -10,7 +10,7 @@ MonoBehaviour:
|
|||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 71c1514a6bd24e1e882cebbe1904ce04, type: 3}
|
||||
m_Name: simfang SDF
|
||||
m_Name: simfangTMP
|
||||
m_EditorClassIdentifier:
|
||||
hashCode: 1985928552
|
||||
material: {fileID: 7425184593944002063}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,15 @@ namespace CustomComponent
|
|||
private int _nextChapterTitleRequestId = 1;
|
||||
private bool _chapterTitleShownAfterInit;
|
||||
|
||||
[Header("章节标题过场")]
|
||||
[SerializeField] [Min(0f)] private float _chapterTitleDimFadeInDuration = 0.2f;
|
||||
[SerializeField] [Min(0f)] private float _chapterTitlePreBlackDuration = 0.25f;
|
||||
[SerializeField] [Min(0f)] private float _chapterTitleTitleFadeInDelay = 0f;
|
||||
[SerializeField] [Min(0f)] private float _chapterTitleFadeInDuration = 0.3f;
|
||||
[SerializeField] [Min(0f)] private float _chapterTitleHoldDuration = 1.2f;
|
||||
[SerializeField] [Min(0f)] private float _chapterTitleFadeOutDuration = 0.3f;
|
||||
[SerializeField] [Min(0f)] private float _chapterTitleDimFadeOutDuration = 0.2f;
|
||||
|
||||
private int _currentChapterId;
|
||||
private int _currentLineIndex = -1;
|
||||
private bool _isInitialized;
|
||||
|
|
@ -572,7 +581,14 @@ namespace CustomComponent
|
|||
ChapterId = _currentChapterId,
|
||||
RequestId = requestId,
|
||||
Title = BuildChapterTitle(_currentChapterId),
|
||||
Subtitle = subtitle
|
||||
Subtitle = subtitle,
|
||||
DimFadeInDuration = _chapterTitleDimFadeInDuration,
|
||||
PreBlackDuration = _chapterTitlePreBlackDuration,
|
||||
TitleFadeInDelay = _chapterTitleTitleFadeInDelay,
|
||||
TitleFadeInDuration = _chapterTitleFadeInDuration,
|
||||
HoldDuration = _chapterTitleHoldDuration,
|
||||
TitleFadeOutDuration = _chapterTitleFadeOutDuration,
|
||||
DimFadeOutDuration = _chapterTitleDimFadeOutDuration
|
||||
};
|
||||
|
||||
int? formSerialId = GameEntry.UI.OpenUIForm(UIFormId.ChapterTitleForm, context);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,22 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using DataTable;
|
||||
using Event;
|
||||
using GameFramework.DataTable;
|
||||
using GameFramework.Event;
|
||||
using GameFramework.Fsm;
|
||||
using GameFramework.Procedure;
|
||||
using UnityEngine;
|
||||
using UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace Procedure
|
||||
{
|
||||
public class ProcedureMain : ProcedureBase
|
||||
{
|
||||
private const int InitialChapterId = 1;
|
||||
private const int InitialDialogId = 1001;
|
||||
private const int DialogChapterDivisor = 1000;
|
||||
|
||||
private IDataTable<DRDialog> _dtDialog;
|
||||
|
||||
public override bool UseNativeDialog => false;
|
||||
|
||||
protected override void OnEnter(IFsm<IProcedureManager> procedureOwner)
|
||||
|
|
@ -16,8 +24,104 @@ namespace Procedure
|
|||
base.OnEnter(procedureOwner);
|
||||
AIChatEntryRuntime.EnsureOpen();
|
||||
|
||||
GameEntry.Dialog.Init(1);
|
||||
GameEntry.Dialog.StartDialog(1001);
|
||||
GameEntry.Event.Subscribe(StoryChapterEndedEventArgs.EventId, OnStoryChapterEnded);
|
||||
|
||||
GameEntry.Dialog.Init(InitialChapterId);
|
||||
GameEntry.Dialog.StartDialog(InitialDialogId);
|
||||
}
|
||||
|
||||
protected override void OnLeave(IFsm<IProcedureManager> procedureOwner, bool isShutdown)
|
||||
{
|
||||
GameEntry.Event.Unsubscribe(StoryChapterEndedEventArgs.EventId, OnStoryChapterEnded);
|
||||
base.OnLeave(procedureOwner, isShutdown);
|
||||
}
|
||||
|
||||
private void OnStoryChapterEnded(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is StoryChapterEndedEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int currentChapterId = args.ChapterId > 0
|
||||
? args.ChapterId
|
||||
: (GameEntry.Dialog != null ? GameEntry.Dialog.CurrentChapterId : 0);
|
||||
int nextChapterId = currentChapterId + 1;
|
||||
if (nextChapterId <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryResolveFirstDialogId(nextChapterId, out int nextDialogId))
|
||||
{
|
||||
Log.Info("ProcedureMain chapter switch stopped. No dialog found for chapter '{0}'.",
|
||||
nextChapterId.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameEntry.Dialog == null)
|
||||
{
|
||||
Log.Warning("ProcedureMain chapter switch failed. Dialog component is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GameEntry.Dialog.Init(nextChapterId))
|
||||
{
|
||||
Log.Warning("ProcedureMain chapter switch failed. Init chapter '{0}' failed.",
|
||||
nextChapterId.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GameEntry.Dialog.StartDialog(nextDialogId))
|
||||
{
|
||||
Log.Warning("ProcedureMain chapter switch failed. Start dialog '{0}' failed.",
|
||||
nextDialogId.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryResolveFirstDialogId(int chapterId, out int dialogId)
|
||||
{
|
||||
dialogId = 0;
|
||||
if (chapterId <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_dtDialog == null)
|
||||
{
|
||||
_dtDialog = GameEntry.DataTable.GetDataTable<DRDialog>();
|
||||
}
|
||||
|
||||
if (_dtDialog == null)
|
||||
{
|
||||
Log.Warning("ProcedureMain chapter switch failed. Data table DRDialog is missing.");
|
||||
return false;
|
||||
}
|
||||
|
||||
DRDialog[] dialogRows = _dtDialog.GetDataRows((a, b) => a.Id.CompareTo(b.Id));
|
||||
for (int i = 0; i < dialogRows.Length; i++)
|
||||
{
|
||||
DRDialog row = dialogRows[i];
|
||||
if (row == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ParseChapterIdFromDialogId(row.Id) != chapterId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
dialogId = row.Id;
|
||||
return dialogId > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int ParseChapterIdFromDialogId(int dialogId)
|
||||
{
|
||||
return dialogId / DialogChapterDivisor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,14 @@ namespace UI
|
|||
public int RequestId = 0;
|
||||
public string Title = string.Empty;
|
||||
public string Subtitle = string.Empty;
|
||||
|
||||
// < 0 表示使用 ChapterTitleForm 上的默认配置。
|
||||
public float DimFadeInDuration = -1f;
|
||||
public float PreBlackDuration = -1f;
|
||||
public float TitleFadeInDelay = -1f;
|
||||
public float TitleFadeInDuration = -1f;
|
||||
public float HoldDuration = -1f;
|
||||
public float TitleFadeOutDuration = -1f;
|
||||
public float DimFadeOutDuration = -1f;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,23 +10,23 @@ namespace UI
|
|||
public class AIChatForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private TMP_Text _returnButtonText;
|
||||
|
||||
|
||||
[SerializeField] private TMP_InputField _inputField;
|
||||
|
||||
|
||||
[SerializeField] private RectTransform _dialogContent;
|
||||
|
||||
|
||||
[SerializeField] private TMP_Text _titleText;
|
||||
|
||||
|
||||
[SerializeField] private ScrollRect _historyScrollRect;
|
||||
|
||||
|
||||
[SerializeField] private AIDialogItem _aiDialogItemPrefab;
|
||||
|
||||
|
||||
[SerializeField] private PlayerDialogItem _playerDialogItemPrefab;
|
||||
|
||||
|
||||
[SerializeField] private HorizonSelectGroup _languageSelectGroup;
|
||||
|
||||
|
||||
[SerializeField] private string _returnButtonNormalText = "<sprite name=\"KEYBOARD_Esc\"> Back";
|
||||
|
||||
|
||||
[SerializeField] private string _returnButtonHoverText = "<sprite name=\"KEYBOARD_Esc\"><u> Back </u>";
|
||||
|
||||
private readonly List<Component> _dialogItems = new List<Component>();
|
||||
|
|
|
|||
|
|
@ -13,14 +13,25 @@ namespace UI
|
|||
[SerializeField] private Image _dimImage;
|
||||
[SerializeField] private TMP_Text _titleText;
|
||||
[SerializeField] private TMP_Text _subtitleText;
|
||||
[SerializeField] private float _dimFadeInDuration = 0.2f;
|
||||
[SerializeField] private float _preBlackDuration = 0.25f;
|
||||
[SerializeField] private float _titleFadeInDelay = 0f;
|
||||
[SerializeField] private float _fadeInDuration = 0.3f;
|
||||
[SerializeField] private float _holdDuration = 1.2f;
|
||||
[SerializeField] private float _fadeOutDuration = 0.3f;
|
||||
[SerializeField] private float _dimFadeOutDuration = 0.2f;
|
||||
|
||||
private Coroutine _playCoroutine;
|
||||
private int _requestId;
|
||||
private int _chapterId;
|
||||
private bool _hasPublishedCompletion;
|
||||
private float _runtimeDimFadeInDuration;
|
||||
private float _runtimePreBlackDuration;
|
||||
private float _runtimeTitleFadeInDelay;
|
||||
private float _runtimeTitleFadeInDuration;
|
||||
private float _runtimeHoldDuration;
|
||||
private float _runtimeTitleFadeOutDuration;
|
||||
private float _runtimeDimFadeOutDuration;
|
||||
|
||||
protected override void OnOpen(object userData)
|
||||
{
|
||||
|
|
@ -33,7 +44,8 @@ namespace UI
|
|||
protected override void OnClose(bool isShutdown, object userData)
|
||||
{
|
||||
StopPlay();
|
||||
SetContentAlpha(0f);
|
||||
SetDimAlpha(0f);
|
||||
SetTitleAlpha(0f);
|
||||
if (!isShutdown)
|
||||
{
|
||||
PublishCompletionIfNeeded();
|
||||
|
|
@ -55,6 +67,11 @@ namespace UI
|
|||
_contentGroup = CreateContentGroup(rootRect);
|
||||
}
|
||||
|
||||
if (_contentGroup != null)
|
||||
{
|
||||
_contentGroup.alpha = 1f;
|
||||
}
|
||||
|
||||
RectTransform contentRect = _contentGroup != null && _contentGroup.transform is RectTransform contentTransform
|
||||
? contentTransform
|
||||
: rootRect;
|
||||
|
|
@ -105,6 +122,7 @@ namespace UI
|
|||
private void ApplyContext(ChapterTitleFormContext context)
|
||||
{
|
||||
_hasPublishedCompletion = false;
|
||||
ResetRuntimeTimings(context);
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
|
|
@ -157,10 +175,17 @@ namespace UI
|
|||
|
||||
private IEnumerator PlayRoutine()
|
||||
{
|
||||
SetContentAlpha(0f);
|
||||
yield return FadeAlpha(0f, 1f, _fadeInDuration);
|
||||
yield return WaitUnscaled(_holdDuration);
|
||||
yield return FadeAlpha(1f, 0f, _fadeOutDuration);
|
||||
SetDimAlpha(0f);
|
||||
SetTitleAlpha(0f);
|
||||
|
||||
yield return FadeDimAlpha(0f, 1f, _runtimeDimFadeInDuration);
|
||||
yield return WaitUnscaled(_runtimePreBlackDuration);
|
||||
yield return WaitUnscaled(_runtimeTitleFadeInDelay);
|
||||
yield return FadeTitleAlpha(0f, 1f, _runtimeTitleFadeInDuration);
|
||||
yield return WaitUnscaled(_runtimeHoldDuration);
|
||||
yield return FadeTitleAlpha(1f, 0f, _runtimeTitleFadeOutDuration);
|
||||
yield return FadeDimAlpha(1f, 0f, _runtimeDimFadeOutDuration);
|
||||
|
||||
_playCoroutine = null;
|
||||
|
||||
PublishCompletionIfNeeded();
|
||||
|
|
@ -170,12 +195,12 @@ namespace UI
|
|||
}
|
||||
}
|
||||
|
||||
private IEnumerator FadeAlpha(float from, float to, float duration)
|
||||
private IEnumerator FadeDimAlpha(float from, float to, float duration)
|
||||
{
|
||||
float safeDuration = Mathf.Max(0f, duration);
|
||||
if (safeDuration <= 0f)
|
||||
{
|
||||
SetContentAlpha(to);
|
||||
SetDimAlpha(to);
|
||||
yield break;
|
||||
}
|
||||
|
||||
|
|
@ -184,11 +209,32 @@ namespace UI
|
|||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / safeDuration);
|
||||
SetContentAlpha(Mathf.Lerp(from, to, t));
|
||||
SetDimAlpha(Mathf.Lerp(from, to, t));
|
||||
yield return null;
|
||||
}
|
||||
|
||||
SetContentAlpha(to);
|
||||
SetDimAlpha(to);
|
||||
}
|
||||
|
||||
private IEnumerator FadeTitleAlpha(float from, float to, float duration)
|
||||
{
|
||||
float safeDuration = Mathf.Max(0f, duration);
|
||||
if (safeDuration <= 0f)
|
||||
{
|
||||
SetTitleAlpha(to);
|
||||
yield break;
|
||||
}
|
||||
|
||||
float elapsed = 0f;
|
||||
while (elapsed < safeDuration)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / safeDuration);
|
||||
SetTitleAlpha(Mathf.Lerp(from, to, t));
|
||||
yield return null;
|
||||
}
|
||||
|
||||
SetTitleAlpha(to);
|
||||
}
|
||||
|
||||
private static IEnumerator WaitUnscaled(float duration)
|
||||
|
|
@ -207,14 +253,57 @@ namespace UI
|
|||
}
|
||||
}
|
||||
|
||||
private void SetContentAlpha(float alpha)
|
||||
private void SetDimAlpha(float alpha)
|
||||
{
|
||||
if (_contentGroup == null)
|
||||
if (_dimImage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_contentGroup.alpha = Mathf.Clamp01(alpha);
|
||||
Color color = _dimImage.color;
|
||||
color.a = Mathf.Clamp01(alpha);
|
||||
_dimImage.color = color;
|
||||
}
|
||||
|
||||
private void SetTitleAlpha(float alpha)
|
||||
{
|
||||
float clampedAlpha = Mathf.Clamp01(alpha);
|
||||
SetTextAlpha(_titleText, clampedAlpha);
|
||||
SetTextAlpha(_subtitleText, clampedAlpha);
|
||||
}
|
||||
|
||||
private static void SetTextAlpha(TMP_Text text, float alpha)
|
||||
{
|
||||
if (text == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Color color = text.color;
|
||||
color.a = alpha;
|
||||
text.color = color;
|
||||
}
|
||||
|
||||
private void ResetRuntimeTimings(ChapterTitleFormContext context)
|
||||
{
|
||||
_runtimeDimFadeInDuration = ResolveDuration(context != null ? context.DimFadeInDuration : -1f,
|
||||
_dimFadeInDuration);
|
||||
_runtimePreBlackDuration = ResolveDuration(context != null ? context.PreBlackDuration : -1f,
|
||||
_preBlackDuration);
|
||||
_runtimeTitleFadeInDelay = ResolveDuration(context != null ? context.TitleFadeInDelay : -1f,
|
||||
_titleFadeInDelay);
|
||||
_runtimeTitleFadeInDuration = ResolveDuration(context != null ? context.TitleFadeInDuration : -1f,
|
||||
_fadeInDuration);
|
||||
_runtimeHoldDuration = ResolveDuration(context != null ? context.HoldDuration : -1f, _holdDuration);
|
||||
_runtimeTitleFadeOutDuration = ResolveDuration(context != null ? context.TitleFadeOutDuration : -1f,
|
||||
_fadeOutDuration);
|
||||
_runtimeDimFadeOutDuration = ResolveDuration(context != null ? context.DimFadeOutDuration : -1f,
|
||||
_dimFadeOutDuration);
|
||||
}
|
||||
|
||||
private static float ResolveDuration(float overrideDuration, float fallbackDuration)
|
||||
{
|
||||
return overrideDuration >= 0f ? overrideDuration : Mathf.Max(0f, fallbackDuration);
|
||||
}
|
||||
|
||||
private void PublishCompletionIfNeeded()
|
||||
|
|
|
|||
|
|
@ -14,5 +14,5 @@ MonoBehaviour:
|
|||
m_EditorClassIdentifier:
|
||||
_enabled: 1
|
||||
_triggerType: 0
|
||||
_triggerId: 1005
|
||||
_chapterId: 2
|
||||
_triggerId: 1006
|
||||
_chapterId: 1
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ MonoBehaviour:
|
|||
_enabled: 1
|
||||
_triggerType: 0
|
||||
_triggerId: 2006
|
||||
_chapterId: 3
|
||||
_chapterId: 2
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ MonoBehaviour:
|
|||
_enabled: 1
|
||||
_triggerType: 0
|
||||
_triggerId: 3005
|
||||
_chapterId: 4
|
||||
_chapterId: 3
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ MonoBehaviour:
|
|||
_enabled: 1
|
||||
_triggerType: 0
|
||||
_triggerId: 4004
|
||||
_chapterId: 0
|
||||
_chapterId: 4
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 77968dfed7b61334db617d490bb05477
|
||||
guid: 0d041269ac2f63d47bc5f189d90dc73d
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
schema: spec-driven
|
||||
|
||||
# Project context (optional)
|
||||
# This is shown to AI when creating artifacts.
|
||||
# Add your tech stack, conventions, style guides, domain knowledge, etc.
|
||||
# Example:
|
||||
# context: |
|
||||
# Tech stack: TypeScript, React, Node.js
|
||||
# We use conventional commits
|
||||
# Domain: e-commerce platform
|
||||
|
||||
# Per-artifact rules (optional)
|
||||
# Add custom rules for specific artifacts.
|
||||
# Example:
|
||||
# rules:
|
||||
# proposal:
|
||||
# - Keep proposals under 500 words
|
||||
# - Always include a "Non-goals" section
|
||||
# tasks:
|
||||
# - Break tasks into chunks of max 2 hours
|
||||
Loading…
Reference in New Issue