规范对话数据

This commit is contained in:
2026-04-20 19:49:18 +08:00
parent 0f928f6167
commit ef68ccc9e9
43 changed files with 1574 additions and 1092 deletions
+238 -4
View File
@@ -1,3 +1,5 @@
using DG.Tweening;
using Event;
using UnityEngine;
using UnityEngine.UI;
using UnityGameFramework.Runtime;
@@ -7,8 +9,15 @@ namespace UI
public class BgForm : UGuiForm
{
[SerializeField] private Image _bgImage;
[SerializeField] private Image _fadeMaskImage;
[SerializeField] private float _fadeToBlackDuration = 0.2f;
[SerializeField] private float _fadeFromBlackDuration = 0.25f;
[SerializeField] private Ease _fadeEase = Ease.Linear;
private Sprite _currentSprite;
private string _currentAssetName = string.Empty;
private Tween _fadeTween;
private int _refreshVersion;
protected override void OnOpen(object userData)
{
@@ -25,18 +34,243 @@ namespace UI
protected override void OnClose(bool isShutdown, object userData)
{
_refreshVersion++;
KillFadeTween();
SetFadeMaskAlpha(0f);
_currentSprite = null;
_bgImage.sprite = null;
_currentAssetName = string.Empty;
if (_bgImage != null)
{
_bgImage.sprite = null;
}
base.OnClose(isShutdown, userData);
}
public void RefreshUI(BgFormContext context)
{
if (this._currentSprite == context.Sprite) return;
if (context == null)
{
Log.Warning("BgForm refresh failed. context is null.");
return;
}
this._currentSprite = context.Sprite;
_bgImage.sprite = context.Sprite;
int requestId = context.TransitionRequestId;
if (context.Sprite != null)
{
RefreshWithSprite(context.Sprite, context.BackgroundAssetName, requestId);
return;
}
string targetAssetName = string.IsNullOrWhiteSpace(context.BackgroundAssetName)
? string.Empty
: context.BackgroundAssetName.Trim();
if (string.IsNullOrEmpty(targetAssetName))
{
PublishTransitionCompleted(requestId);
return;
}
if (_currentSprite != null && string.Equals(_currentAssetName, targetAssetName))
{
PublishTransitionCompleted(requestId);
return;
}
if (GameEntry.SpriteCache == null)
{
Log.Warning("BgForm refresh failed. SpriteCache is missing.");
PublishTransitionCompleted(requestId);
return;
}
int version = ++_refreshVersion;
FadeToBlack(() =>
{
if (version != _refreshVersion)
{
return;
}
GameEntry.SpriteCache.GetSprite(targetAssetName, sprite =>
{
if (version != _refreshVersion)
{
return;
}
if (sprite != null)
{
_currentSprite = sprite;
_currentAssetName = targetAssetName;
if (_bgImage != null)
{
_bgImage.sprite = sprite;
}
}
else
{
Log.Warning("BgForm refresh failed. Can not load sprite '{0}'.", targetAssetName);
}
FadeFromBlack(() => { PublishTransitionCompleted(requestId); });
});
});
}
private void RefreshWithSprite(Sprite sprite, string backgroundAssetName, int requestId)
{
if (sprite == null)
{
PublishTransitionCompleted(requestId);
return;
}
bool isSameSprite = _currentSprite == sprite;
bool hasSameAssetName = !string.IsNullOrEmpty(backgroundAssetName) &&
string.Equals(_currentAssetName, backgroundAssetName);
if (isSameSprite && (string.IsNullOrEmpty(backgroundAssetName) || hasSameAssetName))
{
PublishTransitionCompleted(requestId);
return;
}
int version = ++_refreshVersion;
FadeToBlack(() =>
{
if (version != _refreshVersion)
{
return;
}
_currentSprite = sprite;
_currentAssetName = string.IsNullOrWhiteSpace(backgroundAssetName)
? _currentAssetName
: backgroundAssetName.Trim();
if (_bgImage != null)
{
_bgImage.sprite = sprite;
}
FadeFromBlack(() => { PublishTransitionCompleted(requestId); });
});
}
private void FadeToBlack(TweenCallback onComplete)
{
EnsureFadeMask();
if (_fadeMaskImage == null)
{
onComplete?.Invoke();
return;
}
_fadeMaskImage.transform.SetAsLastSibling();
KillFadeTween();
if (_fadeToBlackDuration <= 0f)
{
SetFadeMaskAlpha(1f);
onComplete?.Invoke();
return;
}
_fadeTween = _fadeMaskImage.DOFade(1f, _fadeToBlackDuration)
.SetEase(_fadeEase)
.OnComplete(onComplete);
}
private void FadeFromBlack(TweenCallback onComplete = null)
{
EnsureFadeMask();
if (_fadeMaskImage == null)
{
onComplete?.Invoke();
return;
}
_fadeMaskImage.transform.SetAsLastSibling();
KillFadeTween();
if (_fadeFromBlackDuration <= 0f)
{
SetFadeMaskAlpha(0f);
onComplete?.Invoke();
return;
}
_fadeTween = _fadeMaskImage.DOFade(0f, _fadeFromBlackDuration)
.SetEase(_fadeEase)
.OnComplete(onComplete);
}
private void EnsureFadeMask()
{
if (_fadeMaskImage != null)
{
return;
}
RectTransform parentRect = transform as RectTransform;
if (parentRect == null)
{
return;
}
GameObject maskObject = new GameObject("BgFadeMask", typeof(RectTransform), typeof(Image));
maskObject.transform.SetParent(parentRect, false);
RectTransform maskRect = maskObject.GetComponent<RectTransform>();
if (maskRect != null)
{
maskRect.anchorMin = Vector2.zero;
maskRect.anchorMax = Vector2.one;
maskRect.offsetMin = Vector2.zero;
maskRect.offsetMax = Vector2.zero;
}
_fadeMaskImage = maskObject.GetComponent<Image>();
if (_fadeMaskImage != null)
{
_fadeMaskImage.raycastTarget = false;
_fadeMaskImage.color = Color.clear;
}
}
private void KillFadeTween()
{
if (_fadeTween == null)
{
return;
}
_fadeTween.Kill();
_fadeTween = null;
}
private void SetFadeMaskAlpha(float alpha)
{
if (_fadeMaskImage == null)
{
return;
}
Color color = _fadeMaskImage.color;
color.r = 0f;
color.g = 0f;
color.b = 0f;
color.a = Mathf.Clamp01(alpha);
_fadeMaskImage.color = color;
}
private void PublishTransitionCompleted(int requestId)
{
if (requestId <= 0)
{
return;
}
GameEntry.Event.Fire(this, BgTransitionCompletedEventArgs.Create(requestId));
}
}
}
@@ -33,6 +33,8 @@ namespace UI
[SerializeField] private Image[] _dialogBgImages;
[SerializeField] private GameObject _speakerNameArea;
private readonly int _singleSpeakerCenterPosition = Screen.width / 2;
private string _leftSpeakerToken = string.Empty;
@@ -40,6 +42,16 @@ namespace UI
private Sequence _layoutSequence;
private DialogWindowAlpha _currentWindowAlpha = DialogWindowAlpha.Medium;
private float _currentPlayingSpeed = 10f;
private int _leftPortraitRequestVersion;
private int _rightPortraitRequestVersion;
private static readonly HashSet<string> NonPortraitSpeakerIds =
new HashSet<string>(System.StringComparer.OrdinalIgnoreCase)
{
"Other",
"Subtitle",
"Time"
};
public override void StartDialog(DialogFormContext context)
{
@@ -89,6 +101,11 @@ namespace UI
_speakerArea.SetActive(!string.IsNullOrEmpty(speakerName));
}
if (_speakerNameArea != null)
{
_speakerNameArea.SetActive(!string.IsNullOrEmpty(speakerName) && IsHumanSpeaker(_context.SpeakerId));
}
if (_speakerNameText != null)
{
_speakerNameText.text = speakerName;
@@ -115,6 +132,9 @@ namespace UI
bool hasLeftSpeaker = !string.IsNullOrEmpty(_leftSpeakerToken);
bool hasRightSpeaker = !string.IsNullOrEmpty(_rightSpeakerToken);
UpdateSpeakerPortrait(isRightSpeaker ? _rightSprite : _leftSprite, _context.SpeakerId,
_context.CurrentLineId, isRightSpeaker);
ApplySpeakerLayout(hasLeftSpeaker, hasRightSpeaker, false);
}
@@ -130,6 +150,8 @@ namespace UI
{
_leftSpeakerToken = string.Empty;
_rightSpeakerToken = string.Empty;
_leftPortraitRequestVersion++;
_rightPortraitRequestVersion++;
}
private void KillLayoutTween()
@@ -313,5 +335,82 @@ namespace UI
anchoredPosition.x = xPosition;
rectTransform.anchoredPosition = anchoredPosition;
}
private void UpdateSpeakerPortrait(Image portraitImage, string speakerId, int lineId,
bool isRightSpeaker)
{
if (portraitImage == null)
{
return;
}
int requestVersion = isRightSpeaker ? ++_rightPortraitRequestVersion : ++_leftPortraitRequestVersion;
if (string.IsNullOrWhiteSpace(speakerId) || NonPortraitSpeakerIds.Contains(speakerId))
{
return;
}
string portraitAssetName = $"Characters/{speakerId}-{ResolvePortraitIndex(speakerId, lineId)}.png";
if (GameEntry.SpriteCache == null)
{
return;
}
GameEntry.SpriteCache.GetSprite(portraitAssetName, sprite =>
{
if (isRightSpeaker)
{
if (requestVersion != _rightPortraitRequestVersion || _rightSprite == null)
{
return;
}
if (sprite != null)
{
_rightSprite.sprite = sprite;
}
return;
}
if (requestVersion != _leftPortraitRequestVersion || _leftSprite == null)
{
return;
}
if (sprite != null)
{
_leftSprite.sprite = sprite;
}
});
}
private static int ResolvePortraitIndex(string speakerId, int lineId)
{
unchecked
{
int hash = 17;
hash = hash * 31 + lineId;
hash = hash * 31 + (string.IsNullOrEmpty(speakerId)
? 0
: System.StringComparer.OrdinalIgnoreCase.GetHashCode(speakerId));
int mod = hash % 3;
if (mod < 0)
{
mod += 3;
}
return mod + 1;
}
}
private static bool IsHumanSpeaker(string speakerId)
{
if (string.IsNullOrWhiteSpace(speakerId))
{
return false;
}
return !NonPortraitSpeakerIds.Contains(speakerId);
}
}
}