添加了对话UI,基本完成对话系统设计

This commit is contained in:
SepComet
2026-02-09 12:56:28 +08:00
parent 197ce38315
commit 26b69bc870
46 changed files with 2598 additions and 11 deletions
@@ -15,10 +15,12 @@ public partial class GameEntry : MonoBehaviour
{
public static BuiltinDataComponent BuiltinData { get; private set; }
public static CombineComponent Combine { get; private set; }
public static DialogComponent Dialog { get; private set; }
private static void InitCustomComponents()
{
BuiltinData = UnityGameFramework.Runtime.GameEntry.GetComponent<BuiltinDataComponent>();
Combine = UnityGameFramework.Runtime.GameEntry.GetComponent<CombineComponent>();
Dialog = UnityGameFramework.Runtime.GameEntry.GetComponent<DialogComponent>();
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0103c893efce444d9c6fe671306746fe
guid: cc0388c578bec9746813380eee9038d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
@@ -0,0 +1,407 @@
using System.Collections.Generic;
using DataTable;
using Definition.Enum;
using Event;
using GameFramework.DataTable;
using GameFramework.Event;
using UI;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace CustomComponent
{
[DisallowMultipleComponent]
public class DialogComponent : GameFrameworkComponent
{
#region Property
private const int DialogChapterDivisor = 1000;
private const int LineChapterDivisor = 100000000;
private const int LineDialogDivisor = 100000;
private readonly Dictionary<int, DRDialog> _dialogMap = new();
private readonly Dictionary<int, List<DRDialogLine>> _dialogLinesMap = new();
private readonly Dictionary<int, int> _dialogFirstLineIdMap = new();
private IDataTable<DRDialog> _dtDialog;
private IDataTable<DRDialogLine> _dtDialogLine;
private DialogFormController _formController;
private DialogFormContext _formContext;
private int _currentChapterId;
private int _currentLineIndex = -1;
private bool _isInitialized;
private bool _isPlaying;
public bool IsInitialized => _isInitialized;
public bool IsPlaying => _isPlaying;
public int CurrentChapterId => _currentChapterId;
public int CurrentDialogId => _formContext != null ? _formContext.DialogId : 0;
public int CurrentLineId => _formContext != null ? _formContext.CurrentLineId : 0;
#endregion
private void Start()
{
GameEntry.Event.Subscribe(DialogNextLineRequestEventArgs.EventId, OnDialogNextLineRequest);
GameEntry.Event.Subscribe(DialogSkipRequestEventArgs.EventId, OnDialogSkipRequest);
GameEntry.Event.Subscribe(DialogStopRequestEventArgs.EventId, OnDialogStopRequest);
}
private void OnDestroy()
{
GameEntry.Event.Unsubscribe(DialogNextLineRequestEventArgs.EventId, OnDialogNextLineRequest);
GameEntry.Event.Unsubscribe(DialogSkipRequestEventArgs.EventId, OnDialogSkipRequest);
GameEntry.Event.Unsubscribe(DialogStopRequestEventArgs.EventId, OnDialogStopRequest);
}
public bool Init(int chapterId)
{
if (chapterId <= 0)
{
Log.Warning("Dialog init failed. chapterId must be positive.");
return false;
}
if (!EnsureDataTables())
{
return false;
}
StopDialog();
_dialogMap.Clear();
_dialogLinesMap.Clear();
_dialogFirstLineIdMap.Clear();
DRDialog[] dialogRows = _dtDialog.GetDataRows((a, b) => a.Id.CompareTo(b.Id));
for (int i = 0; i < dialogRows.Length; i++)
{
DRDialog dialogRow = dialogRows[i];
if (dialogRow == null)
{
continue;
}
if (ParseChapterIdFromDialogId(dialogRow.Id) != chapterId)
{
continue;
}
_dialogMap[dialogRow.Id] = dialogRow;
}
if (_dialogMap.Count == 0)
{
Log.Warning("Dialog init failed. No dialog rows found for chapter '{0}'.", chapterId.ToString());
return false;
}
DRDialogLine[] lineRows = _dtDialogLine.GetDataRows((a, b) => a.Id.CompareTo(b.Id));
for (int i = 0; i < lineRows.Length; i++)
{
DRDialogLine lineRow = lineRows[i];
if (lineRow == null)
{
continue;
}
if (ParseChapterIdFromLineId(lineRow.Id) != chapterId)
{
continue;
}
int dialogId = ParseDialogIdFromLineId(lineRow.Id);
if (!_dialogMap.ContainsKey(dialogId))
{
continue;
}
if (!_dialogLinesMap.TryGetValue(dialogId, out List<DRDialogLine> dialogLines))
{
dialogLines = new List<DRDialogLine>();
_dialogLinesMap.Add(dialogId, dialogLines);
}
dialogLines.Add(lineRow);
}
List<int> invalidDialogIds = new List<int>();
foreach (KeyValuePair<int, DRDialog> dialogPair in _dialogMap)
{
if (!_dialogLinesMap.TryGetValue(dialogPair.Key, out List<DRDialogLine> dialogLines) ||
dialogLines.Count == 0)
{
Log.Warning("Dialog init warning. Dialog '{0}' has no lines and will be ignored.",
dialogPair.Key.ToString());
invalidDialogIds.Add(dialogPair.Key);
continue;
}
dialogLines.Sort((a, b) => a.Id.CompareTo(b.Id));
_dialogFirstLineIdMap[dialogPair.Key] = dialogLines[0].Id;
}
for (int i = 0; i < invalidDialogIds.Count; i++)
{
_dialogMap.Remove(invalidDialogIds[i]);
}
if (_dialogMap.Count == 0)
{
Log.Warning("Dialog init failed. No valid dialog remains for chapter '{0}'.", chapterId.ToString());
return false;
}
EnsureFormController();
_formContext = null;
_currentChapterId = chapterId;
_currentLineIndex = -1;
_isInitialized = true;
_isPlaying = false;
return true;
}
public bool StartDialog(int dialogId)
{
if (!_isInitialized)
{
Log.Warning("Start dialog failed. Dialog component is not initialized.");
return false;
}
if (ParseChapterIdFromDialogId(dialogId) != _currentChapterId)
{
Log.Warning("Start dialog failed. Dialog '{0}' does not belong to chapter '{1}'.", dialogId.ToString(),
_currentChapterId.ToString());
return false;
}
if (!_dialogMap.TryGetValue(dialogId, out DRDialog dialogRow))
{
Log.Warning("Start dialog failed. Dialog '{0}' was not found in current chapter cache.",
dialogId.ToString());
return false;
}
if (!_dialogLinesMap.TryGetValue(dialogId, out List<DRDialogLine> dialogLines) || dialogLines.Count == 0)
{
Log.Warning("Start dialog failed. Dialog '{0}' has no playable lines.", dialogId.ToString());
return false;
}
if (_isPlaying)
{
StopDialog();
}
EnsureFormController();
if (_formContext == null)
{
_formContext = new DialogFormContext();
}
_formContext.ChapterId = _currentChapterId;
_formContext.DialogId = dialogRow.Id;
_formContext.DialogTitle = dialogRow.Title;
_formContext.DialogUIMode = dialogRow.UIMode;
_currentLineIndex = 0;
ApplyLineToContext(dialogLines[_currentLineIndex], _currentLineIndex, dialogLines.Count);
_isPlaying = true;
_formController.OpenUI(_formContext);
_formController.OnDialogStarted(_formContext);
_formController.OnDialogLineChanged(_formContext);
return true;
}
public bool NextLine()
{
if (!_isPlaying)
{
Log.Warning("Next line failed. No dialog is playing.");
return false;
}
if (!TryGetCurrentDialogLines(out List<DRDialogLine> dialogLines))
{
StopDialog();
return false;
}
int nextLineIndex = _currentLineIndex + 1;
if (nextLineIndex >= dialogLines.Count)
{
EndDialogInternal();
return true;
}
_currentLineIndex = nextLineIndex;
ApplyLineToContext(dialogLines[_currentLineIndex], _currentLineIndex, dialogLines.Count);
_formController.OnDialogLineChanged(_formContext);
return true;
}
public bool SkipDialog()
{
if (!_isPlaying)
{
Log.Warning("Skip dialog failed. No dialog is playing.");
return false;
}
EndDialogInternal();
return true;
}
public void StopDialog()
{
if (!_isPlaying)
{
return;
}
EndDialogInternal();
}
public void ClearRuntimeContext()
{
StopDialog();
_dialogMap.Clear();
_dialogLinesMap.Clear();
_dialogFirstLineIdMap.Clear();
_formContext = null;
_currentChapterId = 0;
_currentLineIndex = -1;
_isInitialized = false;
_isPlaying = false;
}
private bool EnsureDataTables()
{
_dtDialog = GameEntry.DataTable.GetDataTable<DRDialog>();
if (_dtDialog == null)
{
Log.Warning("Dialog init failed. Data table DRDialog is missing.");
return false;
}
_dtDialogLine = GameEntry.DataTable.GetDataTable<DRDialogLine>();
if (_dtDialogLine == null)
{
Log.Warning("Dialog init failed. Data table DRDialogLine is missing.");
return false;
}
return true;
}
private void EnsureFormController()
{
if (_formController == null)
{
_formController = new DialogFormController();
}
}
private bool TryGetCurrentDialogLines(out List<DRDialogLine> dialogLines)
{
dialogLines = null;
if (_formContext == null)
{
Log.Warning("Dialog state invalid. Form context is null.");
return false;
}
if (!_dialogLinesMap.TryGetValue(_formContext.DialogId, out dialogLines))
{
Log.Warning("Dialog state invalid. Dialog lines are missing for dialog '{0}'.",
_formContext.DialogId.ToString());
return false;
}
return true;
}
private void EndDialogInternal()
{
_isPlaying = false;
_currentLineIndex = -1;
_formController.OnDialogEnded(_formContext);
_formController.CloseUI();
}
private void ApplyLineToContext(DRDialogLine lineRow, int lineIndex, int totalLines)
{
_formContext.CurrentLineId = lineRow.Id;
_formContext.SpeakerId = lineRow.SpeakerId;
_formContext.SpeakerName = lineRow.SpeakerName;
_formContext.Expression = lineRow.Expression;
_formContext.Direction = lineRow.Direction;
_formContext.Text = lineRow.Text;
_formContext.Emphasis = lineRow.Emphasis;
_formContext.LineIndex = lineIndex;
_formContext.TotalLines = totalLines;
_formContext.IsLastLine = lineIndex >= totalLines - 1;
}
private static int ParseChapterIdFromDialogId(int dialogId)
{
return dialogId / DialogChapterDivisor;
}
private static int ParseChapterIdFromLineId(int lineId)
{
return lineId / LineChapterDivisor;
}
private static int ParseDialogIdFromLineId(int lineId)
{
return lineId / LineDialogDivisor;
}
#region Event Handlers
private void OnDialogNextLineRequest(object sender, GameEventArgs e)
{
if (!(e is DialogNextLineRequestEventArgs))
{
return;
}
NextLine();
}
private void OnDialogSkipRequest(object sender, GameEventArgs e)
{
if (!(e is DialogSkipRequestEventArgs))
{
return;
}
SkipDialog();
}
private void OnDialogStopRequest(object sender, GameEventArgs e)
{
if (!(e is DialogStopRequestEventArgs))
{
return;
}
StopDialog();
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d6174838c30e460429e5628757bbf015
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using CustomUtility;
using Definition;
using GameFramework.Resource;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace CustomComponent
{
public class SpriteCacheComponent : GameFrameworkComponent
{
[SerializeField] private float _pixelsPerUnit = 100f;
[SerializeField] private Vector2 _defaultPivot = new(0.5f, 0.5f);
private Dictionary<string, Sprite> _spriteCache;
private ResourceComponent _resource;
void Start()
{
_spriteCache = new Dictionary<string, Sprite>();
_resource = GameEntry.Resource;
}
public void GetSprite(string assetName, Action<Sprite> callback)
{
if (_spriteCache.TryGetValue(assetName, out var sprite))
{
callback?.Invoke(sprite);
return;
}
else
{
_resource.LoadAsset
(
AssetUtility.GetUIDialogAsset(assetName),
Constant.AssetPriority.UIFormAsset,
new LoadAssetCallbacks(
(resourcePath, asset, duration, userData) =>
{
Log.Debug(resourcePath);
Texture2D texture = asset as Texture2D;
if (texture != null)
{
Sprite newSprite = Sprite.Create(
texture,
new Rect(0, 0, texture.width, texture.height),
_defaultPivot,
_pixelsPerUnit);
_spriteCache.Add(assetName, newSprite);
callback?.Invoke(newSprite);
}
},
(resourcePath, status, errorMessage, userData) =>
{
Log.Error("Can not load icon '{0}' from '{1}' with error message '{2}'.",
assetName,
resourcePath,
errorMessage);
}
)
);
}
}
private void OnDestroy()
{
_spriteCache.Clear();
_resource = null;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 42b4751aefa33c545a86f146a48536ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -21,7 +21,7 @@ namespace DataTable
/// <summary>
/// 获取对话形式。
/// </summary>
public DialogUIMode UIMode { get; private set; }
public DialogFormMode UIMode { get; private set; }
public override bool ParseDataRow(string dataRowString, object userData)
{
@@ -31,7 +31,7 @@ namespace DataTable
m_Id = int.Parse(fields[index++]);
index++;
Title = fields[index++];
UIMode = EnumUtility<DialogUIMode>.Get(fields[index++]);
UIMode = EnumUtility<DialogFormMode>.Get(fields[index++]);
return true;
}
@@ -1,6 +1,6 @@
namespace Definition.Enum
{
public enum DialogUIMode
public enum DialogFormMode
{
None = 0,
@@ -38,5 +38,20 @@ namespace UI
/// 核心玩法A MVC测试界面。
/// </summary>
CombineForm = 103,
/// <summary>
/// 蒙版剧情对话界面。
/// </summary>
MaskDialogForm = 104,
/// <summary>
/// 底部剧情对话界面。
/// </summary>
BottomDialogForm = 105,
/// <summary>
/// 气泡剧情对话界面。
/// </summary>
BubbleDialogForm = 106,
}
}
@@ -0,0 +1,21 @@
using GameFramework;
using GameFramework.Event;
namespace Event
{
public class DialogNextLineRequestEventArgs : GameEventArgs
{
public static readonly int EventId = typeof(DialogNextLineRequestEventArgs).GetHashCode();
public override int Id => EventId;
public static DialogNextLineRequestEventArgs Create()
{
return ReferencePool.Acquire<DialogNextLineRequestEventArgs>();
}
public override void Clear()
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: de8ad9563f91a584da8c775e9fa916b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,21 @@
using GameFramework;
using GameFramework.Event;
namespace Event
{
public class DialogSkipRequestEventArgs : GameEventArgs
{
public static readonly int EventId = typeof(DialogSkipRequestEventArgs).GetHashCode();
public override int Id => EventId;
public static DialogSkipRequestEventArgs Create()
{
return ReferencePool.Acquire<DialogSkipRequestEventArgs>();
}
public override void Clear()
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: df023ccfed92923439c0f66ca1bd457c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,21 @@
using GameFramework;
using GameFramework.Event;
namespace Event
{
public class DialogStopRequestEventArgs : GameEventArgs
{
public static readonly int EventId = typeof(DialogStopRequestEventArgs).GetHashCode();
public override int Id => EventId;
public static DialogStopRequestEventArgs Create()
{
return ReferencePool.Acquire<DialogStopRequestEventArgs>();
}
public override void Clear()
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d49a133afe91ef4e9bc7502e7adc71b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -87,7 +87,11 @@ namespace Procedure
{
base.OnEnter(procedureOwner);
InitializeProcedureState();
//InitializeProcedureState();
GameEntry.Dialog.Init(1);
GameEntry.Dialog.StartDialog(1001);
//GameEntry.Dialog.StartDialog(1002);
}
/// <summary>
@@ -109,12 +113,16 @@ namespace Procedure
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
if (TryUpdateRound(realElapseSeconds))
if (GameEntry.Dialog.IsInitialized)
{
return;
}
TryRestartScene(procedureOwner, realElapseSeconds);
// if (TryUpdateRound(realElapseSeconds))
// {
// return;
// }
//
// TryRestartScene(procedureOwner, realElapseSeconds);
}
#endregion
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d81d2a322c2e93948b4d5aa7e36c99f2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ae5528a06773f5a45b75a99f619c2e22
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,24 @@
using Definition.Enum;
namespace UI
{
public class DialogFormContext : UIContext
{
public int ChapterId = 0;
public int DialogId = 0;
public string DialogTitle = string.Empty;
public DialogFormMode DialogUIMode = DialogFormMode.None;
public int CurrentLineId = 0;
public string SpeakerId = string.Empty;
public string SpeakerName = string.Empty;
public ExpressionType Expression = ExpressionType.None;
public int Direction = 0;
public string Text = string.Empty;
public EmphasisType Emphasis = EmphasisType.None;
public int LineIndex = -1;
public int TotalLines = 0;
public bool IsLastLine = false;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38341401b5804de47bb0a9a1c79d20d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3c721a41db518484e9a1771952725489
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,167 @@
using Definition.Enum;
using GameFramework.Event;
using UnityGameFramework.Runtime;
namespace UI
{
public class DialogFormController : IFormController<DialogFormContext>
{
private DialogFormContext _context;
private DialogFormBase _dialogForm;
private int? _formSerialId;
private bool _pendingRefresh;
public DialogFormController()
{
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OnOpenUIFormSuccess);
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, OnCloseUIFormComplete);
}
public int? OpenUI(DialogFormContext context)
{
if (context == null)
{
Log.Warning("DialogFormController open failed. context is null.");
return null;
}
_context = context;
UIFormId targetFormId = MapDialogFormId(context.DialogUIMode);
if (targetFormId == UIFormId.Undefined)
{
Log.Warning("DialogFormController open failed. Unsupported mode '{0}'.", context.DialogUIMode.ToString());
return null;
}
if (_dialogForm != null && _dialogForm.UIMode == context.DialogUIMode)
{
_dialogForm.StartDialog(context);
return _formSerialId;
}
CloseUI();
_pendingRefresh = true;
_formSerialId = GameEntry.UI.OpenUIForm(targetFormId, context);
return _formSerialId;
}
public void CloseUI()
{
_pendingRefresh = false;
if (_formSerialId.HasValue)
{
GameEntry.UI.CloseUIForm(_formSerialId.Value);
return;
}
if (_dialogForm != null)
{
_dialogForm.Close();
}
}
public void OnDialogStarted(DialogFormContext context)
{
_context = context;
TryRefreshUI();
}
public void OnDialogLineChanged(DialogFormContext context)
{
_context = context;
TryRefreshUI();
}
public void OnDialogEnded(DialogFormContext context)
{
_context = context;
}
~DialogFormController()
{
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OnOpenUIFormSuccess);
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, OnCloseUIFormComplete);
}
private void TryRefreshUI()
{
if (_context == null)
{
return;
}
if (_dialogForm == null)
{
_pendingRefresh = true;
return;
}
_dialogForm.StartDialog(_context);
_pendingRefresh = false;
}
private static UIFormId MapDialogFormId(DialogFormMode mode)
{
switch (mode)
{
case DialogFormMode.Mask:
return UIFormId.MaskDialogForm;
case DialogFormMode.BottomBox:
return UIFormId.BottomDialogForm;
case DialogFormMode.BubbleBox:
return UIFormId.BottomDialogForm;
default:
return UIFormId.Undefined;
}
}
private void OnOpenUIFormSuccess(object sender, GameEventArgs e)
{
if (!(e is OpenUIFormSuccessEventArgs args))
{
return;
}
if (!_formSerialId.HasValue)
{
return;
}
if (args.UIForm == null || args.UIForm.SerialId != _formSerialId.Value || args.UserData != _context)
{
return;
}
_dialogForm = args.UIForm.Logic as DialogFormBase;
if (_dialogForm == null)
{
Log.Warning("DialogFormController open success but form logic is invalid.");
return;
}
if (_pendingRefresh)
{
TryRefreshUI();
}
}
private void OnCloseUIFormComplete(object sender, GameEventArgs e)
{
if (!(e is CloseUIFormCompleteEventArgs args))
{
return;
}
if (args.SerialId != _formSerialId)
{
return;
}
_dialogForm = null;
_formSerialId = null;
_pendingRefresh = false;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 52cc4e85f8030334689fdcff90748161
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c4b7102b2e334264398178de3d6325b4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,144 @@
using Definition.Enum;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityGameFramework.Runtime;
namespace UI
{
public class BottomDialogForm : DialogFormBase
{
public override DialogFormMode UIMode => DialogFormMode.BottomBox;
[SerializeField] private TMP_Text _speakerNameText;
[SerializeField] private TMP_Text _contentText;
[SerializeField] private Image _leftSprite;
[SerializeField] private Image _rightSprite;
[SerializeField] private int _leftSpritePosition = 450;
[SerializeField] private int _rightSpritePosition = -450;
private readonly int _singleSpeakerCenterPosition = Screen.width / 2;
private string _leftSpeakerToken = string.Empty;
private string _rightSpeakerToken = string.Empty;
public override void StartDialog(DialogFormContext context)
{
if (context == null)
{
Log.Warning("BottomDialogForm start failed. context is null.");
return;
}
_context = context;
string speakerName = NormalizeValue(context.SpeakerName);
if (string.IsNullOrEmpty(speakerName))
{
speakerName = NormalizeValue(context.SpeakerId);
}
if (_speakerNameText != null)
{
_speakerNameText.text = speakerName;
}
if (_contentText != null)
{
_contentText.text = NormalizeValue(context.Text);
}
if (string.IsNullOrEmpty(speakerName))
{
ClearSpeakerState();
ApplySpeakerLayout(false, false);
return;
}
bool isRightSpeaker = context.Direction > 0;
if (isRightSpeaker)
{
_rightSpeakerToken = speakerName;
}
else
{
_leftSpeakerToken = speakerName;
}
bool hasLeftSpeaker = !string.IsNullOrEmpty(_leftSpeakerToken);
bool hasRightSpeaker = !string.IsNullOrEmpty(_rightSpeakerToken);
ApplySpeakerLayout(hasLeftSpeaker, hasRightSpeaker);
}
protected override void OnClose(bool isShutdown, object userData)
{
ClearSpeakerState();
ApplySpeakerLayout(false, false);
base.OnClose(isShutdown, userData);
}
private void ClearSpeakerState()
{
_leftSpeakerToken = string.Empty;
_rightSpeakerToken = string.Empty;
}
private void ApplySpeakerLayout(bool hasLeftSpeaker, bool hasRightSpeaker)
{
if (hasLeftSpeaker && hasRightSpeaker)
{
SetSpriteVisible(_leftSprite, true);
SetSpriteVisible(_rightSprite, true);
SetSpritePosition(_leftSprite.rectTransform, _leftSpritePosition);
SetSpritePosition(_rightSprite.rectTransform, _rightSpritePosition);
return;
}
if (hasLeftSpeaker)
{
SetSpriteVisible(_leftSprite, true);
SetSpriteVisible(_rightSprite, false);
SetSpritePosition(_leftSprite.rectTransform, _singleSpeakerCenterPosition);
return;
}
if (hasRightSpeaker)
{
SetSpriteVisible(_leftSprite, false);
SetSpriteVisible(_rightSprite, true);
SetSpritePosition(_rightSprite.rectTransform, -_singleSpeakerCenterPosition);
return;
}
SetSpriteVisible(_leftSprite, false);
SetSpriteVisible(_rightSprite, false);
}
private static void SetSpriteVisible(Image spriteImage, bool visible)
{
if (spriteImage == null)
{
return;
}
spriteImage.gameObject.SetActive(visible);
}
private static void SetSpritePosition(RectTransform rectTransform, float xPosition)
{
if (rectTransform == null)
{
return;
}
Vector2 anchoredPosition = rectTransform.anchoredPosition;
anchoredPosition.x = xPosition;
rectTransform.anchoredPosition = anchoredPosition;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ea6f55294a4cd2946a83649e7be82314
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,71 @@
using Definition.Enum;
using Event;
using UnityEngine;
namespace UI
{
public abstract class DialogFormBase : UGuiForm
{
[SerializeField] protected float _playSpeed;
protected DialogFormContext _context;
public abstract DialogFormMode UIMode { get; }
public abstract void StartDialog(DialogFormContext context);
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
if (!(userData is DialogFormContext context))
{
return;
}
_context = context;
StartDialog(context);
}
protected override void OnClose(bool isShutdown, object userData)
{
_context = null;
base.OnClose(isShutdown, userData);
}
public void OnClickNextLine()
{
GameEntry.Event.Fire(this, DialogNextLineRequestEventArgs.Create());
}
public void OnClickSkipDialog()
{
GameEntry.Event.Fire(this, DialogSkipRequestEventArgs.Create());
}
public void OnClickStopDialog()
{
GameEntry.Event.Fire(this, DialogStopRequestEventArgs.Create());
}
protected static string NormalizeValue(string value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
if (string.Equals(value, "Null", System.StringComparison.OrdinalIgnoreCase))
{
return string.Empty;
}
if (string.Equals(value, "None", System.StringComparison.OrdinalIgnoreCase))
{
return string.Empty;
}
return value;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aafc42873758b514282b3a75e3ba4665
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,38 @@
using Definition.Enum;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityGameFramework.Runtime;
namespace UI
{
public class MaskDialogForm : DialogFormBase
{
public override DialogFormMode UIMode => DialogFormMode.Mask;
[SerializeField] private Image _maskImage;
[SerializeField] private TMP_Text _text;
public override void StartDialog(DialogFormContext context)
{
if (context == null)
{
Log.Warning("MaskDialogForm start failed. context is null.");
return;
}
_context = context;
if (_maskImage != null)
{
_maskImage.gameObject.SetActive(true);
}
if (_text != null)
{
_text.text = NormalizeValue(context.Text);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5a63462640e70af40972b9841beafe6c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -65,5 +65,10 @@ namespace CustomUtility
{
return Utility.Text.Format("Assets/GameMain/UI/UISounds/{0}.wav", assetName);
}
public static string GetUIDialogAsset(string assetName)
{
return Utility.Text.Format("Assets/GameMain/UI/Dialogs/{0}.prefab", assetName);
}
}
}