添加了对话UI,基本完成对话系统设计
This commit is contained in:
@@ -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
-1
@@ -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
-1
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user