This commit is contained in:
2025-11-05 23:59:55 +08:00
parent 80b26ade3b
commit b10e641105
28 changed files with 1089 additions and 1263 deletions
Binary file not shown.
@@ -1,153 +0,0 @@
fileFormatVersion: 2
guid: b939a31a2f1b6524982f857bf92bf218
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
+13 -9
View File
@@ -463,15 +463,19 @@ MonoBehaviour:
m_PostInfinity: 2
m_RotationOrder: 4
_filter:
- 0.007228916
- 0.043749984
- 0.12409639
- 0.20444278
- 0.24096388
- 0.20444278
- 0.12409639
- 0.043749984
- 0.007228916
- 0.0048309183
- 0.016401261
- 0.04532712
- 0.08293076
- 0.12053438
- 0.14946026
- 0.1610306
- 0.14946026
- 0.12053438
- 0.08293076
- 0.04532712
- 0.016401261
- 0.0048309183
--- !u!114 &-6086986437317696584
MonoBehaviour:
m_ObjectHideFlags: 3
+39 -71
View File
@@ -1589,7 +1589,6 @@ Transform:
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1584263514}
- {fileID: 1936621685}
m_Father: {fileID: 1009826577}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@@ -1605,8 +1604,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 8912c53f0d9943940a6bb7e59888ada8, type: 3}
m_Name:
m_EditorClassIdentifier:
quickRunMode: 0
autoRunMode: 0
lineAdvanceInput: {fileID: 1977401368}
--- !u!1 &77816930
GameObject:
m_ObjectHideFlags: 0
@@ -4707,7 +4705,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 1820.0001, y: 980.2295}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &238296904
MonoBehaviour:
@@ -4721,6 +4719,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: a76234ef7ee34400b306a4823081ba62, type: 3}
m_Name:
m_EditorClassIdentifier:
widthEstimation: {fileID: 1318260399}
centerBubble: {fileID: 1403558896}
--- !u!1 &246580517
GameObject:
@@ -6283,7 +6282,7 @@ RectTransform:
m_AnchorMin: {x: 1, y: 0.5}
m_AnchorMax: {x: 1, y: 0.5}
m_AnchoredPosition: {x: 0, y: 540}
m_SizeDelta: {x: 312, y: 150}
m_SizeDelta: {x: 0, y: 150}
m_Pivot: {x: 1, y: 1}
--- !u!114 &367483173
MonoBehaviour:
@@ -10430,9 +10429,9 @@ RectTransform:
- {fileID: 576009914}
m_Father: {fileID: 367483172}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 188, y: -25}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 64, y: 100}
m_Pivot: {x: 1, y: 1}
--- !u!222 &640896811
@@ -16373,9 +16372,9 @@ RectTransform:
- {fileID: 527148464}
m_Father: {fileID: 367483172}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 94, y: -25}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 64, y: 100}
m_Pivot: {x: 1, y: 1}
--- !u!222 &1164645868
@@ -21688,50 +21687,6 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1563634750}
m_CullTransparentMesh: 1
--- !u!1 &1584263513
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1584263514}
- component: {fileID: 1584263515}
m_Layer: 0
m_Name: Line View
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1584263514
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1584263513}
serializedVersion: 2
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: 72512886}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1584263515
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1584263513}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 465bacc132ec6ce4e85f8e68354f60ad, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &1586904152
GameObject:
m_ObjectHideFlags: 0
@@ -22215,7 +22170,8 @@ GameObject:
- component: {fileID: 1604842428}
- component: {fileID: 1604842427}
- component: {fileID: 1604842426}
- component: {fileID: 1604842430}
- component: {fileID: 1604842431}
- component: {fileID: 1604842432}
m_Layer: 5
m_Name: Dialog Canvas
m_TagString: Untagged
@@ -22308,7 +22264,7 @@ RectTransform:
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!114 &1604842430
--- !u!114 &1604842431
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@@ -22317,13 +22273,23 @@ MonoBehaviour:
m_GameObject: {fileID: 1604842425}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c14c52217dac70244b02fe48d9462bae, type: 3}
m_Script: {fileID: 11500000, guid: a38dd6a979b842b280fb73c921999a69, type: 3}
m_Name:
m_EditorClassIdentifier:
widthEstimation: {fileID: 1318260399}
blockDuration: 0.5
autoSkipDelay: 1
autoHideDuration: 0.2
lineView: {fileID: 1829169897}
optionView: {fileID: 381795663}
--- !u!225 &1604842432
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1604842425}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!1 &1605070473
GameObject:
m_ObjectHideFlags: 0
@@ -22441,7 +22407,7 @@ RectTransform:
m_AnchorMin: {x: 1, y: 0.5}
m_AnchorMax: {x: 1, y: 0.5}
m_AnchoredPosition: {x: -20, y: -530}
m_SizeDelta: {x: 101.9, y: 25}
m_SizeDelta: {x: 0, y: 25}
m_Pivot: {x: 1, y: 0}
--- !u!114 &1609388205
MonoBehaviour:
@@ -24679,10 +24645,10 @@ RectTransform:
- {fileID: 238296903}
m_Father: {fileID: 1604842429}
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_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 100}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1829169897
MonoBehaviour:
@@ -24696,7 +24662,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f0d1630fcebe4fb38afd0a2bc7b3112b, type: 3}
m_Name:
m_EditorClassIdentifier:
lineViewTypes: 000000000500000002000000
advanceInput: {fileID: 1977401368}
bubbleGroup: {fileID: 238296904}
--- !u!1 &1846916304
GameObject:
m_ObjectHideFlags: 0
@@ -25220,9 +25188,9 @@ RectTransform:
- {fileID: 1970860741}
m_Father: {fileID: 367483172}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 282, y: -25}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 64, y: 100}
m_Pivot: {x: 1, y: 1}
--- !u!222 &1928871882
@@ -25348,6 +25316,7 @@ MonoBehaviour:
dialogueViews:
- {fileID: 381795663}
- {fileID: 1829169897}
- {fileID: 0}
startNode: Start
startAutomatically: 0
runSelectedOptionAsLine: 0
@@ -26117,7 +26086,6 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 4269da1f8c364e319da84b5fee61bafe, type: 3}
m_Name:
m_EditorClassIdentifier:
advanceKey: 8
--- !u!1 &1982630668
GameObject:
m_ObjectHideFlags: 0
@@ -28761,7 +28729,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
m_IsActive: 1
--- !u!224 &2135943277
RectTransform:
m_ObjectHideFlags: 0
@@ -55,7 +55,7 @@ namespace AibisDream
[YarnCommand("hide_dialog")]
public static void HideDialog()
{
DialogCanvasManager.Instance.HideDialog();
BubbleGroup.Instance.HideDialog();
}
[YarnCommand("fade_in")]
@@ -135,13 +135,6 @@ namespace AibisDream
return panel.FlashRed(duration);
}
[YarnCommand("switch_dialog_viewer")]
public static void SwitchDialogViewer(string viewerTypeStr)
{
var viewType = Enum.Parse<DialogViewType>(viewerTypeStr);
DialogCanvasManager.Instance.SwitchDialogView(viewType);
}
#endregion
}
}
@@ -2,7 +2,6 @@ using System;
using AibisDream.Framework;
using AibisDream.Kit;
using UnityEngine;
using UnityEngine.Events;
using Yarn.Unity;
namespace AibisDream
@@ -12,12 +11,9 @@ namespace AibisDream
private DialogueRunner _dialogueRunner;
private LocalisedLineProvider _lineProvider;
# region controller状态标识
[SerializeField] private LineAdvanceInput lineAdvanceInput;
public bool quickRunMode;
public bool autoRunMode;
# endregion
public AdvanceMode advanceMode;
private void Start()
{
@@ -34,6 +30,28 @@ namespace AibisDream
});
}
private void OnEnable()
{
EnumEventSystem.Global.Register(GameLoopEnum.GameStart, ResetAdvanceMode);
}
private void OnDisable()
{
EnumEventSystem.Global.UnRegister(GameLoopEnum.GameStart, ResetAdvanceMode);
}
private void ResetAdvanceMode()
{
advanceMode = new AdvanceMode
{
isAuto = false,
isQuick = false
};
Time.timeScale = 1f;
lineAdvanceInput.ResetAdvanceMode();
}
#region Yarn控制
/// <summary>
@@ -46,13 +64,12 @@ namespace AibisDream
_lineProvider.InitStringTable(yarnProject.name);
_dialogueRunner.StartDialogue("Start");
}
public void StopDialog()
{
_dialogueRunner.Stop();
StorageSystem.Instance.Clear();
DialogCanvasManager.Instance.HideDialog();
DialogCanvasManager.Instance.SwitchDialogView(DialogViewType.StandardBubble);
DialogUIManager.Instance.HideDialog();
}
public void LoadDialog(YarnProject yarnProject)
@@ -70,13 +87,13 @@ namespace AibisDream
Debug.LogWarning($"对话正在运行中,无法启动新节点: {nodeName}");
return;
}
if (CheckIfNodeExistInCurrentYarnProject(nodeName))
{
_dialogueRunner.StartDialogue(nodeName);
}
}
private bool CheckIfNodeExistInCurrentYarnProject(string nodeName)
{
YarnProject currentProject = _dialogueRunner.yarnProject;
@@ -93,16 +110,56 @@ namespace AibisDream
}
#endregion
/// <summary>
/// 跳到下一句
/// </summary>
public void JumpLine()
#region
public void SwitchAutoMode()
{
if (!DialogCanvasManager.Instance.TrySkipLine())
advanceMode.isAuto = !advanceMode.isAuto;
lineAdvanceInput.SwitchAdvanceHandler(advanceMode.CurrentMode);
}
public void SwitchQuickMode()
{
advanceMode.isQuick = !advanceMode.isQuick;
lineAdvanceInput.SwitchAdvanceHandler(advanceMode.CurrentMode);
Time.timeScale = advanceMode.CurrentMode == AdvanceModeEnum.Quick ? 8f : 1f;
}
#endregion
}
public struct AdvanceMode
{
public bool isAuto;
public bool isQuick;
public AdvanceModeEnum CurrentMode
{
get
{
DialogCanvasManager.Instance.NextStep();
if (isAuto)
{
return AdvanceModeEnum.Auto;
}
else if (isQuick)
{
return AdvanceModeEnum.Quick;
}
else
{
return AdvanceModeEnum.Default;
}
}
}
}
public enum AdvanceModeEnum
{
Default,
Auto,
Quick
}
}
@@ -1,4 +1,5 @@
using AibisDream.Framework;
using System;
using AibisDream.Framework;
using UnityEngine;
namespace AibisDream
@@ -11,82 +12,123 @@ namespace AibisDream
private bool _isDialog;
private LineSyncToken _curSyncToken;
private ILineAdvanceHandler _handler;
#endregion
[SerializeField] private KeyCode advanceKey;
private void OnEnable()
{
RegisterEvents();
}
private void OnDisable()
{
UnRegisterEvents();
}
private ILineAdvanceHandler _defaultHandler = new DefaultAdvanceHandler();
private ILineAdvanceHandler _quickHandler;
private ILineAdvanceHandler _autoHandler = new AutoAdvanceHandler();
public void RunLine(LineSyncToken lineSyncEvent)
{
_curSyncToken = lineSyncEvent;
}
private void Update()
{
_handler.Handle(_curSyncToken, IsInputAvailable);
}
public void SwitchAdvanceHandler(AdvanceModeEnum advanceMode)
{
_handler = advanceMode switch
{
AdvanceModeEnum.Auto => _autoHandler,
AdvanceModeEnum.Quick => _quickHandler,
AdvanceModeEnum.Default => _defaultHandler,
_ => _defaultHandler,
};
}
public void ResetAdvanceMode()
{
_handler = _defaultHandler;
}
#region
private bool IsInputAvailable()
{
return _inGame && _isDialog && _curSyncToken != null && _curSyncToken.state != LineSyncState.Advanced;
}
private void OnAdvanceInput()
#endregion
}
public interface ILineAdvanceHandler
{
void Handle(LineSyncToken token, Func<bool> isAvailable);
static ILineAdvanceHandler Create(bool autoMode)
{
if (!IsInputAvailable())
return autoMode ? new AutoAdvanceHandler() : new DefaultAdvanceHandler();
}
}
public class DefaultAdvanceHandler : ILineAdvanceHandler
{
public void Handle(LineSyncToken token, Func<bool> isAvailable)
{
if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space))
{
if (isAvailable())
{
HandleInput(token);
}
}
}
private void HandleInput(LineSyncToken token)
{
switch (token.state)
{
case LineSyncState.PlayText:
token.TrySkip();
break;
case LineSyncState.WaitForAdvance:
token.TryAdvance();
break;
}
}
}
public class AutoAdvanceHandler : ILineAdvanceHandler
{
public void Handle(LineSyncToken token, Func<bool> isAvailable)
{
if (!isAvailable())
{
return;
}
switch (_curSyncToken.state)
// 如果已经可以继续了,就直接往下
if (token.state == LineSyncState.WaitForAdvance)
{
case LineSyncState.PlayText:
_curSyncToken.TrySkip();
break;
case LineSyncState.WaitForAdvance:
_curSyncToken.TryAdvance();
break;
token.TryAdvance();
}
}
private void Update()
}
public class QuickAdvanceHandler : ILineAdvanceHandler
{
public void Handle(LineSyncToken token, Func<bool> isAvailable)
{
if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space))
if (!isAvailable())
{
OnAdvanceInput();
return;
}
// 加速模式强制快进
if (token.state == LineSyncState.PlayText)
{
token.TrySkip();
}
// 如果已经可以继续了,就直接往下
if (token.state is LineSyncState.AdvanceDelay or LineSyncState.WaitForAdvance)
{
token.ForceAdvance();
}
}
#region
private IUnRegister[] _unRegisters;
private void RegisterEvents()
{
_unRegisters = new IUnRegister[6];
_unRegisters[0] = EnumEventSystem.Global.Register(GameLoopEnum.GameStart, () => _inGame = true);
_unRegisters[1] = EnumEventSystem.Global.Register(GameLoopEnum.UnPauseGame, () => _inGame = true);
_unRegisters[2] = EnumEventSystem.Global.Register(GameLoopEnum.GameQuit, () => _inGame = false);
_unRegisters[3] = EnumEventSystem.Global.Register(GameLoopEnum.PauseGame, () => _inGame = false);
_unRegisters[4] = EnumEventSystem.Global.Register(InteractionEventEnum.DialogStart, () => _isDialog = true);
_unRegisters[5] = EnumEventSystem.Global.Register(InteractionEventEnum.DialogEnd, () => _isDialog = true);
}
private void UnRegisterEvents()
{
foreach (var unRegister in _unRegisters)
{
unRegister.UnRegister();
}
}
#endregion
}
}
@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AibisDream.Utility;
using UnityEngine;
using Yarn.Unity;
namespace AibisDream
{
public class LineRunner : DialogueViewBase
{
[SerializeField] private DialogViewType[] lineViewTypes;
[SerializeField] private LineAdvanceInput advanceInput;
[SerializeField] private BubbleGroup bubbleGroup;
private readonly Dictionary<DialogViewType, ILineView> _screenViewDict = new();
public override void RunLine(LocalizedLine dialogueLine, Action onDialogueLineFinished)
{
LineSyncToken lineSyncEvent;
if (!lineViewTypes.Contains(dialogueLine.GetCharacterVo().dialogViewType))
{
// 手动推进视图
lineSyncEvent = new LineSyncToken(dialogueLine, onDialogueLineFinished);
bubbleGroup.RunLine(lineSyncEvent);
}
else
{
// 自动推进视图
lineSyncEvent = new LineSyncToken(dialogueLine, onDialogueLineFinished, true);
if (_screenViewDict.TryGetValue(dialogueLine.GetCharacterVo().dialogViewType, out var view))
{
view.RunLine(lineSyncEvent);
}
else
{
Debug.LogWarning($"未注册的视图类型: {dialogueLine.GetCharacterVo().dialogViewType}");
}
}
// 最后交给推进处理
advanceInput.RunLine(lineSyncEvent);
}
public void RegisterView(DialogViewType dialogViewType, ILineView view)
{
_screenViewDict.Add(dialogViewType, view);
}
public void UnRegisterView(DialogViewType dialogViewType)
{
_screenViewDict.Remove(dialogViewType);
}
}
public interface ILineView
{
void RunLine(LineSyncToken token);
}
}
@@ -0,0 +1,153 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using AibisDream.Kit;
using AibisDream.Utility;
using UnityEngine;
using Yarn.Unity;
namespace AibisDream
{
public class LineSyncToken
{
private Action _nextStep;
private readonly ITextShown _textShown;
public LineInfo lineInfo;
public LineSyncState state;
public CancellationTokenSource AdvanceDelayToken { get; private set; }
public event Action SkipTextAnima;
public event Action OnAdvance;
public LineSyncToken(LocalizedLine lineInfo, Action nextStep, bool isAutoSkip = false)
{
this.lineInfo = LineInfo.Generate(lineInfo);
_nextStep = nextStep;
state = LineSyncState.PlayText;
if (this.lineInfo.isAutoSkip && isAutoSkip)
{
_textShown = new AutoNextTextShown();
}
else
{
_textShown = new DefaultTextShown();
}
}
public void TrySkip()
{
if (state == LineSyncState.PlayText)
{
SkipTextAnima?.Invoke();
SkipTextAnima = null;
}
else
{
Debug.Log($"line{lineInfo.lineId} 状态为{state.ToString()}, 不能跳过文本");
}
}
public void TryAdvance()
{
if (state == LineSyncState.WaitForAdvance)
{
Advance();
Dispose();
}
else
{
Debug.Log($"line{lineInfo.lineId} 状态为{state.ToString()}, 不能向前推进");
}
}
public void ForceAdvance()
{
// 先中断延时
AdvanceDelayToken?.Cancel();
// 推进
Advance();
Dispose();
}
private void Advance()
{
OnAdvance?.Invoke();
OnAdvance = null;
_nextStep?.Invoke();
state = LineSyncState.Advanced;
}
private void Dispose()
{
_nextStep = null;
SkipTextAnima = null;
}
public async void TextShown()
{
AdvanceDelayToken = new CancellationTokenSource();
await _textShown.OnTextShown(this);
}
}
public struct LineInfo
{
public string lineId;
public string lineText;
public CharacterVo character;
public bool isAutoSkip;
public static LineInfo Generate(LocalizedLine dialogueLine)
{
return new LineInfo
{
lineId = dialogueLine.TextID,
lineText = dialogueLine.TextWithoutCharacterName.Text,
character = dialogueLine.GetCharacterVo(),
isAutoSkip = dialogueLine.IsAutoSkipLine()
};
}
public int CalcDelayTime()
{
var delayTime = lineText.RemoveRichTextTags().Length * ConstRef.CharacterDelayTime;
return CommonUtil.Limit(delayTime, ConstRef.LineMaxDelay, ConstRef.LineMinDelay);
}
}
public interface ITextShown
{
Task OnTextShown(LineSyncToken token);
}
public class DefaultTextShown : ITextShown
{
public async Task OnTextShown(LineSyncToken token)
{
token.state = LineSyncState.AdvanceDelay;
await Task.Delay(token.lineInfo.CalcDelayTime(), token.AdvanceDelayToken.Token);
token.state = LineSyncState.WaitForAdvance;
}
}
public class AutoNextTextShown : ITextShown
{
public async Task OnTextShown(LineSyncToken token)
{
token.state = LineSyncState.AdvanceDelay;
await Task.Delay(token.lineInfo.CalcDelayTime(), token.AdvanceDelayToken.Token);
token.state = LineSyncState.WaitForAdvance;
token.TryAdvance();
}
}
public enum LineSyncState
{
Default,
PlayText,
AdvanceDelay,
WaitForAdvance,
Advanced
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fc70491af0c642d28147d3dbf6a70ca6
timeCreated: 1761551427
+60 -60
View File
@@ -11,65 +11,65 @@ namespace AibisDream
/// </summary>
public class LineView : DialogueViewBase
{
public override void RunLine(LocalizedLine dialogueLine, Action onDialogueLineFinished)
{
string lineId = dialogueLine.TextID;
string shortText = dialogueLine.TextWithoutCharacterName.Text.Length > 30
? dialogueLine.TextWithoutCharacterName.Text.Substring(0, 30) + "..."
: dialogueLine.TextWithoutCharacterName.Text;
Debug.Log($"[对话流程] [{lineId}] ===== 开始处理 =====");
Debug.Log($"[对话流程] [{lineId}] 文本内容: {shortText}");
EnumEventSystem.Global.Send(DialogEventEnum.LineStart, DialogText.GetLine(dialogueLine));
HandleLineOutput(dialogueLine, NextStep);
if (DialogController.Instance.quickRunMode)
{
Debug.Log($"[快速模式] [{lineId}] ===== 快速处理开始 =====");
// 快速模式:直接跳过文本显示并继续
bool skipResult = DialogCanvasManager.Instance.TrySkipLine(true);
Debug.Log($"[快速模式] [{lineId}] TrySkipLine结果: {skipResult}");
EnumEventSystem.Global.Send(DialogEventEnum.LineShown);
Debug.Log($"[快速模式] [{lineId}] LineShown事件已发送");
DialogCanvasManager.Instance.NextStep();
Debug.Log($"[快速模式] [{lineId}] NextStep已调用");
Debug.Log($"[快速模式] [{lineId}] ===== 快速处理完成 =====");
}
else
{
Debug.Log($"[正常模式] [{lineId}] 等待用户交互");
}
Debug.Log($"[对话流程] [{lineId}] ===== 处理完成 =====");
return;
void NextStep()
{
EnumEventSystem.Global.Send(DialogEventEnum.LineEnd);
onDialogueLineFinished();
}
}
private void HandleLineOutput(LocalizedLine dialogueLine, Action onDialogueLineFinished)
{
string lineId = dialogueLine.TextID;
Debug.Log($"[文本显示] [{lineId}] 开始显示文本");
DialogCanvasManager.Instance.ShowLine(dialogueLine.TextWithoutCharacterName.Text,
dialogueLine.GetCharacterVo(), onDialogueLineFinished, dialogueLine.TextID,
dialogueLine.IsAutoSkipLine());
}
public override void RunOptions(DialogueOption[] dialogueOptions, Action<int> onOptionSelected)
{
Debug.Log($"[选项显示] ===== 显示 {dialogueOptions.Length} 个选项 =====");
EnumEventSystem.Global.Send(DialogEventEnum.OptionShow);
var options = DialogOption.GeneOptions(dialogueOptions, onOptionSelected);
DialogCanvasManager.Instance.ShowOptions(options);
}
// public override void RunLine(LocalizedLine dialogueLine, Action onDialogueLineFinished)
// {
// string lineId = dialogueLine.TextID;
// string shortText = dialogueLine.TextWithoutCharacterName.Text.Length > 30
// ? dialogueLine.TextWithoutCharacterName.Text.Substring(0, 30) + "..."
// : dialogueLine.TextWithoutCharacterName.Text;
//
// Debug.Log($"[对话流程] [{lineId}] ===== 开始处理 =====");
// Debug.Log($"[对话流程] [{lineId}] 文本内容: {shortText}");
//
// EnumEventSystem.Global.Send(DialogEventEnum.LineStart, DialogText.GetLine(dialogueLine));
//
// HandleLineOutput(dialogueLine, NextStep);
//
// if (DialogController.Instance.quickRunMode)
// {
// Debug.Log($"[快速模式] [{lineId}] ===== 快速处理开始 =====");
//
// // 快速模式:直接跳过文本显示并继续
// bool skipResult = DialogCanvasManager.Instance.TrySkipLine(true);
// Debug.Log($"[快速模式] [{lineId}] TrySkipLine结果: {skipResult}");
//
// EnumEventSystem.Global.Send(DialogEventEnum.LineShown);
// Debug.Log($"[快速模式] [{lineId}] LineShown事件已发送");
//
// DialogCanvasManager.Instance.NextStep();
// Debug.Log($"[快速模式] [{lineId}] NextStep已调用");
// Debug.Log($"[快速模式] [{lineId}] ===== 快速处理完成 =====");
// }
// else
// {
// Debug.Log($"[正常模式] [{lineId}] 等待用户交互");
// }
//
// Debug.Log($"[对话流程] [{lineId}] ===== 处理完成 =====");
// return;
//
// void NextStep()
// {
// EnumEventSystem.Global.Send(DialogEventEnum.LineEnd);
// onDialogueLineFinished();
// }
// }
//
// private void HandleLineOutput(LocalizedLine dialogueLine, Action onDialogueLineFinished)
// {
// string lineId = dialogueLine.TextID;
// Debug.Log($"[文本显示] [{lineId}] 开始显示文本");
// DialogCanvasManager.Instance.ShowLine(dialogueLine.TextWithoutCharacterName.Text,
// dialogueLine.GetCharacterVo(), onDialogueLineFinished, dialogueLine.TextID,
// dialogueLine.IsAutoSkipLine());
// }
//
// public override void RunOptions(DialogueOption[] dialogueOptions, Action<int> onOptionSelected)
// {
// Debug.Log($"[选项显示] ===== 显示 {dialogueOptions.Length} 个选项 =====");
// EnumEventSystem.Global.Send(DialogEventEnum.OptionShow);
// var options = DialogOption.GeneOptions(dialogueOptions, onOptionSelected);
// DialogCanvasManager.Instance.ShowOptions(options);
// }
}
}
-116
View File
@@ -1,116 +0,0 @@
using System;
using System.Threading.Tasks;
using AibisDream.Kit;
using AibisDream.Utility;
using UnityEngine;
using Yarn.Unity;
namespace AibisDream
{
public class LineViewNew : DialogueViewBase
{
[SerializeField] private LineAdvanceInput advanceInput;
// [SerializeField] private BubbleCenter bubbleCenter;
public override void RunLine(LocalizedLine dialogueLine, Action onDialogueLineFinished)
{
// 预处理Line和Event
var lineSyncEvent = new LineSyncToken(dialogueLine, onDialogueLineFinished);
// bubbleCenter.RunLine(lineSyncEvent);
advanceInput.RunLine(lineSyncEvent);
}
}
public class LineSyncToken
{
public LineInfo lineInfo;
public LineSyncState state;
public Action nextStep;
public event Action SkipTextAnima;
public LineSyncToken(LocalizedLine lineInfo, Action nextStep)
{
this.lineInfo = LineInfo.Generate(lineInfo);
this.nextStep = nextStep;
state = LineSyncState.PlayText;
}
public bool TrySkip()
{
if (state == LineSyncState.PlayText)
{
SkipTextAnima?.Invoke();
SkipTextAnima = null;
return true;
}
else
{
Debug.Log($"line{lineInfo.lineId} 状态为{state.ToString()}, 不能跳过文本");
return false;
}
}
public bool TryAdvance()
{
if (state == LineSyncState.WaitForAdvance)
{
nextStep?.Invoke();
state = LineSyncState.Advanced;
Dispose();
return true;
}
else
{
Debug.Log($"line{lineInfo.lineId} 状态为{state.ToString()}, 不能向前推进");
return false;
}
}
private void Dispose()
{
nextStep = null;
SkipTextAnima = null;
}
public void TextStart()
{
state = LineSyncState.PlayText;
}
public void TextShown()
{
state = LineSyncState.AdvanceDelay;
// 延时
state = LineSyncState.WaitForAdvance;
}
}
public struct LineInfo
{
public string lineId;
public string lineText;
public CharacterVo character;
public bool isAutoSkip;
public static LineInfo Generate(LocalizedLine dialogueLine)
{
return new LineInfo
{
lineId = dialogueLine.TextID,
lineText = dialogueLine.TextWithoutCharacterName.Text,
character = dialogueLine.GetCharacterVo(),
isAutoSkip = dialogueLine.IsAutoSkipLine()
};
}
}
public enum LineSyncState
{
PlayText,
AdvanceDelay,
WaitForAdvance,
Advanced
}
}
@@ -171,7 +171,6 @@ namespace AibisDream.FixSystem
// 加载气泡
var data = FixSystemCenter.SystemDic.Get<ScreenSystem>().BubbleSlotGroup.CollectData();
BubbleGroup.Instance.LoadBubbles(data);
DialogCanvasManager.Instance.SwitchDialogView(DialogViewType.StandardBubble);
// 开启交互
bodyModuleSystem.EnableSystem();
@@ -1,18 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AibisDream.Framework;
using AibisDream.Kit;
using AibisDream.Utility;
using DG.Tweening;
using Febucci.UI;
using Febucci.UI.Core;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace AibisDream.FixSystem
{
public class ScreenSystem : MonoBehaviour, IDialogView
public class ScreenSystem : MonoBehaviour, ILineView
{
#region
@@ -47,15 +46,15 @@ namespace AibisDream.FixSystem
private void Register()
{
FixSystemCenter.SystemDic.Register(this);
DialogCanvasManager.Instance.RegisterDialogView(DialogViewType.Screen, this);
DialogCanvasManager.Instance.RegisterDialogView(DialogViewType.Task, TaskScreen);
DialogUIManager.Instance.RegisterScreenView(DialogViewType.Screen, this);
DialogUIManager.Instance.RegisterScreenView(DialogViewType.Task, TaskScreen);
}
private void OnDestroy()
{
FixSystemCenter.SystemDic.Unregister<ScreenSystem>();
DialogCanvasManager.Instance.UnregisterDialogView(DialogViewType.Screen);
DialogCanvasManager.Instance.UnregisterDialogView(DialogViewType.Task);
DialogUIManager.Instance.UnRegisterScreenView(DialogViewType.Screen);
DialogUIManager.Instance.UnRegisterScreenView(DialogViewType.Task);
}
#region
@@ -82,25 +81,38 @@ namespace AibisDream.FixSystem
}
#endregion
#region
public void ShowLine(string dialogLine, CharacterVo character, Action nextStep, string lineId, bool isAutoSkip = false)
public void RunLine(LineSyncToken token)
{
Debug.Log($"[ScreenSystem] ShowLine - 原始文本: '{dialogLine}'");
if (string.IsNullOrWhiteSpace(dialogLine))
// 空格延长一点避免报错
if (string.IsNullOrWhiteSpace(token.lineInfo.lineText))
{
dialogLine = " ";
// Debug.Log("[ScreenSystem] ShowLine - 空文本被替换为空格");
token.lineInfo.lineText = " ";
}
ShowTitle(character.GetActorName());
// 显示标题
ShowTitle(token.lineInfo.character.GetActorName());
// 显示文本
TypewriterCore textItemTypewriter = AddText(token.lineInfo.lineText);
textItemTypewriter.onTextShowed.AddListener(() => OnTextShown(token, textItemTypewriter));
token.state = LineSyncState.PlayText;
// 强制更新布局
LayoutRebuilder.ForceRebuildLayoutImmediate(textRoot);
}
private void OnTextShown(LineSyncToken syncToken, TypewriterCore typewriter)
{
typewriter.onTextShowed.RemoveAllListeners();
syncToken.TextShown();
}
private TypewriterByCharacter AddText(string newLine)
{
// 生成新文本组件
var textItem = Instantiate(_textItemPrefab, textRoot);
var textItemTypewriter = textItem.GetComponent<TypewriterByCharacter>();
// 估算文本高度
var newLineNum = EstimateLineNum(dialogLine);
var newLineNum = EstimateLineNum(newLine);
var oldLineNum = CalcTotalLineNum();
if (newLineNum + oldLineNum > maxLineNum)
{
@@ -109,57 +121,10 @@ namespace AibisDream.FixSystem
_lineQueue.Enqueue(textItemTypewriter);
// 播放文本
textItemTypewriter.ShowText(dialogLine);
textItemTypewriter.onTextShowed.AddListener(() => AfterLineShown(textItemTypewriter, nextStep));
// 强制更新布局
LayoutRebuilder.ForceRebuildLayoutImmediate(textRoot);
Debug.Log($"[ScreenSystem] ShowLine - 文本播放开始,nextStep是否为null: {nextStep == null}");
}
textItemTypewriter.ShowText(newLine);
private void AfterLineShown(TypewriterByCharacter typewriter, Action nextStop)
{
Debug.Log("[ScreenSystem] AfterLineShown - 文本显示完成");
Debug.Log($"[ScreenSystem] AfterLineShown - nextStop是否为null: {nextStop == null}");
EnumEventSystem.Global.Send(DialogEventEnum.LineShown);
ActionKit.Delay(0.1f, () =>
{
Debug.Log("[ScreenSystem] AfterLineShown - 延迟结束,调用回调");
nextStop?.Invoke();
Debug.Log("[ScreenSystem] AfterLineShown - 回调调用完成");
}).StartGlobal();
typewriter.onTextShowed.RemoveAllListeners();
return textItemTypewriter;
}
public void ShowOptions(DialogOption[] dialogueOptions)
{
Debug.Log("不能在显示屏显示对话");
}
public void HideDialog()
{
// HideDialog应该由其他函数控制
}
public bool TrySkipLine(bool isAutoSkip)
{
// 这里不能跳过对话
return true;
}
public void NextStep()
{
// 这里不能手动跳到下一行
}
public void OnLocalizationChanged(string value)
{
// 屏幕不需要本地化
}
#endregion
private int EstimateLineNum(string text)
{
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using AibisDream.Framework;
using AibisDream.Kit;
using AibisDream.Utility;
using TMPro;
using UnityEngine;
@@ -9,7 +7,7 @@ using UnityEngine.UI;
namespace AibisDream
{
public class TaskScreen : MonoBehaviour, IDialogView
public class TaskScreen : MonoBehaviour, ILineView
{
#region
@@ -24,72 +22,6 @@ namespace AibisDream
_taskListParent = transform.Find("Record");
_taskItemPrefab = ResourceKit.LoadAssetSync<GameObject>(ConstRef.TaskItemName);
}
#region
// TODO 随时切换回头再加,先直接赋值
// public void ShowLine(string dialogLine, CharacterVo character, Action nextStep, string lineId, bool isAutoSkip = false)
// {
// var taskItem = Instantiate(_taskItemPrefab, _taskListParent);
// // 设置本地化数据
//
// var localize = taskItem.GetComponent<LocalizedText>();
// localize.SetLocalizedText(DialogController.Instance.GetCurLocalizedTableName(), lineId);
// _taskDict[lineId] = localize;
//
// EnumEventSystem.Global.Send(DialogEventEnum.LineShown);
// EnumEventSystem.Global.Send(DialogEventEnum.LineEnd);
// nextStep.Invoke();
// LayoutRebuilder.ForceRebuildLayoutImmediate(_taskListParent.GetComponent<RectTransform>());
// }
public void ShowLine(string dialogLine, CharacterVo character, Action nextStep, string lineId,
bool isAutoSkip = false)
{
if (_taskDict.ContainsKey(lineId))
{
EnumEventSystem.Global.Send(DialogEventEnum.LineShown);
nextStep.Invoke();
return;
}
var taskItem = Instantiate(_taskItemPrefab, _taskListParent);
var task = taskItem.GetComponent<TMP_Text>();
task.text = $"\u25cf {dialogLine}";
_taskDict[lineId] = taskItem;
EnumEventSystem.Global.Send(DialogEventEnum.LineShown);
nextStep.Invoke();
LayoutRebuilder.ForceRebuildLayoutImmediate(_taskListParent.GetComponent<RectTransform>());
}
public void ShowOptions(DialogOption[] dialogueOptions)
{
Debug.Log("Task 面板不能显示选项");
}
public void HideDialog()
{
// 无法隐藏
}
public bool TrySkipLine(bool isForceSkip = false)
{
// 直接继续不跳过
return true;
}
public void NextStep()
{
// 这里不能手动跳到下一行
}
public void OnLocalizationChanged(string value)
{
// TODO 有需要可以调整文本
}
#endregion
public void CompleteTask(string lineId)
{
@@ -107,5 +39,23 @@ namespace AibisDream
}
_taskDict.Clear();
}
public void RunLine(LineSyncToken token)
{
if (_taskDict.ContainsKey(token.lineInfo.lineId))
{
EnumEventSystem.Global.Send(DialogEventEnum.LineShown);
token.ForceAdvance();
return;
}
var taskItem = Instantiate(_taskItemPrefab, _taskListParent);
var task = taskItem.GetComponent<TMP_Text>();
task.text = $"\u25cf {token.lineInfo.lineText}";
_taskDict[token.lineInfo.lineId] = taskItem;
token.ForceAdvance();
LayoutRebuilder.ForceRebuildLayoutImmediate(_taskListParent.GetComponent<RectTransform>());
}
}
}
+65 -3
View File
@@ -18,6 +18,8 @@ namespace AibisDream
{
#region
public GameLoopState state;
private TalkSceneSO[] _totalSceneSos;
public RunMode runMode;
@@ -129,7 +131,7 @@ namespace AibisDream
// 音频全部暂停
AudioManager.Instance.PauseAll();
// 关闭Dialog
DialogCanvasManager.Instance.CloseCanvas();
DialogUIManager.Instance.CloseCanvas();
}
public void ContinueGame()
@@ -141,7 +143,7 @@ namespace AibisDream
// 音频继续
AudioManager.Instance.UnPauseAll();
// 打开Dialog
DialogCanvasManager.Instance.OpenCanvas();
DialogUIManager.Instance.OpenCanvas();
}
public void QuitGame()
@@ -177,7 +179,7 @@ namespace AibisDream
// 音频继续
AudioManager.Instance.UnPauseAll();
// 打开Dialog
DialogCanvasManager.Instance.OpenCanvas();
DialogUIManager.Instance.OpenCanvas();
yield return UIManager.Instance.GetPanel<PlayToolPanel>().FadeOutAsync();
}
@@ -240,6 +242,44 @@ namespace AibisDream
{
return _currentTalkSceneSo.Value.name;
}
#region
private IUnRegister[] _unRegisters;
private void OnEnable()
{
RegisterEvents();
}
private void OnDisable()
{
UnRegisterEvents();
}
private void RegisterEvents()
{
_unRegisters = new IUnRegister[6];
_unRegisters[0] = EnumEventSystem.Global.Register(GameLoopEnum.GameStart, () => state.OnGame(true));
_unRegisters[1] = EnumEventSystem.Global.Register(GameLoopEnum.PauseGame, () => state.OnPause(true));
_unRegisters[2] = EnumEventSystem.Global.Register(GameLoopEnum.GameQuit, () => state.OnGame(false));
_unRegisters[3] = EnumEventSystem.Global.Register(GameLoopEnum.UnPauseGame, () => state.OnPause(false));
_unRegisters[4] = EnumEventSystem.Global.Register(InteractionEventEnum.DialogStart, () => state.OnDialog(true));
_unRegisters[5] = EnumEventSystem.Global.Register(InteractionEventEnum.DialogEnd, () => state.OnDialog(true));
}
private void UnRegisterEvents()
{
foreach (var unRegister in _unRegisters)
{
unRegister.UnRegister();
}
}
#endregion
}
/// <summary>
@@ -287,6 +327,28 @@ namespace AibisDream
}
}
public struct GameLoopState
{
public bool isInGame;
public bool isInDialog;
public bool isInPause;
public void OnGame(bool isInGame)
{
this.isInGame = isInGame;
}
public void OnPause(bool isInPause)
{
this.isInPause = isInPause;
}
public void OnDialog(bool isInDialog)
{
this.isInDialog = isInDialog;
}
}
public enum RunMode
{
Product,
-1
View File
@@ -13,7 +13,6 @@ namespace AibisDream
public override void OnSingletonInit()
{
// 加载气泡
DialogCanvasManager.Instance.SwitchDialogView(DialogViewType.StandardBubble);
BubbleGroup.Instance.LoadBubbles(bubbleSlotGroup.CollectData());
}
@@ -46,6 +46,11 @@ namespace AibisDream
_stringEvent.SetEntry(getBool.Invoke() ? data.trueKey : data.falseKey);
}
}
public void FreshState()
{
AfterClick();
}
}
[Serializable]
+2 -187
View File
@@ -1,6 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using AibisDream.Framework;
using AibisDream.Kit;
@@ -12,8 +10,6 @@ namespace AibisDream
{
public class CursorManager : Singleton<CursorManager>
{
private const float DelayTime = 0.1f;
[SerializeField] private GameObject border;
[Header("指针图片")] public Sprite defaultCursor;
@@ -22,7 +18,6 @@ namespace AibisDream
private Image _cursor;
private GraphicRaycaster _raycaster;
private Sprite _curCursor;
private CursorState _stateCache;
#region
@@ -47,7 +42,7 @@ namespace AibisDream
private void InitCursor()
{
Cursor.visible = false;
SwitchCursor(defaultCursor);
_cursor.sprite = defaultCursor;
}
private void OnEnable()
@@ -76,8 +71,6 @@ namespace AibisDream
{
// 此处监听所有可能对鼠标状态产生影响的事件
RegisterGameEvent();
RegisterDialogEvent();
RegisterTriggerEvent();
}
private void RegisterGameEvent()
@@ -111,67 +104,12 @@ namespace AibisDream
private void OnGameQuit()
{
_curCursor = defaultCursor;
_curState = CursorState.Default;
_stateCache = CursorState.Default;
_isInGame = false;
}
private void RegisterDialogEvent()
{
// 对话相关
_unRegisters.Add(EnumEventSystem.Global.Register<DialogEventEnum, DialogText>(DialogEventEnum.LineStart,
OnLineStart));
_unRegisters.Add(EnumEventSystem.Global.Register(DialogEventEnum.LineShown, () => SwitchCursor(nextTalk)));
_unRegisters.Add(EnumEventSystem.Global.Register(DialogEventEnum.LineEnd, OnLineEnd));
_unRegisters.Add(EnumEventSystem.Global.Register(DialogEventEnum.OptionShow, OnOptionShow));
_unRegisters.Add(
EnumEventSystem.Global.Register<DialogEventEnum, DialogText>(DialogEventEnum.OptionSelected,
OnOptionSelect));
}
private void RegisterTriggerEvent()
{
// 交互相关
_unRegisters.Add(EnumEventSystem.Global.Register(InteractionEventEnum.DialogStart, OnInteractiveStart));
_unRegisters.Add(EnumEventSystem.Global.Register(InteractionEventEnum.DialogEnd, OnInteractiveEnd));
_unRegisters.Add(
EnumEventSystem.Global.Register<TriggerEnum, IInteraction>(TriggerEnum.PointerEnter, OnPointerEnter));
_unRegisters.Add(
EnumEventSystem.Global.Register<TriggerEnum, IInteraction>(TriggerEnum.PointerExit, OnPointerExit));
_unRegisters.Add(
EnumEventSystem.Global.Register<TriggerEnum, IInteraction>(TriggerEnum.PointerDown, OnPointerDown));
_unRegisters.Add(
EnumEventSystem.Global.Register<TriggerEnum, IInteraction>(TriggerEnum.PointerUp, OnPointerUp));
}
private void Update()
{
UpdateCursor();
HandleDialog();
}
private void UpdateCursor()
{
// 再判断是不是浮在可交互物上
_cursor.sprite = !_isInGame || IsOverSelected() ? defaultCursor : _curCursor;
}
private void HandleDialog()
{
if (!_isInGame) return;
if (!Input.GetMouseButtonDown(0) && !Input.GetKeyDown(KeyCode.Space)) return;
// 控制对话
if (_isTalking)
{
DialogController.Instance.JumpLine();
}
}
private bool IsOverSelected()
{
if (!EventSystem.current.IsPointerOverGameObject())
@@ -200,129 +138,6 @@ namespace AibisDream
// 在输入状态,要单独处理
}
#region
private void OnLineStart(DialogText line)
{
_isTalking = true;
SwitchCursor(talking);
}
private void OnLineEnd()
{
StopDelayCursor();
_switchCoroutine = StartCoroutine(DelayAction(() =>
{
_isTalking = false;
_curCursor = defaultCursor;
}));
}
private void OnOptionShow()
{
_isTalking = true;
SwitchCursor(option);
}
private void OnOptionSelect(DialogText line)
{
StopDelayCursor();
_switchCoroutine = StartCoroutine(DelayAction(() =>
{
_isTalking = false;
_curCursor = defaultCursor;
}));
}
#endregion
#region
private void OnInteractiveStart()
{
_curState = CursorState.Interactive;
if (EventSystemEx.Instance.pointerOverObj)
{
SwitchCursor(interactive);
}
else
{
SwitchCursor(defaultCursor);
}
}
private void OnInteractiveEnd()
{
_curState = CursorState.Default;
// 退出时把Cursor置为Default
SwitchCursor(defaultCursor);
}
private void OnPointerEnter(IInteraction target)
{
if (_curState == CursorState.Interactive && !_isTalking)
{
// 如果不可交互,就显示不可交互的指针
SwitchCursor(target.IsActive ? interactive : disable);
}
}
private void OnPointerExit(IInteraction target)
{
if (_curState == CursorState.Interactive && !_isTalking)
{
SwitchCursor(defaultCursor);
}
}
private void OnPointerDown(IInteraction target)
{
if (_curState == CursorState.Interactive && !_isTalking)
{
SwitchCursor(target.IsActive ? holding : defaultCursor);
}
}
private void OnPointerUp(IInteraction target)
{
if (_curState == CursorState.Interactive && !_isTalking)
{
// 如果松手时还在物体之上
SwitchCursor(EventSystemEx.Instance.holdingObj == target.GetGameObject() ? interactive : defaultCursor);
}
}
#endregion
#region
/// <summary>
/// 直接切指针
/// </summary>
/// <param name="sprite">指针图片</param>
private void SwitchCursor(Sprite sprite)
{
StopDelayCursor();
_curCursor = sprite;
}
private void StopDelayCursor()
{
if (_switchCoroutine != null)
{
StopCoroutine(_switchCoroutine);
_switchCoroutine = null;
}
}
#endregion
private static IEnumerator DelayAction(Action action)
{
yield return new WaitForSeconds(DelayTime);
action.Invoke();
}
}
public enum CursorState
+1 -1
View File
@@ -140,7 +140,7 @@ namespace AibisDream
private void HandleLayout(string line)
{
// 先去除富文本标签
var width = DialogCanvasManager.Instance.EstimateTextWidth(line.RemoveRichTextTags(), _lineText.fontSize);
var width = BubbleGroup.Instance.EstimateTextWidth(line.RemoveRichTextTags(), _lineText.fontSize);
if (width > maxWidth)
{
_layoutElement.preferredWidth = maxWidth;
@@ -1,18 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AibisDream.Framework;
using AibisDream.Kit;
using AibisDream.Utility;
using TMPro;
using UnityEngine;
namespace AibisDream
{
public class BubbleGroup : Singleton<BubbleGroup>, IDialogView
public class BubbleGroup : Singleton<BubbleGroup>, IDialogView, ILineView
{
[SerializeField] private TMP_Text widthEstimation;
[SerializeField] private CenterBubble centerBubble;
private Dictionary<ActorRole, Bubble[]> _bubbleDict;
private BubbleFactory _bubbleFactory;
private CancellationTokenSource _autoHideToken;
#region
@@ -178,6 +184,10 @@ namespace AibisDream
public void RunLine(LineSyncToken syncToken)
{
// 先中断原来的事件
_autoHideToken?.Cancel();
_autoHideToken = null;
var character = syncToken.lineInfo.character;
if (TryGetBubble(character.role, character.bubbleIdx, out var bubble))
@@ -185,9 +195,11 @@ namespace AibisDream
// 注册事件
bubble.OnLineShown += syncToken.TextShown;
syncToken.SkipTextAnima += () => bubble.SkipLine();
syncToken.OnAdvance += AfterAdvance;
// 播放文字
bubble.ShowActorName(character);
bubble.ShowLine(syncToken.lineInfo.lineText);
syncToken.state = LineSyncState.PlayText;
// 将非选定的Bubble隐藏
HideDialog(bubble);
}
@@ -197,6 +209,28 @@ namespace AibisDream
HideDialog();
}
}
public float EstimateTextWidth(string text, float fontSize)
{
widthEstimation.fontSize = fontSize;
widthEstimation.text = text;
widthEstimation.ForceMeshUpdate();
return widthEstimation.preferredWidth;
}
private async void AfterAdvance()
{
_autoHideToken = new CancellationTokenSource();
try
{
await Task.Delay(ConstRef.AutoHideDelay, _autoHideToken.Token);
HideDialog();
}
catch (TaskCanceledException)
{
// 忽略取消
}
}
#endregion
}
+343 -368
View File
@@ -3,380 +3,355 @@ using System.Collections.Generic;
using AibisDream.Framework;
using AibisDream.Kit;
using AibisDream.UI;
using TMPro;
using UnityEngine;
namespace AibisDream
{
public class DialogCanvasManager : Singleton<DialogCanvasManager>
{
#region
private readonly Dictionary<DialogViewType, IDialogView> _dialogViewDict = new();
private IDialogView _defaultView;
private IDialogView _showingView;
private IActionController _autoNext;
private IActionController _block;
private IActionController _hideDialog;
public TMP_Text widthEstimation;
#endregion
#region
[SerializeField] private float blockDuration;
[SerializeField] private float autoSkipDelay;
[SerializeField] private float autoHideDuration;
private bool _isNextAutoSkip;
private bool _isNextBlocked;
#endregion
private readonly List<IUnRegister> _rmvList = new();
public override void OnSingletonInit()
{
InitEvent();
}
private void Start()
{
// 默认对话框直接加载
var centerText = transform.Find("Center Text").GetComponent<IDialogView>();
RegisterDialogView(DialogViewType.CenterText, centerText);
var centerOption = transform.Find("Center Option").GetComponent<IDialogView>();
RegisterDialogView(DialogViewType.CenterOption, centerOption);
var bubbleGroup = transform.Find("Bubble Group").GetComponent<IDialogView>();
RegisterDialogView(DialogViewType.StandardBubble, bubbleGroup);
_defaultView = _dialogViewDict[DialogViewType.StandardBubble];
}
private void InitEvent()
{
// 记录相关
_rmvList.Add(
EnumEventSystem.Global.Register<DialogEventEnum, DialogText>(DialogEventEnum.LineStart,
AddDialogueLine));
_rmvList.Add(
EnumEventSystem.Global.Register<DialogEventEnum, DialogText>(DialogEventEnum.OptionSelected,
AddDialogueLine));
_rmvList.Add(EnumEventSystem.Global.Register(EventEnum.NextYarn, CleanDialogHistory));
// 文本显示事件
_rmvList.Add(EnumEventSystem.Global.Register(DialogEventEnum.LineShown, OnTextShown));
_rmvList.Add(EnumEventSystem.Global.Register(DialogEventEnum.LineEnd, OnTextEnd));
}
private void OnTextShown()
{
Debug.Log("[事件处理] OnTextShown - 文本显示完成事件");
// 在快速模式下,不设置阻塞,因为快速模式会立即处理
if (DialogController.Instance.quickRunMode)
{
Debug.Log("[事件处理] OnTextShown - 快速模式,跳过阻塞设置");
return;
}
// 阻塞部分 - 只在非快速模式下阻塞,确保文本能够显示
_isNextBlocked = true;
_block = ActionKit.Delay(blockDuration, () =>
{
Debug.Log("[事件处理] OnTextShown - 阻塞时间结束");
_isNextBlocked = false;
_block = null;
}
).Start(this);
// 自动跳过部分 - 只在非快速模式下执行
if (DialogController.Instance.autoRunMode || _isNextAutoSkip)
{
Debug.Log("[事件处理] OnTextShown - 设置自动跳过定时器");
_autoNext = ActionKit.Delay(autoSkipDelay, () =>
{
Debug.Log("[事件处理] OnTextShown - 自动跳过定时器触发");
_autoNext = null;
NextStep();
}).Start(this);
_isNextAutoSkip = false;
}
}
private void OnTextEnd()
{
Debug.Log("[事件处理] OnTextEnd - 文本结束事件");
// 清理阻塞定时器
if (_block != null)
{
Debug.Log("[事件处理] OnTextEnd - 清理阻塞定时器");
_block.Deinit();
_block = null;
}
_isNextBlocked = false;
// 清理自动跳过定时器
if (_autoNext != null)
{
Debug.Log("[事件处理] OnTextEnd - 清理自动跳过定时器");
_autoNext.Deinit();
_autoNext = null;
}
Debug.Log("[事件处理] OnTextEnd - 清理完成");
}
public override void OnSingletonDestroy()
{
// 销毁前处理事件
_rmvList.ForEach(item => item.UnRegister());
}
#region
private void OnEnable()
{
EnumEventSystem.Global.Register(InteractionEventEnum.DialogStart, HideDialog);
}
private void OnDisable()
{
EnumEventSystem.Global.Register(InteractionEventEnum.DialogStart, HideDialog);
}
#endregion
#region
/// <summary>
/// 注册对话框
/// </summary>
/// <param name="viewType"></param>
/// <param name="dialogView"></param>
public void RegisterDialogView(DialogViewType viewType, IDialogView dialogView)
{
_dialogViewDict.Add(viewType, dialogView);
}
/// <summary>
/// 注销对话框
/// </summary>
/// <param name="viewType"></param>
public void UnregisterDialogView(DialogViewType viewType)
{
_dialogViewDict.Remove(viewType);
}
#endregion
#region
public void ShowLine(string dialogLine, CharacterVo character, Action nextStep, string lineId,
bool isAutoSkip = false)
{
Debug.Log($"[DialogCanvasManager] ShowLine - 角色对话框类型: {character.dialogViewType}");
Debug.Log($"[DialogCanvasManager] ShowLine - nextStep是否为null: {nextStep == null}");
_isNextAutoSkip = isAutoSkip;
// 根据DialogViewType确定在哪个对话框显示
if (character.dialogViewType == DialogViewType.Default)
{
Debug.Log("[DialogCanvasManager] ShowLine - 使用默认对话框");
if (_showingView != _defaultView)
{
_showingView?.HideDialog();
}
else
{
_hideDialog?.Deinit();
_hideDialog = null;
}
_showingView = _defaultView;
_showingView.ShowLine(dialogLine, character, nextStep, lineId, isAutoSkip);
}
else if (_dialogViewDict.TryGetValue(character.dialogViewType, out var view))
{
Debug.Log($"[DialogCanvasManager] ShowLine - 使用指定对话框: {view.GetType().Name}");
if (_showingView != view)
{
_showingView?.HideDialog();
}
else
{
_hideDialog?.Deinit();
_hideDialog = null;
}
_showingView = view;
// 对于指定了对话框的,就在指定对话框显示
view.ShowLine(dialogLine, character, nextStep, lineId, isAutoSkip);
}
else
{
Debug.Log($"{character.dialogViewType.ToString()}类型对话框不存在");
}
Debug.Log($"[DialogCanvasManager] ShowLine - 当前_showingView类型: {_showingView?.GetType().Name}");
}
public void ShowOptions(DialogOption[] dialogueOptions)
{
var viewType = dialogueOptions[0].Character.dialogViewType;
// 根据状况选择不同的选择框
if (viewType == DialogViewType.Default)
{
if (_showingView != _defaultView)
{
_showingView?.HideDialog();
}
else
{
_hideDialog?.Deinit();
_hideDialog = null;
}
_showingView = _defaultView;
_showingView.ShowOptions(dialogueOptions);
}
else if(_dialogViewDict.TryGetValue(viewType, out var view))
{
if (_showingView != view)
{
_showingView?.HideDialog();
}
else
{
_hideDialog?.Deinit();
_hideDialog = null;
}
_showingView = view;
_showingView.ShowOptions(dialogueOptions);
}
}
public void HideDialog()
{
_showingView?.HideDialog();
}
public bool TrySkipLine(bool isForceSkip = false)
{
string skipType = isForceSkip ? "强制跳过" : "用户跳过";
Debug.Log($"[跳过操作] {skipType} - 阻塞状态: {_isNextBlocked}");
if (_isNextBlocked)
{
Debug.Log("[跳过操作] ❌ 被阻塞,返回true");
return true;
}
bool result = _showingView != null && _showingView.TrySkipLine(isForceSkip);
Debug.Log($"[跳过操作] {skipType} - 结果: {(result ? " " : " ")}");
return result;
}
public void NextStep()
{
Debug.Log("[对话继续] ===== NextStep开始 =====");
Debug.Log($"[对话继续] 当前显示的对话框类型: {_showingView?.GetType().Name}");
// 在快速模式下,不设置延迟隐藏,避免定时器冲突
if (!DialogController.Instance.quickRunMode)
{
var view = _showingView;
_hideDialog = ActionKit.Delay(autoHideDuration, () =>
{
Debug.Log("[对话继续] 延迟隐藏对话框");
_hideDialog = null;
view?.HideDialog();
}).Start(this);
}
else
{
Debug.Log("[对话继续] 快速模式,跳过延迟隐藏设置");
// 清理可能存在的延迟隐藏定时器
if (_hideDialog != null)
{
_hideDialog.Deinit();
_hideDialog = null;
}
}
Debug.Log("[对话继续] 调用对话框NextStep");
_showingView?.NextStep();
Debug.Log("[对话继续] ===== NextStep完成 =====");
}
public void OnLocalizationChanged(string value)
{
}
public void SwitchDialogView(DialogViewType dialogViewType)
{
if (_dialogViewDict.TryGetValue(dialogViewType, out var view))
{
_defaultView?.HideDialog();
_showingView?.HideDialog();
_defaultView = view;
}
else
{
Debug.Log($"{dialogViewType.ToString()}类型对话框不存在");
}
}
#endregion
#region
private RecordPanel RecordPanel => UIManager.Instance.GetPanel<RecordPanel>();
/// <summary>
/// 记录对话
/// </summary>
/// <param name="line">对话</param>
private void AddDialogueLine(DialogText line)
{
if (line.actor.dialogViewType == DialogViewType.Task)
{
return;
}
RecordPanel.AppendDialogLine(line);
}
private void CleanDialogHistory()
{
RecordPanel.ClearDialog();
}
public void CloseCanvas()
{
gameObject.SetActive(false);
}
public void OpenCanvas()
{
gameObject.SetActive(true);
}
#endregion
public float EstimateTextWidth(string text, float fontSize)
{
widthEstimation.fontSize = fontSize;
widthEstimation.text = text;
widthEstimation.ForceMeshUpdate();
return widthEstimation.preferredWidth;
}
// #region 引用
//
// private readonly Dictionary<DialogViewType, IDialogView> _dialogViewDict = new();
// private IDialogView _defaultView;
// private IDialogView _showingView;
//
// private IActionController _autoNext;
// private IActionController _block;
// private IActionController _hideDialog;
//
// #endregion
//
// #region 参数
//
// [SerializeField] private float blockDuration;
// [SerializeField] private float autoSkipDelay;
// [SerializeField] private float autoHideDuration;
//
// private bool _isNextAutoSkip;
// private bool _isNextBlocked;
//
// #endregion
//
// private readonly List<IUnRegister> _rmvList = new();
//
// public override void OnSingletonInit()
// {
// InitEvent();
// }
//
// private void Start()
// {
// // 默认对话框直接加载
// var centerText = transform.Find("Center Text").GetComponent<IDialogView>();
// RegisterDialogView(DialogViewType.CenterText, centerText);
// var centerOption = transform.Find("Center Option").GetComponent<IDialogView>();
// RegisterDialogView(DialogViewType.CenterOption, centerOption);
// var bubbleGroup = transform.Find("Bubble Group").GetComponent<IDialogView>();
// RegisterDialogView(DialogViewType.StandardBubble, bubbleGroup);
//
// _defaultView = _dialogViewDict[DialogViewType.StandardBubble];
// }
//
// private void InitEvent()
// {
// // 记录相关
// _rmvList.Add(
// EnumEventSystem.Global.Register<DialogEventEnum, DialogText>(DialogEventEnum.LineStart,
// AddDialogueLine));
// _rmvList.Add(
// EnumEventSystem.Global.Register<DialogEventEnum, DialogText>(DialogEventEnum.OptionSelected,
// AddDialogueLine));
// _rmvList.Add(EnumEventSystem.Global.Register(EventEnum.NextYarn, CleanDialogHistory));
//
// // 文本显示事件
// _rmvList.Add(EnumEventSystem.Global.Register(DialogEventEnum.LineShown, OnTextShown));
// _rmvList.Add(EnumEventSystem.Global.Register(DialogEventEnum.LineEnd, OnTextEnd));
// }
//
// private void OnTextShown()
// {
// Debug.Log("[事件处理] OnTextShown - 文本显示完成事件");
//
// // 在快速模式下,不设置阻塞,因为快速模式会立即处理
// if (DialogController.Instance.quickRunMode)
// {
// Debug.Log("[事件处理] OnTextShown - 快速模式,跳过阻塞设置");
// return;
// }
//
// // 阻塞部分 - 只在非快速模式下阻塞,确保文本能够显示
// _isNextBlocked = true;
// _block = ActionKit.Delay(blockDuration, () =>
// {
// Debug.Log("[事件处理] OnTextShown - 阻塞时间结束");
// _isNextBlocked = false;
// _block = null;
// }
// ).Start(this);
//
// // 自动跳过部分 - 只在非快速模式下执行
// if (DialogController.Instance.autoRunMode || _isNextAutoSkip)
// {
// Debug.Log("[事件处理] OnTextShown - 设置自动跳过定时器");
// _autoNext = ActionKit.Delay(autoSkipDelay, () =>
// {
// Debug.Log("[事件处理] OnTextShown - 自动跳过定时器触发");
// _autoNext = null;
// NextStep();
// }).Start(this);
// _isNextAutoSkip = false;
// }
// }
//
// private void OnTextEnd()
// {
// Debug.Log("[事件处理] OnTextEnd - 文本结束事件");
//
// // 清理阻塞定时器
// if (_block != null)
// {
// Debug.Log("[事件处理] OnTextEnd - 清理阻塞定时器");
// _block.Deinit();
// _block = null;
// }
// _isNextBlocked = false;
//
// // 清理自动跳过定时器
// if (_autoNext != null)
// {
// Debug.Log("[事件处理] OnTextEnd - 清理自动跳过定时器");
// _autoNext.Deinit();
// _autoNext = null;
// }
//
// Debug.Log("[事件处理] OnTextEnd - 清理完成");
// }
//
// public override void OnSingletonDestroy()
// {
// // 销毁前处理事件
// _rmvList.ForEach(item => item.UnRegister());
// }
//
// #region 对话结束后隐藏
//
// private void OnEnable()
// {
// EnumEventSystem.Global.Register(InteractionEventEnum.DialogStart, HideDialog);
// }
//
// private void OnDisable()
// {
// EnumEventSystem.Global.Register(InteractionEventEnum.DialogStart, HideDialog);
// }
//
// #endregion
//
// #region 对话框相关处理
//
// /// <summary>
// /// 注册对话框
// /// </summary>
// /// <param name="viewType"></param>
// /// <param name="dialogView"></param>
// public void RegisterDialogView(DialogViewType viewType, IDialogView dialogView)
// {
// _dialogViewDict.Add(viewType, dialogView);
// }
//
// /// <summary>
// /// 注销对话框
// /// </summary>
// /// <param name="viewType"></param>
// public void UnregisterDialogView(DialogViewType viewType)
// {
// _dialogViewDict.Remove(viewType);
// }
//
// #endregion
//
// #region 对话框接口
//
// public void ShowLine(string dialogLine, CharacterVo character, Action nextStep, string lineId,
// bool isAutoSkip = false)
// {
// Debug.Log($"[DialogCanvasManager] ShowLine - 角色对话框类型: {character.dialogViewType}");
// Debug.Log($"[DialogCanvasManager] ShowLine - nextStep是否为null: {nextStep == null}");
//
// _isNextAutoSkip = isAutoSkip;
// // 根据DialogViewType确定在哪个对话框显示
// if (character.dialogViewType == DialogViewType.Default)
// {
// Debug.Log("[DialogCanvasManager] ShowLine - 使用默认对话框");
// if (_showingView != _defaultView)
// {
// _showingView?.HideDialog();
// }
// else
// {
// _hideDialog?.Deinit();
// _hideDialog = null;
// }
//
// _showingView = _defaultView;
//
// _showingView.ShowLine(dialogLine, character, nextStep, lineId, isAutoSkip);
// }
// else if (_dialogViewDict.TryGetValue(character.dialogViewType, out var view))
// {
// Debug.Log($"[DialogCanvasManager] ShowLine - 使用指定对话框: {view.GetType().Name}");
// if (_showingView != view)
// {
// _showingView?.HideDialog();
// }
// else
// {
// _hideDialog?.Deinit();
// _hideDialog = null;
// }
//
// _showingView = view;
//
// // 对于指定了对话框的,就在指定对话框显示
// view.ShowLine(dialogLine, character, nextStep, lineId, isAutoSkip);
// }
// else
// {
// Debug.Log($"{character.dialogViewType.ToString()}类型对话框不存在");
// }
//
// Debug.Log($"[DialogCanvasManager] ShowLine - 当前_showingView类型: {_showingView?.GetType().Name}");
// }
//
// public void ShowOptions(DialogOption[] dialogueOptions)
// {
// var viewType = dialogueOptions[0].Character.dialogViewType;
// // 根据状况选择不同的选择框
// if (viewType == DialogViewType.Default)
// {
// if (_showingView != _defaultView)
// {
// _showingView?.HideDialog();
// }
// else
// {
// _hideDialog?.Deinit();
// _hideDialog = null;
// }
// _showingView = _defaultView;
//
// _showingView.ShowOptions(dialogueOptions);
// }
// else if(_dialogViewDict.TryGetValue(viewType, out var view))
// {
// if (_showingView != view)
// {
// _showingView?.HideDialog();
// }
// else
// {
// _hideDialog?.Deinit();
// _hideDialog = null;
// }
// _showingView = view;
// _showingView.ShowOptions(dialogueOptions);
// }
// }
//
// public void HideDialog()
// {
// _showingView?.HideDialog();
// }
//
// public bool TrySkipLine(bool isForceSkip = false)
// {
// string skipType = isForceSkip ? "强制跳过" : "用户跳过";
// Debug.Log($"[跳过操作] {skipType} - 阻塞状态: {_isNextBlocked}");
//
// if (_isNextBlocked)
// {
// Debug.Log("[跳过操作] ❌ 被阻塞,返回true");
// return true;
// }
//
// bool result = _showingView != null && _showingView.TrySkipLine(isForceSkip);
// Debug.Log($"[跳过操作] {skipType} - 结果: {(result ? "✅ 成功" : "❌ 失败")}");
// return result;
// }
//
// public void NextStep()
// {
// Debug.Log("[对话继续] ===== NextStep开始 =====");
// Debug.Log($"[对话继续] 当前显示的对话框类型: {_showingView?.GetType().Name}");
//
// // 在快速模式下,不设置延迟隐藏,避免定时器冲突
// if (!DialogController.Instance.quickRunMode)
// {
// var view = _showingView;
// _hideDialog = ActionKit.Delay(autoHideDuration, () =>
// {
// Debug.Log("[对话继续] 延迟隐藏对话框");
// _hideDialog = null;
// view?.HideDialog();
// }).Start(this);
// }
// else
// {
// Debug.Log("[对话继续] 快速模式,跳过延迟隐藏设置");
// // 清理可能存在的延迟隐藏定时器
// if (_hideDialog != null)
// {
// _hideDialog.Deinit();
// _hideDialog = null;
// }
// }
//
// Debug.Log("[对话继续] 调用对话框NextStep");
// _showingView?.NextStep();
//
// Debug.Log("[对话继续] ===== NextStep完成 =====");
// }
//
// public void OnLocalizationChanged(string value)
// {
// }
//
// #endregion
//
// #region 对话回顾
//
// private RecordPanel RecordPanel => UIManager.Instance.GetPanel<RecordPanel>();
//
// /// <summary>
// /// 记录对话
// /// </summary>
// /// <param name="line">对话</param>
// private void AddDialogueLine(DialogText line)
// {
// if (line.actor.dialogViewType == DialogViewType.Task)
// {
// return;
// }
//
// RecordPanel.AppendDialogLine(line);
// }
//
// private void CleanDialogHistory()
// {
// RecordPanel.ClearDialog();
// }
//
// public void CloseCanvas()
// {
// gameObject.SetActive(false);
// }
//
// public void OpenCanvas()
// {
// gameObject.SetActive(true);
// }
//
// #endregion
}
public enum DialogViewType
@@ -0,0 +1,44 @@
using AibisDream.Kit;
using UnityEngine;
namespace AibisDream
{
public class DialogUIManager : Singleton<DialogUIManager>
{
private CanvasGroup _canvasGroup;
[SerializeField] private LineRunner lineView;
[SerializeField] private OptionView optionView;
public override void OnSingletonInit()
{
_canvasGroup = GetComponent<CanvasGroup>();
}
public void CloseCanvas()
{
gameObject.SetActive(false);
}
public void OpenCanvas()
{
gameObject.SetActive(true);
}
public void HideDialog()
{
// 关闭所有对话框
BubbleGroup.Instance.HideDialog();
}
public void RegisterScreenView(DialogViewType dialogViewType, ILineView screenView)
{
lineView.RegisterView(dialogViewType, screenView);
}
public void UnRegisterScreenView(DialogViewType dialogViewType)
{
lineView.UnRegisterView(dialogViewType);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a38dd6a979b842b280fb73c921999a69
timeCreated: 1761295222
+16 -11
View File
@@ -8,6 +8,9 @@ namespace AibisDream.UI
public bool IsOpen => gameObject.activeSelf;
public bool IsCloseable => false;
private BoolButton _autoBtn;
private BoolButton _ffwdBtn;
#region
public void Show()
@@ -45,25 +48,27 @@ namespace AibisDream.UI
topBar.Find("Record").GetComponent<Button>().onClick.AddListener(OpenRecordPanel);
// 自动跳过和快进包括状态变换
var auto = topBar.Find("Auto Skip").GetComponent<BoolButton>();
auto.OnClick.AddListener(AutoSkip);
auto.getBool = () => DialogController.Instance.autoRunMode;
var ffwd = topBar.Find("Quick Forward").GetComponent<BoolButton>();
ffwd.OnClick.AddListener(QuickForward);
ffwd.getBool = () => DialogController.Instance.quickRunMode;
_autoBtn = topBar.Find("Auto Skip").GetComponent<BoolButton>();
_autoBtn.OnClick.AddListener(AutoSkip);
_autoBtn.getBool = () => DialogController.Instance.advanceMode.CurrentMode == AdvanceModeEnum.Auto;
_ffwdBtn = topBar.Find("Quick Forward").GetComponent<BoolButton>();
_ffwdBtn.OnClick.AddListener(QuickForward);
_ffwdBtn.getBool = () => DialogController.Instance.advanceMode.CurrentMode == AdvanceModeEnum.Quick;
}
private void AutoSkip()
{
DialogController.Instance.autoRunMode = !DialogController.Instance.autoRunMode;
DialogController.Instance.SwitchAutoMode();
_autoBtn.FreshState();
_ffwdBtn.FreshState();
}
private void QuickForward()
{
DialogController.Instance.quickRunMode = !DialogController.Instance.quickRunMode;
Time.timeScale = DialogController.Instance.quickRunMode ? 8f : 1f;
DialogController.Instance.SwitchQuickMode();
_ffwdBtn.FreshState();
_autoBtn.FreshState();
}
private void OpenSettingPanel()
+15 -59
View File
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Unity.VisualScripting;
@@ -72,6 +71,21 @@ namespace AibisDream.Utility
return target;
}
public static int Limit(int target, int max, int min)
{
if (target <= min)
{
return min;
}
if (target >= max)
{
return max;
}
return target;
}
/// <summary>
/// 步进循环
/// </summary>
@@ -194,64 +208,6 @@ namespace AibisDream.Utility
return textArr.Length > 1 ? textArr[1].Trim() : lineText;
}
/// <summary>
/// 拆分字符串为若干行
/// </summary>
/// <param name="input"></param>
/// <param name="maxCharsPerLine"></param>
/// <returns></returns>
public static string SplitString(string input, int maxCharsPerLine)
{
if (string.IsNullOrEmpty(input) || maxCharsPerLine <= 0)
{
return input;
}
var result = new StringBuilder();
var lines = input.Replace("<br>", "\n").Split('\n');
foreach (var line in lines)
{
var currentIndex = 0;
while (currentIndex < line.Length)
{
var length = Math.Min(maxCharsPerLine, line.Length - currentIndex);
var substring = line.Substring(currentIndex, length);
// 检查是否需要调整长度以避免在单词中间断开
if (currentIndex + length < line.Length && !char.IsWhiteSpace(line[currentIndex + length]) &&
!char.IsPunctuation(line[currentIndex + length]))
{
var lastSpace = substring.LastIndexOf(' ');
if (lastSpace > 0)
{
length = lastSpace;
substring = line.Substring(currentIndex, length);
}
}
result.Append(substring.TrimStart());
currentIndex += length;
if (currentIndex < line.Length)
{
result.Append('\n');
}
}
result.Append('\n');
}
// Remove the last '\n' if needed
if (result.Length > 0)
{
result.Length--;
}
return result.ToString();
}
public static Vector2 GetPivotByType(PivotType pivotType)
{
return pivotType switch
+10 -4
View File
@@ -62,15 +62,21 @@ namespace AibisDream.Utility
#endregion
#region
public const float TweenEndY = -10;
public const float FallSpeed = 10;
public const float TargetAspect = 16f / 9f;
public const float PanelDelayDuration = 0.4f;
#endregion
#region ()
public const int CharacterDelayTime = 20;
public const int LineMaxDelay = 1000;
public const int LineMinDelay = 200;
public const int AutoHideDelay = 300;
#endregion
}
}