using System; using AibisDream.Framework; using AibisDream.Kit; using AibisDream.Utility; using Febucci.UI.Core; using TMPro; using Unity.VisualScripting; using UnityEngine; using UnityEngine.UI; namespace AibisDream { public class Bubble : MonoBehaviour, IBubble { private BubbleGroup _bubbleGroup; private TypewriterCore _typewriter; private TMP_Text _actorName; private TMP_Text _lineText; private LayoutElement _layoutElement; private LayoutGroup _layoutGroup; private RectTransform _rect; private Image _bubbleImage; #region 参数 [Header("参数")] [SerializeField] private ActorRole role; [SerializeField] private int idx; [SerializeField] private int maxWidth; [SerializeField] private int minWidth; public ActorRole Role => role; public int Idx => idx; #endregion public event Action OnDisposed; public event Action OnLineShown; public void LoadConfig(BubbleSlotData data, BubbleGroup bubbleGroup = null) { _bubbleGroup = bubbleGroup; InitRefs(); // Dialogue playback is started explicitly by BubbleGroup. OnEnable playback can // resume fully displayed text when a pooled bubble is reactivated and synchronously // emit onTextShowed for the wrong line. _typewriter.startTypewriterMode = TypewriterCore.StartTypewriterMode.OnShowText; role = data.actorRole; idx = data.idx; maxWidth = data.maxWidth; minWidth = data.minWidth; var rect = transform as RectTransform; rect.localPosition = UIManager.GetDialogUIPos(data.slotPosition); // 设置style SetStyle(data.bubbleStyle, data.pivotType); // 注册事件 _typewriter.onTextShowed.AddListener(InvokeLineShown); } private void InvokeLineShown() { OnLineShown?.Invoke(); OnLineShown = null; } private void SetStyle(BubbleStyle bubbleStyle, PivotType pivotType) { _bubbleImage.color = bubbleStyle.imageColor; // padding内容 _layoutGroup.padding = bubbleStyle.BubblePadding; _lineText.margin = bubbleStyle.linePadding; _lineText.fontSize = bubbleStyle.lineFontSize; _actorName.margin = bubbleStyle.actorPadding; _actorName.fontSize = bubbleStyle.actorFontSize; // 是否展示名称 _actorName.gameObject.SetActive(bubbleStyle.hasActorName); // 锚点相关 _rect.pivot = CommonUtil.GetPivotByType(pivotType); var spritePath = bubbleStyle.bubbleSpriteAddress.EndsWith("]") ? $"{bubbleStyle.bubbleSpriteAddress}" : $"{bubbleStyle.bubbleSpriteAddress}[{pivotType}]"; _bubbleImage.sprite = ResourceKit.LoadAssetSync(spritePath); } private void InitRefs() { _typewriter = GetComponentInChildren(); _actorName = transform.Find("名称").GetComponent(); _typewriter.onTextShowed.RemoveAllListeners(); _typewriter.onTextShowed.AddListener(() => EnumEventSystem.Global.Send(DialogEventEnum.LineShown)); // 获取内容文字 _lineText = _typewriter.GetComponent(); _layoutElement = _typewriter.GetComponent(); // 气泡自身 _layoutGroup = GetComponent(); _bubbleImage = GetComponent(); _rect = _bubbleImage.rectTransform; } public void ShowActorName(CharacterVo characterVo) { if (_actorName == null) return; _actorName.text = string.IsNullOrEmpty(characterVo.GetActorName()) ? string.Empty : characterVo.GetActorName(); } public void PrepareForLine() { _typewriter.StopShowingText(); OnLineShown = null; gameObject.SetActive(true); _typewriter.TextAnimator.SetText(string.Empty); } public void ShowLine(string line) { HandleLayout(line); ApplyTextSpeedSnapshot(); _typewriter.ShowText(line); } private void ApplyTextSpeedSnapshot() { _typewriter.resetTypingSpeedAtStartup = false; _typewriter.SetTypewriterSpeed(TextSpeedSettings.CurrentMultiplier); } public bool SkipLine() { if (_typewriter.isShowingText) { _typewriter.SkipTypewriter(); return true; } else { return false; } } public void Hide() { gameObject.SetActive(false); } public void Dispose() { Hide(); _typewriter.onTextShowed.RemoveAllListeners(); OnDisposed?.Invoke(this); OnDisposed = null; } private void HandleLayout(string line) { if (_bubbleGroup == null) { Debug.LogWarning("BubbleGroup为空"); return; } // 先去除富文本标签 var width = DialogUIManager.Instance.EstimateTextWidth(line.RemoveRichTextTags(), _lineText.fontSize); if (width > maxWidth) { _layoutElement.preferredWidth = maxWidth; } else { _layoutElement.preferredWidth = -1; } _layoutElement.minWidth = minWidth; LayoutRebuilder.ForceRebuildLayoutImmediate(_rect); } #if UNITY_EDITOR [Header("测试参数")] public int testMinWidth; public int testMaxWidth; public BubbleStyle testBubbleStyle; public PivotType testPivotType; public void LoadStyle() { InitRefs(); SetStyle(testBubbleStyle, testPivotType); _layoutElement.minWidth = testMinWidth; _layoutElement.preferredWidth = testMaxWidth; } public void SaveStyle() { if (!testBubbleStyle) return; InitRefs(); testBubbleStyle.linePadding = _lineText.margin; testBubbleStyle.lineFontSize = _lineText.fontSize; testBubbleStyle.actorPadding = _actorName.margin; testBubbleStyle.actorFontSize = _actorName.fontSize; testBubbleStyle.BubblePadding = _layoutGroup.padding; testBubbleStyle.hasActorName = _actorName.gameObject.activeSelf; } #endif } public interface IBubble { public ActorRole Role { get; } public int Idx { get; } public event Action OnLineShown; public void PrepareForLine(); public void ShowActorName(CharacterVo characterVo); public void ShowLine(string line); public bool SkipLine(); public void Hide(); } }