타이핑 연출
This commit is contained in:
@@ -2,17 +2,21 @@
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
// UI Toolkit 버전 대사 HUD. DialogUI.uxml의 요소(#SpeakerName / #DialogText / #DialogField)를 잡아
|
||||
// 화자 이름 + 대사를 표시한다.
|
||||
// 공개 API(Instance / Show / Hide)는 기존과 동일 — DialogPlayer는 수정 없이 그대로 쓴다.
|
||||
// 화자 이름 + 대사를 표시한다. 대사는 Typewriter로 한 글자씩 드러난다.
|
||||
//
|
||||
// UIDocument의 후속인 PanelRenderer를 쓴다. 요소 참조는 UI 리로드 콜백으로 받는다:
|
||||
// - rootVisualElement가 준비됐는지 매번 확인하던 지연 초기화(EnsureRefs)가 사라진다.
|
||||
// - 플레이 중 UXML을 수정해 UI가 리로드돼도 참조가 자동으로 다시 연결된다.
|
||||
// - 플레이 중 UXML을 수정해 UI가 리로드돼도 참조가 자동으로 다시 연결되고,
|
||||
// 타이핑 중이던 대사도 그 지점부터 이어진다 (표시가 ApplyState 한 곳으로 모여 있어서).
|
||||
[RequireComponent(typeof(PanelRenderer))]
|
||||
public class DialogHud : MonoBehaviour
|
||||
{
|
||||
public static DialogHud Instance { get; private set; }
|
||||
|
||||
[Tooltip("대사의 기본 타이핑 연출. 노드에 Typewriter가 지정돼 있으면 그것이 우선한다. " +
|
||||
"여기까지 비우면 연출 없이 대사가 한 번에 표시된다")]
|
||||
[SerializeField] private TypewriterStyle _defaultStyle;
|
||||
|
||||
private PanelRenderer _panelRenderer;
|
||||
|
||||
private VisualElement _panel; // 대사 패널(#DialogField) — 토글 대상
|
||||
@@ -22,16 +26,24 @@ public class DialogHud : MonoBehaviour
|
||||
// 현재 표시 상태. 리로드로 요소가 새로 만들어졌을 때 이 값으로 복원한다.
|
||||
private bool _visible;
|
||||
private string _speakerText = string.Empty;
|
||||
private string _bodyText = string.Empty;
|
||||
private Typewriter _typewriter;
|
||||
|
||||
// 같은 버전으로 콜백이 중복 호출될 때 헛일을 막는다 (Unity 권장 패턴)
|
||||
private int _uiVersion = -1;
|
||||
|
||||
// 대사가 아직 드러나는 중인가 (DialogPlayer가 진행 입력을 스킵으로 쓸지 판단)
|
||||
public bool IsRevealing => _typewriter != null && _typewriter.IsRevealing;
|
||||
|
||||
// 남은 글자를 즉시 전부 드러낸다 (진행 입력 1번째)
|
||||
public void SkipReveal() => _typewriter?.SkipToEnd();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
|
||||
Instance = this;
|
||||
|
||||
_typewriter = new Typewriter(ApplyState); // 진행될 때마다 화면에 반영
|
||||
|
||||
_panelRenderer = GetComponent<PanelRenderer>();
|
||||
// root가 이미 준비돼 있으면 즉시 호출되고, 이후 UI가 리로드될 때마다 다시 호출된다
|
||||
_panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
@@ -57,25 +69,28 @@ private void OnUIReload(PanelRenderer panelRenderer, VisualElement root, int ver
|
||||
ApplyState(); // 첫 호출에선 _visible=false라 숨김 상태로 시작한다
|
||||
}
|
||||
|
||||
// 화자 이름 + 대사 표시.
|
||||
// 화자 이름 + 대사 표시. 대사는 style(없으면 기본 스타일)로 한 글자씩 드러난다.
|
||||
// - speakerNameOverride가 비어있지 않으면 CharacterData.Name 대신 그 이름을 표시한다 (예: "???")
|
||||
public void Show(CharacterData speaker, string text, string speakerNameOverride = null)
|
||||
// - style은 이 대사만의 연출 (DialogNode.Typewriter). 둘 다 비면 한 번에 표시된다.
|
||||
public void Show(CharacterData speaker, string text, string speakerNameOverride = null,
|
||||
TypewriterStyle style = null)
|
||||
{
|
||||
string speakerName = !string.IsNullOrEmpty(speakerNameOverride) ? speakerNameOverride
|
||||
: speaker != null ? speaker.Name : string.Empty;
|
||||
|
||||
_speakerText = DialogVariables.Format(speakerName); // {key} 토큰 치환
|
||||
_bodyText = DialogVariables.Format(text);
|
||||
_visible = true;
|
||||
ApplyState();
|
||||
|
||||
// Begin이 진행 콜백으로 ApplyState를 부르므로 _visible을 먼저 세워 둔다
|
||||
_typewriter.Begin(DialogVariables.Format(text), style != null ? style : _defaultStyle,
|
||||
destroyCancellationToken);
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
_speakerText = string.Empty;
|
||||
_bodyText = string.Empty;
|
||||
_visible = false;
|
||||
ApplyState();
|
||||
_typewriter.Clear(); // 타이핑 사운드 정지 + ApplyState 호출
|
||||
}
|
||||
|
||||
// 캐시된 상태를 실제 요소에 반영한다.
|
||||
@@ -83,7 +98,19 @@ public void Hide()
|
||||
private void ApplyState()
|
||||
{
|
||||
if (_speakerName != null) _speakerName.text = _speakerText;
|
||||
if (_dialogText != null) _dialogText.text = _bodyText;
|
||||
|
||||
if (_dialogText != null)
|
||||
{
|
||||
_dialogText.text = _typewriter.Composed;
|
||||
|
||||
// 스타일이 색을 덮으면 인라인으로 지정하고, 아니면 인라인을 걷어 USS 색으로 되돌린다
|
||||
var style = _typewriter.Style;
|
||||
if (style != null && style.OverrideColor)
|
||||
_dialogText.style.color = style.TextColor;
|
||||
else
|
||||
_dialogText.style.color = StyleKeyword.Null;
|
||||
}
|
||||
|
||||
if (_panel != null)
|
||||
_panel.style.display = _visible ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user