2026-07-05 글로벌 프리팹 추가
This commit is contained in:
@@ -7,5 +7,5 @@ public class AffectionModifier : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private CharacterData _character;
|
||||
|
||||
public void Add(int delta) => StoryState.AddAffection(_character, delta);
|
||||
public void Add(int delta) => StoryManager.Instance.AddAffection(_character, delta);
|
||||
}
|
||||
|
||||
@@ -22,18 +22,20 @@ public class DialogCondition
|
||||
// affectionTarget: 호감도 조건을 검사할 캐릭터 (보통 대화를 거는 NPC 자신)
|
||||
public bool IsMet(CharacterData affectionTarget)
|
||||
{
|
||||
if (StoryState.MainProgress < MinMainProgress)
|
||||
var story = StoryManager.Instance;
|
||||
|
||||
if (story.MainProgress < MinMainProgress)
|
||||
return false;
|
||||
|
||||
if (MinAffection > 0 && StoryState.GetAffection(affectionTarget) < MinAffection)
|
||||
if (MinAffection > 0 && story.GetAffection(affectionTarget) < MinAffection)
|
||||
return false;
|
||||
|
||||
foreach (var group in RequiredDialogs)
|
||||
if (group != null && !StoryState.IsDialogCompleted(group.name))
|
||||
if (group != null && !story.IsDialogCompleted(group.name))
|
||||
return false;
|
||||
|
||||
foreach (var code in RequiredChoiceCodes)
|
||||
if (!string.IsNullOrEmpty(code) && !StoryState.HasChosen(code))
|
||||
if (!string.IsNullOrEmpty(code) && !story.HasChosen(code))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -19,6 +19,10 @@ public class DialogNode : ScriptableObject
|
||||
//Voice 없음 → LineDuration 대기
|
||||
|
||||
|
||||
[Header("Presentation")]
|
||||
public AudioClip Bgm; // 있으면 이 대사부터 전용 BGM 재생, 비어있으면 기본 BGM으로 복귀
|
||||
public GameObject Vfx; // 이 대사 시작 시 화자 위치에서 1회 재생할 이펙트 프리팹
|
||||
|
||||
[Header("Behavior")]
|
||||
public bool LookAtPlayer;
|
||||
public bool WaitForInput; // true면 LineDuration 무시하고 B버튼(OnDialogNext) 입력까지 대기
|
||||
|
||||
@@ -86,7 +86,7 @@ private int FindPlayableIndex()
|
||||
{
|
||||
var entry = _dialogs[i];
|
||||
if (entry.Group == null) continue;
|
||||
if (!entry.Repeatable && StoryState.IsDialogCompleted(entry.Group.name)) continue;
|
||||
if (!entry.Repeatable && StoryManager.Instance.IsDialogCompleted(entry.Group.name)) continue;
|
||||
if (entry.Condition != null && !entry.Condition.IsMet(_voice.Character)) continue;
|
||||
return i;
|
||||
}
|
||||
@@ -117,10 +117,11 @@ private async Awaitable PlayEntry(DialogEntry entry)
|
||||
|
||||
// 여기까지 왔으면 자연 종료(끝까지 재생) — 이때만 완료로 기록한다.
|
||||
// (중간에 오브젝트 파괴 등으로 끊기면 예외로 빠져나가 기록되지 않음)
|
||||
bool firstTime = StoryState.MarkDialogCompleted(entry.Group.name);
|
||||
var story = StoryManager.Instance;
|
||||
bool firstTime = story.MarkDialogCompleted(entry.Group.name);
|
||||
if (firstTime && entry.ProgressOnComplete > 0)
|
||||
StoryState.MainProgress += entry.ProgressOnComplete;
|
||||
StoryState.Save();
|
||||
story.MainProgress += entry.ProgressOnComplete;
|
||||
story.Save();
|
||||
|
||||
Debug.Log($"[DialogPlayer] 대화 종료: {entry.Group.name}");
|
||||
}
|
||||
@@ -129,6 +130,8 @@ private async Awaitable PlayEntry(DialogEntry entry)
|
||||
IsPlaying = false;
|
||||
if (DialogHud.Instance != null)
|
||||
DialogHud.Instance.Hide();
|
||||
if (SoundManager.Instance != null)
|
||||
SoundManager.Instance.ClearOverrideBGM(); // 대화가 끝나면 기본 BGM으로 복귀
|
||||
RestoreDefaultAnimations();
|
||||
RestoreRotations();
|
||||
}
|
||||
@@ -195,6 +198,28 @@ private async Awaitable PlayNode(DialogNode node)
|
||||
|
||||
RaiseNodeEvent(node.EventKey); // EventKey 있으면 매칭 이벤트 호출
|
||||
|
||||
// 전용 BGM: 설정돼 있으면 교체, 비어 있으면 기본 BGM으로 복귀
|
||||
if (SoundManager.Instance != null)
|
||||
{
|
||||
if (node.Bgm != null)
|
||||
SoundManager.Instance.PlayOverrideBGM(node.Bgm);
|
||||
else
|
||||
SoundManager.Instance.ClearOverrideBGM();
|
||||
}
|
||||
|
||||
// 전용 VFX: 화자 위치에서 1회 재생
|
||||
if (node.Vfx != null)
|
||||
{
|
||||
var speakerObj = node.Speaker != null ? CharacterVoiceObject.Find(node.Speaker) : null;
|
||||
var anchor = speakerObj != null ? speakerObj.transform : transform;
|
||||
var vfx = Instantiate(node.Vfx, anchor.position, anchor.rotation);
|
||||
|
||||
// 파티클이면 재생 길이만큼, 아니면 5초 뒤 자동 제거
|
||||
var ps = vfx.GetComponentInChildren<ParticleSystem>();
|
||||
float life = ps != null ? ps.main.duration + ps.main.startLifetime.constantMax : 5f;
|
||||
Destroy(vfx, life);
|
||||
}
|
||||
|
||||
// 보이스 재생
|
||||
if (node.Voice != null && node.Speaker != null)
|
||||
{
|
||||
@@ -253,7 +278,7 @@ private void RecordChoice(DialogNode node, int index)
|
||||
code = DialogVariables.Format(code); // {token} 치환 → 동적으로 생성된 코드 반영
|
||||
|
||||
if (!string.IsNullOrEmpty(code))
|
||||
StoryState.RecordChoice(code);
|
||||
StoryManager.Instance.RecordChoice(code);
|
||||
}
|
||||
|
||||
private async Awaitable<int> WaitForChoice(DialogNode node)
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace DinoLove.Dialog.GraphTool.Editor
|
||||
// .dlg 그래프 에셋을 기존 런타임 타입(DialogGroup / DialogNode / DialogChoice)으로 변환한다.
|
||||
// 생성된 DialogNode들은 서브에셋으로, DialogGroup이 메인 에셋으로 등록된다.
|
||||
// 따라서 DialogPlayer는 수정 없이 임포트된 .dlg 에셋(= DialogGroup)을 그대로 사용한다.
|
||||
[ScriptedImporter(1, DialogGraph.AssetExtension)]
|
||||
[ScriptedImporter(2, DialogGraph.AssetExtension)] // 버전 올리면 기존 .dlg 에셋이 재임포트됨
|
||||
internal class DialogGraphImporter : ScriptedImporter
|
||||
{
|
||||
public override void OnImportAsset(AssetImportContext ctx)
|
||||
@@ -79,6 +79,8 @@ public override void OnImportAsset(AssetImportContext ctx)
|
||||
dn.Gesture = GetInputPortValue<GestureData>(gn.GetInputPortByName(DialogLineNode.PORT_GESTURE));
|
||||
dn.Expression = GetInputPortValue<ExpressionData>(gn.GetInputPortByName(DialogLineNode.PORT_EXPRESSION));
|
||||
dn.Voice = GetInputPortValue<VoiceClip>(gn.GetInputPortByName(DialogLineNode.PORT_VOICE));
|
||||
dn.Bgm = GetInputPortValue<AudioClip>(gn.GetInputPortByName(DialogLineNode.PORT_BGM));
|
||||
dn.Vfx = GetInputPortValue<GameObject>(gn.GetInputPortByName(DialogLineNode.PORT_VFX));
|
||||
dn.LineDuration = GetInputPortValue<float>(gn.GetInputPortByName(DialogLineNode.PORT_DURATION));
|
||||
dn.LookAtPlayer = GetInputPortValue<bool>(gn.GetInputPortByName(DialogLineNode.PORT_LOOKAT));
|
||||
dn.WaitForInput = GetInputPortValue<bool>(gn.GetInputPortByName(DialogLineNode.PORT_WAITINPUT));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Unity.GraphToolkit.Editor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DinoLove.Dialog.GraphTool.Editor
|
||||
{
|
||||
@@ -18,6 +19,8 @@ internal class DialogLineNode : DialogGraphNode
|
||||
public const string PORT_GESTURE = "Gesture";
|
||||
public const string PORT_EXPRESSION = "Expression";
|
||||
public const string PORT_VOICE = "Voice";
|
||||
public const string PORT_BGM = "Bgm";
|
||||
public const string PORT_VFX = "Vfx";
|
||||
public const string PORT_DURATION = "LineDuration";
|
||||
public const string PORT_LOOKAT = "LookAtPlayer";
|
||||
public const string PORT_WAITINPUT = "WaitForInput";
|
||||
@@ -55,6 +58,10 @@ protected override void OnDefinePorts(IPortDefinitionContext context)
|
||||
context.AddInputPort<GestureData>(PORT_GESTURE).WithDisplayName("Gesture").Build();
|
||||
context.AddInputPort<ExpressionData>(PORT_EXPRESSION).WithDisplayName("Expression").Build();
|
||||
context.AddInputPort<VoiceClip>(PORT_VOICE).WithDisplayName("Voice").Build();
|
||||
context.AddInputPort<AudioClip>(PORT_BGM).WithDisplayName("BGM")
|
||||
.WithTooltip("있으면 이 대사부터 전용 BGM 재생, 비우면 기본 BGM으로 복귀").Build();
|
||||
context.AddInputPort<GameObject>(PORT_VFX).WithDisplayName("VFX Prefab")
|
||||
.WithTooltip("이 대사 시작 시 화자 위치에서 1회 재생할 이펙트 프리팹").Build();
|
||||
context.AddInputPort<float>(PORT_DURATION).WithDisplayName("Line Duration").Build();
|
||||
context.AddInputPort<bool>(PORT_LOOKAT).WithDisplayName("Look At Player").Build();
|
||||
context.AddInputPort<bool>(PORT_WAITINPUT).WithDisplayName("Wait For Input").Build();
|
||||
|
||||
Reference in New Issue
Block a user