수정사항
This commit is contained in:
@@ -23,6 +23,10 @@ public class DialogPlayer : MonoBehaviour
|
||||
// 대화 중 제스처/표정을 재생한 Animator들의 원래 상태 — 대화 종료 시 전부 복원.
|
||||
// (끼어든 다른 NPC의 Animator도 포함되므로 딕셔너리로 추적한다)
|
||||
private readonly Dictionary<Animator, (int gestureHash, int expressionHash, bool hasExpression)> _touchedAnimators = new();
|
||||
|
||||
// 대화 중 포즈를 바꾼 캐릭터들의 원래 포즈 — 대화 종료 시 전부 복원.
|
||||
// (끼어든 다른 NPC의 포즈도 포함되므로 Animator와 같은 방식으로 딕셔너리에 추적한다)
|
||||
private readonly Dictionary<CharacterPoses, Pose> _touchedPoses = new();
|
||||
public bool IsPlaying { get; private set; }
|
||||
|
||||
// 지금 실제 대사를 재생 중인 DialogPlayer (전역 1개) — 이때 다른 NPC의 Play()는 무시된다.
|
||||
@@ -72,6 +76,10 @@ private void OnDestroy()
|
||||
// 선택 메뉴를 띄우지 않는다 — 메뉴는 플레이어가 "말을 건" 경우의 UI다.
|
||||
public void PlayAuto() => _ = Play(autoSelect: true);
|
||||
|
||||
// 「이야기한다」 버튼용 — 선택 메뉴를 띄우고 대화를 시작한다.
|
||||
// PlayAuto와 달리 후보가 여럿이면 DialogEnterHud로 고르게 한다. (UnityEvent 연결용 void)
|
||||
public void Talk() => _ = Play();
|
||||
|
||||
// autoSelect가 true면 후보가 여럿이어도 메뉴 없이 최상단(우선순위 1위) 비트를 바로 재생한다.
|
||||
public async Awaitable Play(bool autoSelect = false)
|
||||
{
|
||||
@@ -202,12 +210,6 @@ private async Awaitable PlayBeat(StoryBeat beat)
|
||||
// 도달한 종료 노드가 정한다. 중간에 끊기면(씬 전환 등) false로 남아 장소 BGM으로 복귀한다.
|
||||
bool keepBgmOnEnd = false;
|
||||
|
||||
// 대화 전에 떠 있던 화면 그림 — 끝나면 이 상태로 되돌린다.
|
||||
// 제스처·표정 Animator를 되돌리는 것과 같은 취지다: 대화는 장소 화면을 잠시 빌려 쓸 뿐이라
|
||||
// 장소에 서 있던 인물이 대화 한 번 했다고 사라지거나 표정이 굳어 있으면 안 된다.
|
||||
var frontBefore = FrontSprite.Instance != null ? FrontSprite.Instance.Current : null;
|
||||
var sideBefore = SideSprite.Instance != null ? SideSprite.Instance.Current : null;
|
||||
|
||||
try
|
||||
{
|
||||
var node = beat.Group.StartNode;
|
||||
@@ -291,10 +293,11 @@ private async Awaitable PlayBeat(StoryBeat beat)
|
||||
if (SoundManager.Instance != null && !keepBgmOnEnd)
|
||||
SoundManager.Instance.ClearOverrideBGM();
|
||||
|
||||
// 화면 그림 복원 — Show(null)은 Hide()와 같으므로 "대화 전에 아무것도 없었다"도 그대로 처리된다
|
||||
if (FrontSprite.Instance != null) FrontSprite.Instance.Show(frontBefore);
|
||||
if (SideSprite.Instance != null) SideSprite.Instance.Show(sideBefore);
|
||||
// 앞/옆 그림은 대화 전용 오버레이라 대화가 끝나면 무조건 걷는다
|
||||
if (FrontSprite.Instance != null) FrontSprite.Instance.Hide();
|
||||
if (SideSprite.Instance != null) SideSprite.Instance.Hide();
|
||||
|
||||
RestoreDefaultPoses();
|
||||
RestoreDefaultAnimations();
|
||||
}
|
||||
}
|
||||
@@ -351,8 +354,8 @@ private async Awaitable<bool> PlayNode(DialogNode node)
|
||||
if (node.Sfx != null && SoundManager.Instance != null)
|
||||
SoundManager.Instance.PlaySFX(node.Sfx);
|
||||
|
||||
// 화면 그림(앞/옆) — BGM과 같은 규칙으로 비우면 변경 없음
|
||||
ApplySprites(node);
|
||||
// 포즈 + 화면 그림
|
||||
ApplyStageVisuals(node);
|
||||
|
||||
// 보이스 재생
|
||||
if (node.Voice != null && node.Speaker != null)
|
||||
@@ -389,21 +392,53 @@ private async Awaitable<bool> PlayNode(DialogNode node)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 노드가 지정한 화면 그림을 반영한다.
|
||||
// 비어 있는 지정은 "변경 없음"이라(BGM과 같은 규칙) 인물이 서 있는 동안 매 대사에 다시 꽂을 필요가 없다.
|
||||
// 지우기를 먼저, 지정을 나중에 처리한다 — 한 노드에서 둘 다 걸면 새 그림이 이긴다.
|
||||
private static void ApplySprites(DialogNode node)
|
||||
// 노드가 지정한 무대 연출(포즈 + 앞/옆 그림)을 반영한다. 셋의 수명 규칙이 각각 다르다:
|
||||
// Pose — 지속. '변경 없음'이 기본이라 인물이 서 있는 동안 다시 꽂을 필요가 없다
|
||||
// FrontSprite — 1회성. 지정 안 한 노드에선 사라진다 ("이의있음!" 같은 번쩍 연출)
|
||||
// SideSprite — 지정한 대사에서만. 비우면 사라진다
|
||||
// 뒤의 둘은 Show(null)이 곧 Hide라 그대로 넘기면 두 경우가 한 줄로 처리된다.
|
||||
private void ApplyStageVisuals(DialogNode node)
|
||||
{
|
||||
var front = FrontSprite.Instance;
|
||||
var side = SideSprite.Instance;
|
||||
bool hasFront = front != null;
|
||||
bool hasSide = side != null;
|
||||
if (FrontSprite.Instance != null) FrontSprite.Instance.Show(node.FrontSprite);
|
||||
if (SideSprite.Instance != null) SideSprite.Instance.Show(node.SideSprite);
|
||||
|
||||
if (hasFront && node.ClearSprite is SpriteClear.Front or SpriteClear.Both) front.Hide();
|
||||
if (hasSide && node.ClearSprite is SpriteClear.Side or SpriteClear.Both) side.Hide();
|
||||
if (node.Pose == Pose.Keep) return; // 변경 없음 — 지금 포즈를 그대로 둔다
|
||||
|
||||
if (hasFront && node.FrontSprite != null) front.Show(node.FrontSprite);
|
||||
if (hasSide && node.SideSprite != null) side.Show(node.SideSprite);
|
||||
// 대상을 안 정했으면 아무 일도 하지 않는다.
|
||||
// 화자나 장소 인물로 자동 유추할 수도 있지만, 그러면 "누가 바뀌는지"가 노드만 봐서는
|
||||
// 보이지 않는다. 말하는 사람과 화면에 비치는 사람이 다를 수 있는 이상 명시를 요구하는 편이 낫다.
|
||||
if (node.PoseTarget == null)
|
||||
{
|
||||
Debug.LogWarning($"[DialogPlayer] Pose({node.Pose})를 지정했으나 Pose Target이 비어 있음 — 무시");
|
||||
return;
|
||||
}
|
||||
|
||||
var poses = FindPoses(node.PoseTarget);
|
||||
if (poses == null)
|
||||
{
|
||||
Debug.LogWarning($"[DialogPlayer] {node.PoseTarget.name}의 CharacterPoses를 찾지 못함 — 포즈 무시");
|
||||
return;
|
||||
}
|
||||
|
||||
// 대화 종료 시 복원할 원래 포즈를 처음 건드릴 때만 기억한다
|
||||
if (!_touchedPoses.ContainsKey(poses)) _touchedPoses[poses] = poses.Current;
|
||||
poses.Apply(node.Pose);
|
||||
}
|
||||
|
||||
// 포즈를 적용할 대상의 CharacterPoses. 그 캐릭터가 지금 씬에 없으면 null.
|
||||
// 제스처/표정과 달리 화자를 전혀 보지 않는다 — 대상은 노드가 명시한 것 하나뿐이다.
|
||||
private static CharacterPoses FindPoses(CharacterData target)
|
||||
{
|
||||
var obj = CharacterVoiceObject.Find(target);
|
||||
return obj != null ? obj.GetComponentInChildren<CharacterPoses>(includeInactive: true) : null;
|
||||
}
|
||||
|
||||
// 대화 중 포즈를 바꾼 모든 캐릭터(끼어든 NPC 포함)를 원래 포즈로 복원
|
||||
private void RestoreDefaultPoses()
|
||||
{
|
||||
foreach (var kvp in _touchedPoses)
|
||||
if (kvp.Key != null) kvp.Key.Apply(kvp.Value);
|
||||
_touchedPoses.Clear();
|
||||
}
|
||||
|
||||
// 호감도 분기 노드의 조건식 평가.
|
||||
|
||||
Reference in New Issue
Block a user