From c751a859ff66670f9bcb5fad75b29f2e5f69075c Mon Sep 17 00:00:00 2001 From: Ding Yuntian <1491671119@qq.com> Date: Sun, 19 Jul 2026 00:00:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(dialog):=20=E6=B7=BB=E5=8A=A0=E6=A2=A6?= =?UTF-8?q?=E5=A2=83=E9=80=89=E9=A1=B9=E6=8F=90=E7=A4=BA=E8=AF=AD=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 标记 #option_prompt 的台词不再进入普通台词流程,由 OptionView 暂存并在下一组 Dream Options 出现时显示在选项上方,同时通过 新增 LineHistoryAppend 事件加入对话历史。OnelineOptions 支持 提示语文本与独立选项根节点,并改为按实例追踪销毁按钮。 --- Assets/Scenes/Persistence.unity | 392 ++++++++++++++++-- Assets/Scripts/Dialog System/LineRunner.cs | 7 + Assets/Scripts/Dialog System/OptionView.cs | 34 +- Assets/Scripts/Game Loop/EventEnums.cs | 3 +- Assets/Scripts/UI/DialogUI/DialogUIManager.cs | 2 + .../UI/DialogUI/Options/OnelineOptions.cs | 85 +++- Assets/Scripts/Utility/YarnUtil.cs | 11 +- 7 files changed, 496 insertions(+), 38 deletions(-) diff --git a/Assets/Scenes/Persistence.unity b/Assets/Scenes/Persistence.unity index 6e8778551..0b6faeaa4 100644 --- a/Assets/Scenes/Persistence.unity +++ b/Assets/Scenes/Persistence.unity @@ -152,13 +152,15 @@ RectTransform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: [] + m_Children: + - {fileID: 812095842947190205} + - {fileID: 1889272444} m_Father: {fileID: 381795662} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 135, y: -331} - m_SizeDelta: {x: 500, y: 400} + m_AnchoredPosition: {x: 0, y: -325.35} + m_SizeDelta: {x: 500, y: 411.29} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &23905304 MonoBehaviour: @@ -173,7 +175,10 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: choiceButtonPrefab: {fileID: 3274141711606541777, guid: 11ba9c199d8b0fb44815c3467fc6c1cf, type: 3} + optionsRoot: {fileID: 1889272444} showDisabledOptions: 0 + optionPromptRoot: {fileID: 7601020547643004580} + optionPrompt: {fileID: 9176963010502204022} --- !u!114 &23905305 MonoBehaviour: m_ObjectHideFlags: 0 @@ -191,9 +196,9 @@ MonoBehaviour: m_Right: 0 m_Top: 0 m_Bottom: 0 - m_ChildAlignment: 3 - m_Spacing: 5 - m_ChildForceExpandWidth: 1 + m_ChildAlignment: 4 + m_Spacing: 0 + m_ChildForceExpandWidth: 0 m_ChildForceExpandHeight: 0 m_ChildControlWidth: 0 m_ChildControlHeight: 0 @@ -3313,8 +3318,8 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 15e947a337c0448ca777e71852e5bb3c, type: 3} - m_Name: - m_EditorClassIdentifier: + m_Name: + m_EditorClassIdentifier: --- !u!114 &719228342 MonoBehaviour: m_ObjectHideFlags: 0 @@ -3325,8 +3330,8 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 2a2aa6c1f5b143acb0fc561b2af40e36, type: 3} - m_Name: - m_EditorClassIdentifier: + m_Name: + m_EditorClassIdentifier: --- !u!1 &749156594 GameObject: m_ObjectHideFlags: 0 @@ -7110,6 +7115,68 @@ RectTransform: m_CorrespondingSourceObject: {fileID: 6777966190391379135, guid: 0bd336c33b9dc77498582232b543e604, type: 3} m_PrefabInstance: {fileID: 8741175886188417585} m_PrefabAsset: {fileID: 0} +--- !u!1 &1889272443 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1889272444} + - component: {fileID: 1889272445} + m_Layer: 5 + m_Name: OptionRoot + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1889272444 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1889272443} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 23905303} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 500, y: 201.08} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1889272445 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1889272443} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 130 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 3 + m_Spacing: 5 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 0 + m_ChildControlWidth: 0 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 --- !u!1 &1928871878 GameObject: m_ObjectHideFlags: 0 @@ -9054,6 +9121,44 @@ MonoBehaviour: m_EditorClassIdentifier: m_HorizontalFit: 0 m_VerticalFit: 0 +--- !u!1001 &3100101000 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 2091451641} + m_Modifications: [] + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 3923593479c745bdb809777da8333e7e, type: 3} +--- !u!224 &3100101002 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 100101, guid: 3923593479c745bdb809777da8333e7e, type: 3} + m_PrefabInstance: {fileID: 3100101000} + m_PrefabAsset: {fileID: 0} +--- !u!224 &812095842947190205 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7601020547643004580} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 5856570566943705213} + m_Father: {fileID: 23905303} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 250, y: -105.105} + m_SizeDelta: {x: 800, y: 30.232557} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1001 &894054357145943781 PrefabInstance: m_ObjectHideFlags: 0 @@ -9263,6 +9368,70 @@ MonoBehaviour: m_hasFontAssetChanged: 0 m_baseMaterial: {fileID: 0} m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &1670798941504811587 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7601020547643004580} + m_CullTransparentMesh: 1 +--- !u!114 &1759752980084053034 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7601020547643004580} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0.7411765} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: -123270858, guid: ca909870d485bde44b1e652490df203f, type: 3} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &2466976478708415771 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7601020547643004580} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 15 + m_Bottom: 15 + m_ChildAlignment: 3 + m_Spacing: 15 + m_ChildForceExpandWidth: 0 + m_ChildForceExpandHeight: 0 + m_ChildControlWidth: 1 + m_ChildControlHeight: 1 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 --- !u!224 &2894450840709450890 RectTransform: m_ObjectHideFlags: 0 @@ -9301,6 +9470,25 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 +--- !u!1 &3550804347409055510 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5856570566943705213} + - component: {fileID: 9208108607144599073} + - component: {fileID: 9176963010502204022} + - component: {fileID: 6603620953946417737} + m_Layer: 0 + m_Name: "\u5185\u5BB9" + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 --- !u!1001 &3839501584692003346 PrefabInstance: m_ObjectHideFlags: 0 @@ -9455,6 +9643,25 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7060338768394643986} m_CullTransparentMesh: 1 +--- !u!224 &5856570566943705213 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3550804347409055510} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 812095842947190205} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 400, y: -15.116279} + m_SizeDelta: {x: 800, y: 0} + m_Pivot: {x: 0.5, y: 0} --- !u!1001 &6362458665361278028 PrefabInstance: m_ObjectHideFlags: 0 @@ -9588,6 +9795,40 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3224902310437709222} m_CullTransparentMesh: 1 +--- !u!114 &6603620953946417737 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3550804347409055510} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreLayout: 0 + m_MinWidth: 10 + m_MinHeight: -1 + m_PreferredWidth: 800 + m_PreferredHeight: -1 + m_FlexibleWidth: -1 + m_FlexibleHeight: -1 + m_LayoutPriority: 1 +--- !u!114 &6980702625350260739 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7601020547643004580} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalFit: 2 + m_VerticalFit: 2 --- !u!1 &7060338768394643986 GameObject: m_ObjectHideFlags: 0 @@ -9606,6 +9847,26 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 0 +--- !u!1 &7601020547643004580 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 812095842947190205} + - component: {fileID: 1670798941504811587} + - component: {fileID: 1759752980084053034} + - component: {fileID: 2466976478708415771} + - component: {fileID: 6980702625350260739} + m_Layer: 0 + m_Name: OptionPrompt + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 --- !u!114 &7617572865850552188 stripped MonoBehaviour: m_CorrespondingSourceObject: {fileID: 3620463141908576383, guid: 9514dab519fa1f24dacc2903b8f331c3, type: 3} @@ -9719,24 +9980,103 @@ PrefabInstance: m_AddedGameObjects: [] m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 0bd336c33b9dc77498582232b543e604, type: 3} ---- !u!1001 &3100101000 -PrefabInstance: +--- !u!114 &9176963010502204022 +MonoBehaviour: m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 2091451641} - m_Modifications: [] - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: [] - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: 3923593479c745bdb809777da8333e7e, type: 3} ---- !u!224 &3100101002 stripped -RectTransform: - m_CorrespondingSourceObject: {fileID: 100101, guid: 3923593479c745bdb809777da8333e7e, type: 3} - m_PrefabInstance: {fileID: 3100101000} + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3550804347409055510} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2} + m_sharedMaterial: {fileID: -5947736473428621296, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 38 + m_fontSizeBase: 38 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 256 + m_textAlignment: 65535 + m_characterSpacing: 7 + m_wordSpacing: 0 + m_lineSpacing: 30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 30, y: 10, z: 20, w: 10} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &9208108607144599073 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3550804347409055510} + m_CullTransparentMesh: 1 --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Dialog System/LineRunner.cs b/Assets/Scripts/Dialog System/LineRunner.cs index cf81f5896..d001e8c3d 100644 --- a/Assets/Scripts/Dialog System/LineRunner.cs +++ b/Assets/Scripts/Dialog System/LineRunner.cs @@ -24,6 +24,13 @@ namespace AibisDream public override async YarnTask RunLineAsync(LocalizedLine dialogueLine, LineCancellationToken token) { + // 选项提示语由 OptionView 暂存,并在 Dream Options 出现时直接显示。 + // 不创建 LineSyncToken,避免进入普通台词的动画、推进和音频流程。 + if (dialogueLine.IsOptionPromptLine()) + { + return; + } + var lineSyncEvent = BuildSyncToken(dialogueLine); DialogController.Instance.lineSyncToken = lineSyncEvent; DispatchToView(dialogueLine, lineSyncEvent); diff --git a/Assets/Scripts/Dialog System/OptionView.cs b/Assets/Scripts/Dialog System/OptionView.cs index ba96e1f76..7d7d2a8a6 100644 --- a/Assets/Scripts/Dialog System/OptionView.cs +++ b/Assets/Scripts/Dialog System/OptionView.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using AibisDream.Framework; +using AibisDream.Utility; using UnityEngine; using Yarn.Unity; @@ -17,6 +18,7 @@ namespace AibisDream [SerializeField] private DialogViewType defaultViewType = DialogViewType.StandardBubble; private OptionExploreTracker? _tracker; + private LineInfo? _pendingOptionPrompt; private void Awake() { @@ -32,20 +34,36 @@ namespace AibisDream public override YarnTask OnDialogueStartedAsync() { + ClearPendingOptionPrompt(); return YarnTask.CompletedTask; } public override YarnTask OnDialogueCompleteAsync() { + ClearPendingOptionPrompt(); HideDialog(); return YarnTask.CompletedTask; } public override YarnTask RunLineAsync(LocalizedLine line, LineCancellationToken token) { + if (line.IsOptionPromptLine()) + { + _pendingOptionPrompt = LineInfo.Generate(line); + } + else + { + ClearPendingOptionPrompt(); + } + return YarnTask.CompletedTask; } + public override void OnNodeExit(string nodeName) + { + ClearPendingOptionPrompt(); + } + public void LoadBubbles(BubbleSlotGroupData data) { defaultViewType = data.dialogViewType; @@ -68,6 +86,8 @@ namespace AibisDream optionId => optionSelectedSource.TrySetResult(optionId)); var viewType = GetCurrentViewType(options); + var optionPrompt = _pendingOptionPrompt; + ClearPendingOptionPrompt(); switch (viewType) { @@ -76,7 +96,13 @@ namespace AibisDream defaultOptions.ShowOptions(options); break; case DialogViewType.OnelineBox: - onelineOptions.ShowOptions(options); + onelineOptions.ShowOptions(options, optionPrompt?.lineText); + if (optionPrompt.HasValue) + { + EnumEventSystem.Global.Send( + DialogEventEnum.LineHistoryAppend, + optionPrompt.Value); + } break; case DialogViewType.CenterOption: centerOptions.ShowOptions(options); @@ -106,10 +132,16 @@ namespace AibisDream } catch (TaskCanceledException) { + HideDialog(); return null; } } + private void ClearPendingOptionPrompt() + { + _pendingOptionPrompt = null; + } + private DialogViewType GetCurrentViewType(DialogOption[] options) { var optionViewType = options[0].Character.dialogViewType; diff --git a/Assets/Scripts/Game Loop/EventEnums.cs b/Assets/Scripts/Game Loop/EventEnums.cs index 1ac1a1883..11640df00 100644 --- a/Assets/Scripts/Game Loop/EventEnums.cs +++ b/Assets/Scripts/Game Loop/EventEnums.cs @@ -35,7 +35,8 @@ namespace AibisDream LineShown, LineEnd, OptionShow, - OptionSelected + OptionSelected, + LineHistoryAppend } public enum InteractionEventEnum diff --git a/Assets/Scripts/UI/DialogUI/DialogUIManager.cs b/Assets/Scripts/UI/DialogUI/DialogUIManager.cs index 09c3d387a..40f7d674a 100644 --- a/Assets/Scripts/UI/DialogUI/DialogUIManager.cs +++ b/Assets/Scripts/UI/DialogUI/DialogUIManager.cs @@ -23,6 +23,7 @@ namespace AibisDream } EnumEventSystem.Global.Register(DialogEventEnum.LineStart, AddDialogueLine); + EnumEventSystem.Global.Register(DialogEventEnum.LineHistoryAppend, AddDialogueLine); EnumEventSystem.Global.Register(DialogEventEnum.OptionSelected, AddDialogueLine); EnumEventSystem.Global.Register(EventEnum.NextYarn, CleanDialogHistory); EnumEventSystem.Global.Register(GameLifecycleEvent.SessionEnded, CleanDialogHistory); @@ -33,6 +34,7 @@ namespace AibisDream public override void OnSingletonDestroy() { EnumEventSystem.Global.UnRegister(DialogEventEnum.LineStart, AddDialogueLine); + EnumEventSystem.Global.UnRegister(DialogEventEnum.LineHistoryAppend, AddDialogueLine); EnumEventSystem.Global.UnRegister(DialogEventEnum.OptionSelected, AddDialogueLine); EnumEventSystem.Global.UnRegister(EventEnum.NextYarn, CleanDialogHistory); EnumEventSystem.Global.UnRegister(GameLifecycleEvent.SessionEnded, CleanDialogHistory); diff --git a/Assets/Scripts/UI/DialogUI/Options/OnelineOptions.cs b/Assets/Scripts/UI/DialogUI/Options/OnelineOptions.cs index b71c6e470..9759012b0 100644 --- a/Assets/Scripts/UI/DialogUI/Options/OnelineOptions.cs +++ b/Assets/Scripts/UI/DialogUI/Options/OnelineOptions.cs @@ -1,16 +1,39 @@ -using UnityEngine; +using System.Collections.Generic; using AibisDream.Framework; +using TMPro; +using UnityEngine; namespace AibisDream { public class OnelineOptions : MonoBehaviour { [SerializeField] private GameObject choiceButtonPrefab; + [SerializeField] private Transform optionsRoot; [SerializeField] private bool showDisabledOptions; + [SerializeField] private GameObject optionPromptRoot; + [SerializeField] private TMP_Text optionPrompt; - public void ShowOptions(DialogOption[] dialogOptions) + private readonly List _choiceInstances = new(); + + private Transform OptionsRoot => optionsRoot != null ? optionsRoot : transform; + + private GameObject OptionPromptRoot + { + get + { + if (optionPromptRoot != null) + return optionPromptRoot; + if (optionPrompt != null && optionPrompt.transform.parent != null) + return optionPrompt.transform.parent.gameObject; + return optionPrompt != null ? optionPrompt.gameObject : null; + } + } + + public void ShowOptions(DialogOption[] dialogOptions, string optionPromptText = null) { gameObject.SetActive(true); + ClearOptionButtons(); + ShowOptionPrompt(optionPromptText); // 生成新按钮 foreach (var option in dialogOptions) @@ -19,7 +42,8 @@ namespace AibisDream if (!option.IsAvailable && !showDisabledOptions) continue; // 生成按钮 - var choiceInstance = Instantiate(choiceButtonPrefab, transform); + var choiceInstance = Instantiate(choiceButtonPrefab, OptionsRoot); + _choiceInstances.Add(choiceInstance); var choiceButton = choiceInstance.GetComponent(); choiceButton.Init(); @@ -29,17 +53,60 @@ namespace AibisDream public void HideOptions() { - // 销毁所有按钮 - foreach (Transform child in transform) + ClearOptionButtons(); + SetOptionPromptVisible(false); + } + + private void ShowOptionPrompt(string optionPromptText) + { + var shouldShowPrompt = !string.IsNullOrWhiteSpace(optionPromptText); + if (shouldShowPrompt && optionPrompt != null) { - Destroy(child.gameObject); + optionPrompt.text = optionPromptText; } + + SetOptionPromptVisible(shouldShowPrompt); + } + + private void SetOptionPromptVisible(bool visible) + { + var root = OptionPromptRoot; + if (root != null) + { + root.SetActive(visible); + } + } + + private void ClearOptionButtons() + { + foreach (var choiceInstance in _choiceInstances) + { + if (choiceInstance != null) + { + Destroy(choiceInstance); + } + } + + _choiceInstances.Clear(); + } + + private void OnOptionSelected(LineInfo _) + { + HideOptions(); } private void OnEnable() { - EnumEventSystem.Global.Register(DialogEventEnum.OptionSelected, - _ => HideOptions()); + EnumEventSystem.Global.Register( + DialogEventEnum.OptionSelected, + OnOptionSelected); + } + + private void OnDisable() + { + EnumEventSystem.Global.UnRegister( + DialogEventEnum.OptionSelected, + OnOptionSelected); } } -} \ No newline at end of file +} diff --git a/Assets/Scripts/Utility/YarnUtil.cs b/Assets/Scripts/Utility/YarnUtil.cs index b17d3d4ef..13c2bdd22 100644 --- a/Assets/Scripts/Utility/YarnUtil.cs +++ b/Assets/Scripts/Utility/YarnUtil.cs @@ -10,6 +10,7 @@ namespace AibisDream.Utility public static class YarnUtil { private const string AutoNext = "auto_next"; + public const string OptionPrompt = "option_prompt"; #region 处理行信息 @@ -23,6 +24,14 @@ namespace AibisDream.Utility return line.Metadata != null && line.Metadata.Contains(AutoNext); } + /// + /// 当前行是否应作为下一组梦境选项的提示语。 + /// + public static bool IsOptionPromptLine(this LocalizedLine line) + { + return line.Metadata != null && line.Metadata.Contains(OptionPrompt); + } + /// /// 从line中获取角色vo定义 /// @@ -124,4 +133,4 @@ namespace AibisDream.Utility #endregion } -} \ No newline at end of file +}