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