From 24134838534c5a44b4cbeb5ea59a8ff40f1a348b Mon Sep 17 00:00:00 2001 From: nakjun Date: Thu, 30 Jul 2026 12:46:37 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B8=80=EB=A1=9C=EB=B2=8C=20=EC=98=A4?= =?UTF-8?q?=EB=B8=8C=EC=A0=9D=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/01_Scenes/TestScene.unity | 4 +- .../Communication/Dialog/DialogNode.cs | 30 ++- .../Communication/Dialog/DialogPlayer.cs | 37 ++- .../GraphTool/Editor/DialogAffectionNode.cs | 2 +- .../Dialog/GraphTool/Editor/DialogEndNode.cs | 32 +++ .../GraphTool/Editor/DialogEndNode.cs.meta | 2 + .../Dialog/GraphTool/Editor/DialogGraph.cs | 5 + .../GraphTool/Editor/DialogGraphImporter.cs | 21 +- .../Dialog/GraphTool/Editor/DialogLineNode.cs | 7 +- Assets/02_Scripts/GlobalObject.cs | 18 ++ Assets/02_Scripts/GlobalObject.cs.meta | 2 + Assets/02_Scripts/Managers/LocationManager.cs | 6 + Assets/02_Scripts/Managers/SoundManager.cs | 2 +- Assets/02_Scripts/Story/LocationData.cs | 4 + Assets/02_Scripts/_UI/Typewriter.cs | 37 ++- Assets/03_Shaders.meta | 8 + Assets/04_Models.meta | 8 + Assets/04_Models/GlobalObjects.prefab | 3 + Assets/04_Models/GlobalObjects.prefab.meta | 7 + Assets/07_Data/DialogGraph/TestDialog.dlg | 222 ++++++++++++++++-- Assets/07_Data/Locations.meta | 8 + Assets/07_Data/Locations/TestLocation.asset | 3 + .../07_Data/Locations/TestLocation.asset.meta | 8 + 23 files changed, 425 insertions(+), 51 deletions(-) create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogEndNode.cs create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogEndNode.cs.meta create mode 100644 Assets/02_Scripts/GlobalObject.cs create mode 100644 Assets/02_Scripts/GlobalObject.cs.meta create mode 100644 Assets/03_Shaders.meta create mode 100644 Assets/04_Models.meta create mode 100644 Assets/04_Models/GlobalObjects.prefab create mode 100644 Assets/04_Models/GlobalObjects.prefab.meta create mode 100644 Assets/07_Data/Locations.meta create mode 100644 Assets/07_Data/Locations/TestLocation.asset create mode 100644 Assets/07_Data/Locations/TestLocation.asset.meta diff --git a/Assets/01_Scenes/TestScene.unity b/Assets/01_Scenes/TestScene.unity index f6b5fb5..110e5b8 100644 --- a/Assets/01_Scenes/TestScene.unity +++ b/Assets/01_Scenes/TestScene.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:277cc60cb67a35ecf3445ce1561e0b815adb10fec99c401854373883070da098 -size 19587 +oid sha256:abccd4ada0664ca3d00ecaa3a0f8a4d1b7a54100b225540882161489af785313 +size 23502 diff --git a/Assets/02_Scripts/Communication/Dialog/DialogNode.cs b/Assets/02_Scripts/Communication/Dialog/DialogNode.cs index 6020681..99f5938 100644 --- a/Assets/02_Scripts/Communication/Dialog/DialogNode.cs +++ b/Assets/02_Scripts/Communication/Dialog/DialogNode.cs @@ -1,9 +1,20 @@ using System.Collections.Generic; using UnityEngine; +// 노드의 역할. 서로 배타적이므로 플래그 여러 개가 아니라 하나의 종류로 둔다. +public enum DialogNodeKind +{ + [InspectorName("대사")] Line, // 평범한 대사 노드 + [InspectorName("호감도 분기")] AffectionCheck, // 대사 없이 조건만 검사해 즉시 라우팅 + [InspectorName("종료")] End, // 대화를 끝낸다 (BGM 처리만 정함) +} + [CreateAssetMenu(menuName = "Communication/Dialog Node")] public class DialogNode : ScriptableObject { + [Tooltip("이 노드의 역할. 그래프에서 어떤 노드를 썼는지에 따라 임포터가 정한다")] + public DialogNodeKind Kind = DialogNodeKind.Line; + [Header("Speaker")] public CharacterData Speaker; @@ -18,7 +29,13 @@ public class DialogNode : ScriptableObject // 진행은 항상 플레이어 입력(OnDialogNext)으로 넘어간다. [Header("Presentation")] - public AudioClip Bgm; // 있으면 이 대사부터 전용 BGM 재생, 비어있으면 기본 BGM으로 복귀 + [Tooltip("있으면 이 대사부터 이 BGM으로 갈아탄다. 비우면 변경 없음 — " + + "다시 바꾸기 전까지 계속 이어진다")] + public AudioClip Bgm; + + [Tooltip("이 대사가 시작될 때 한 번 재생할 효과음 (문 여는 소리, 쿵 소리 등). " + + "BGM과 달리 이어지지 않고 1회성이다")] + public AudioClip Sfx; [Tooltip("이 대사만의 타이핑 연출(속도·글자색·타이핑 사운드). " + "비우면 DialogHud의 기본 스타일을 쓴다")] @@ -36,17 +53,18 @@ public class DialogNode : ScriptableObject public List HiddenBranches = new(); [Header("Affection Branch")] - [Tooltip("켜면 이 노드는 대사 없이 호감도 조건만 검사해 즉시 라우팅한다: " + - "조건을 만족하면 AffectionPassBranch로, 아니면 Next로")] - public bool AffectionCheck; - - [Tooltip("검사할 호감도 조건들 (캐릭터·연산자·값 + 다음 조건과의 and/or). " + + [Tooltip("(Kind = 호감도 분기 전용) 검사할 호감도 조건들 (캐릭터·연산자·값 + 다음 조건과의 and/or). " + "캐릭터를 비우면 대화 주인 NPC. AND가 OR보다 우선. 비어 있으면 무조건 통과")] public List AffectionRequirements = new(); [Tooltip("조건을 만족했을 때 갈 노드 (실패하면 Next로)")] public DialogNode AffectionPassBranch; + [Header("End")] + [Tooltip("(Kind = 종료 전용) 켜면 대화가 끝나도 마지막에 재생 중이던 BGM을 유지한다. " + + "끄면 장소 BGM으로 복귀. 종료 노드마다 따로 정할 수 있어서 분기별로 다르게 끝낼 수 있다")] + public bool KeepBgmOnEnd; + [Header("ChoiceQuestion")] [TextArea(2,5)] public string ChoiceQuestion; diff --git a/Assets/02_Scripts/Communication/Dialog/DialogPlayer.cs b/Assets/02_Scripts/Communication/Dialog/DialogPlayer.cs index f0c746e..17a1268 100644 --- a/Assets/02_Scripts/Communication/Dialog/DialogPlayer.cs +++ b/Assets/02_Scripts/Communication/Dialog/DialogPlayer.cs @@ -185,14 +185,25 @@ private async Awaitable SelectBeat(List playable) private async Awaitable PlayBeat(StoryBeat beat) { + // 도달한 종료 노드가 정한다. 중간에 끊기면(씬 전환 등) false로 남아 장소 BGM으로 복귀한다. + bool keepBgmOnEnd = false; + try { var node = beat.Group.StartNode; int routingHops = 0; // 연속 라우팅 횟수 — 라우팅 노드끼리 순환하면 대기 없는 무한 루프가 되므로 차단 while (node != null) { + // 종료 노드 — 대화를 끝낸다. 분기마다 다른 종료 노드를 둘 수 있어서 + // "이 루트는 BGM 유지, 저 루트는 복귀"가 각각 정해진다. + if (node.Kind == DialogNodeKind.End) + { + keepBgmOnEnd = node.KeepBgmOnEnd; + break; + } + // 호감도 라우팅 노드 — 대사 없이 즉시 분기 (플레이어에겐 분기 자체가 보이지 않는다) - if (node.AffectionCheck) + if (node.Kind == DialogNodeKind.AffectionCheck) { if (++routingHops > 100) { @@ -254,8 +265,12 @@ private async Awaitable PlayBeat(StoryBeat beat) { if (DialogHud.Instance != null) DialogHud.Instance.Hide(); - if (SoundManager.Instance != null) - SoundManager.Instance.ClearOverrideBGM(); // 대화가 끝나면 기본 BGM으로 복귀 + + // 대화 BGM 처리 — 도달한 종료 노드가 Keep BGM이면 마지막 곡을 그대로 유지하고, + // 아니면 장소 BGM으로 복귀한다. + if (SoundManager.Instance != null && !keepBgmOnEnd) + SoundManager.Instance.ClearOverrideBGM(); + RestoreDefaultAnimations(); } } @@ -303,14 +318,14 @@ private async Awaitable PlayNode(DialogNode node) if (node.Progress != 0) StoryManager.Instance.MainProgress += node.Progress; - // 전용 BGM: 설정돼 있으면 교체, 비어 있으면 기본 BGM으로 복귀 - if (SoundManager.Instance != null) - { - if (node.Bgm != null) - SoundManager.Instance.PlayOverrideBGM(node.Bgm); - else - SoundManager.Instance.ClearOverrideBGM(); - } + // BGM: 지정된 노드에서만 갈아타고, 다시 바꾸기 전까지 계속 이어진다. + // 비어 있으면 "변경 없음" — 모든 노드에 같은 곡을 넣어줄 필요가 없다. + if (node.Bgm != null && SoundManager.Instance != null) + SoundManager.Instance.PlayOverrideBGM(node.Bgm); + + // 1회성 효과음 — 이 대사가 뜨는 순간 한 번 재생된다 + if (node.Sfx != null && SoundManager.Instance != null) + SoundManager.Instance.PlaySFX(node.Sfx); // 보이스 재생 if (node.Voice != null && node.Speaker != null) diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogAffectionNode.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogAffectionNode.cs index c27e268..b2311ab 100644 --- a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogAffectionNode.cs +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogAffectionNode.cs @@ -4,7 +4,7 @@ namespace DinoLove.Dialog.GraphTool.Editor { // 호감도 분기 노드. 대사 없이 호감도 조건식만 검사해 두 경로 중 하나로 즉시 라우팅한다. - // DialogNode(AffectionCheck=true) 하나로 변환된다. + // DialogNode(Kind = AffectionCheck) 하나로 변환된다. // // Condition Count로 조건 줄을 늘린다. 조건 하나는 [Target / 연산자 / 값] 세 줄이고, // 조건 사이마다 [and · or] 연결자 줄이 하나씩 생긴다: diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogEndNode.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogEndNode.cs new file mode 100644 index 0000000..1b2fcd5 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogEndNode.cs @@ -0,0 +1,32 @@ +using System; +using Unity.GraphToolkit.Editor; + +namespace DinoLove.Dialog.GraphTool.Editor +{ + // 대화의 종료점. 진행 경로의 끝을 이 노드에 연결하면 "여기서 대화가 끝난다"가 그래프에 드러난다. + // Start와 달리 **여러 개 둘 수 있다** — 분기로 갈린 루트마다 종료 노드를 따로 두고 + // BGM 처리를 다르게 정할 수 있다. + // + // 대화가 끝날 때의 BGM 처리를 여기서 정한다: + // Keep BGM 꺼짐(기본) → 장소 BGM(LocationData.Bgm)으로 복귀 + // Keep BGM 켜짐 → 마지막에 재생 중이던 BGM을 그대로 유지 + // + // 임포터는 이 노드를 DialogNode(Kind = End)로 변환한다. 대사가 없으므로 재생 시 + // 아무것도 표시하지 않고 BGM 처리만 정한 뒤 대화를 끝낸다. + [Serializable] + internal class DialogEndNode : DialogGraphNode + { + public const string PORT_KEEP_BGM = "KeepBgm"; + + protected override void OnDefinePorts(IPortDefinitionContext context) + { + AddExecInput(context); + + // 옵션(AddOption)이 아니라 입력 포트를 쓴다 — 이 프로젝트에서 값 렌더링이 + // 여러 타입으로 검증된 경로이고, 임포터의 GetInputPortValue로 그대로 읽힌다. + context.AddInputPort(PORT_KEEP_BGM).WithDisplayName("Keep BGM") + .WithTooltip("켜면 대화가 끝나도 마지막 BGM을 그대로 유지한다. " + + "끄면(기본) 장소 BGM으로 돌아간다").Build(); + } + } +} diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogEndNode.cs.meta b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogEndNode.cs.meta new file mode 100644 index 0000000..9376e85 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogEndNode.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 96e836626b396b144a706f8e4e8e611e \ No newline at end of file diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraph.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraph.cs index 10be139..6e2ca53 100644 --- a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraph.cs +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraph.cs @@ -42,6 +42,11 @@ public override void OnGraphChanged(GraphLogger infos) infos.LogWarning("Start 노드는 하나만 사용됩니다. 가장 먼저 생성된 노드만 적용됩니다.", extra); break; } + + // End 노드도 필수. 단 Start와 달리 여러 개 둘 수 있다 — + // 분기로 갈린 루트마다 종료 노드를 따로 두고 BGM 처리를 다르게 정할 수 있다. + if (!GetNodes().OfType().Any()) + infos.LogError("End 노드가 필요합니다. (Dialog End Node를 추가해 마지막 대사에 연결하세요)", this); } } } diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs index e96bdfe..74f01ce 100644 --- a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs @@ -10,7 +10,7 @@ namespace DinoLove.Dialog.GraphTool.Editor // .dlg 그래프 에셋을 기존 런타임 타입(DialogGroup / DialogNode / DialogChoice)으로 변환한다. // 생성된 DialogNode들은 서브에셋으로, DialogGroup이 메인 에셋으로 등록된다. // 따라서 DialogPlayer는 수정 없이 임포트된 .dlg 에셋(= DialogGroup)을 그대로 사용한다. - [ScriptedImporter(17, DialogGraph.AssetExtension)] // 버전 올리면 기존 .dlg 에셋이 재임포트됨 + [ScriptedImporter(20, DialogGraph.AssetExtension)] // 버전 올리면 기존 .dlg 에셋이 재임포트됨 internal class DialogGraphImporter : ScriptedImporter { public override void OnImportAsset(AssetImportContext ctx) @@ -38,7 +38,9 @@ public override void OnImportAsset(AssetImportContext ctx) if (firstGraphNode == null) return; // Start만 있고 연결 없음 - // 1패스: 도달 가능한 모든 라인 노드 → DialogNode 인스턴스 생성 (중복 제거) + // 1패스: 도달 가능한 모든 노드 → DialogNode 인스턴스 생성 (중복 제거). + // End 노드도 DialogNode가 된다 — 그래야 분기마다 다른 종료 노드를 두고 + // 각자 다른 BGM 처리를 갖게 할 수 있다 (Next로 자연히 연결되므로 별도 배선이 없다). var map = new Dictionary(); var order = new List(); var queue = new Queue(); @@ -48,7 +50,8 @@ public override void OnImportAsset(AssetImportContext ctx) { var gn = queue.Dequeue(); if (gn == null || map.ContainsKey(gn) - || (gn is not DialogLineNode && gn is not DialogAffectionNode)) + || (gn is not DialogLineNode && gn is not DialogAffectionNode + && gn is not DialogEndNode)) continue; var dn = ScriptableObject.CreateInstance(); @@ -75,10 +78,19 @@ public override void OnImportAsset(AssetImportContext ctx) { var dn = map[gn]; + // 종료 노드 — 대사 없이 대화를 끝내며, BGM 처리만 담는다 + if (gn is DialogEndNode) + { + dn.Kind = DialogNodeKind.End; + dn.KeepBgmOnEnd = GetInputPortValue( + gn.GetInputPortByName(DialogEndNode.PORT_KEEP_BGM)); + continue; + } + // 호감도 분기 노드 — 대사 없이 라우팅만: 조건 통과 → AffectionPassBranch, 실패 → Next if (gn is DialogAffectionNode affectionNode) { - dn.AffectionCheck = true; + dn.Kind = DialogNodeKind.AffectionCheck; int conditionCount = 1; var countOption = affectionNode.GetNodeOptionByName(DialogAffectionNode.OPTION_CONDITION_COUNT); @@ -117,6 +129,7 @@ public override void OnImportAsset(AssetImportContext ctx) dn.Expression = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_EXPRESSION)); dn.Voice = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_VOICE)); dn.Bgm = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_BGM)); + dn.Sfx = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_SFX)); dn.Typewriter = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_TYPEWRITER)); dn.Affection = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_AFFECTION)); dn.Progress = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_PROGRESS)); diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs index 82a6362..305c35c 100644 --- a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs @@ -21,6 +21,7 @@ internal class DialogLineNode : DialogGraphNode public const string PORT_EXPRESSION = "Expression"; public const string PORT_VOICE = "Voice"; public const string PORT_BGM = "Bgm"; + public const string PORT_SFX = "Sfx"; public const string PORT_TYPEWRITER = "Typewriter"; public const string PORT_AFFECTION = "Affection"; public const string PORT_PROGRESS = "Progress"; @@ -69,7 +70,11 @@ protected override void OnDefinePorts(IPortDefinitionContext context) context.AddInputPort(PORT_EXPRESSION).WithDisplayName("Expression").Build(); context.AddInputPort(PORT_VOICE).WithDisplayName("Voice").Build(); context.AddInputPort(PORT_BGM).WithDisplayName("BGM") - .WithTooltip("있으면 이 대사부터 전용 BGM 재생, 비우면 기본 BGM으로 복귀").Build(); + .WithTooltip("있으면 이 대사부터 이 BGM으로 갈아탄다. " + + "비우면 변경 없음 — 다시 바꾸기 전까지 계속 이어진다").Build(); + context.AddInputPort(PORT_SFX).WithDisplayName("SFX") + .WithTooltip("이 대사가 시작될 때 한 번 재생할 효과음 (문 여는 소리, 쿵 소리 등). " + + "BGM과 달리 이어지지 않고 1회성이다").Build(); context.AddInputPort(PORT_TYPEWRITER).WithDisplayName("Typewriter") .WithTooltip("이 대사만의 타이핑 연출(속도·글자색·타이핑 사운드). " + "비우면 DialogHud의 기본 스타일").Build(); diff --git a/Assets/02_Scripts/GlobalObject.cs b/Assets/02_Scripts/GlobalObject.cs new file mode 100644 index 0000000..d7c5c19 --- /dev/null +++ b/Assets/02_Scripts/GlobalObject.cs @@ -0,0 +1,18 @@ +using UnityEngine; + +public class GlobalObject : MonoBehaviour +{ + private static GlobalObject _instance; + void Awake() + { + if (_instance == null) + { + _instance = this; + DontDestroyOnLoad(gameObject); + } + else + { + Destroy(gameObject); + } + } +} \ No newline at end of file diff --git a/Assets/02_Scripts/GlobalObject.cs.meta b/Assets/02_Scripts/GlobalObject.cs.meta new file mode 100644 index 0000000..fe57999 --- /dev/null +++ b/Assets/02_Scripts/GlobalObject.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 945cc6d1ec9db6446b7c06f420117c84 \ No newline at end of file diff --git a/Assets/02_Scripts/Managers/LocationManager.cs b/Assets/02_Scripts/Managers/LocationManager.cs index 7553fed..4961180 100644 --- a/Assets/02_Scripts/Managers/LocationManager.cs +++ b/Assets/02_Scripts/Managers/LocationManager.cs @@ -55,6 +55,12 @@ public void MoveTo(LocationData location) if (location == null || DialogPlayer.IsAnyActive) return; Current = location; + + // 장소 BGM = SoundManager의 기본 BGM 층. 대화용 BGM(Override)이 걷히면 이 곡으로 돌아온다. + // 비어 있으면 변경하지 않는다 — 노드 BGM과 같은 규칙(비면 변경 없음)이다. + if (location.Bgm != null && SoundManager.Instance != null) + SoundManager.Instance.SetDefaultBGM(location.Bgm); + if (_currentInstance != null) Destroy(_currentInstance); _currentInstance = location.Prefab != null ? Instantiate(location.Prefab, _locationRoot) diff --git a/Assets/02_Scripts/Managers/SoundManager.cs b/Assets/02_Scripts/Managers/SoundManager.cs index 0c4ecf1..b03b596 100644 --- a/Assets/02_Scripts/Managers/SoundManager.cs +++ b/Assets/02_Scripts/Managers/SoundManager.cs @@ -45,7 +45,7 @@ private void Awake() } // 씬이 바뀌면 이전 씬에서 남은 전용(Override) BGM을 정리한다 (ISceneInitializable). - // 새 씬의 SceneBgm.Start가 SetDefaultBGM으로 그 씬의 곡을 넘겨주면 그대로 재생된다. + // 기본 BGM 층은 LocationManager.MoveTo가 장소 입장 시 SetDefaultBGM으로 넘겨준다. public void OnSceneLoaded() => ClearOverrideBGM(); private void Initialize() diff --git a/Assets/02_Scripts/Story/LocationData.cs b/Assets/02_Scripts/Story/LocationData.cs index 9259a91..7d1add3 100644 --- a/Assets/02_Scripts/Story/LocationData.cs +++ b/Assets/02_Scripts/Story/LocationData.cs @@ -11,4 +11,8 @@ public class LocationData : ScriptableObject [Tooltip("이 장소의 프리팹 — 배경 + 캐릭터 슬롯(CharacterSlot)들 + 이동 버튼 등")] public GameObject Prefab; + + [Tooltip("이 장소의 기본 BGM. 대화용 BGM이 걷히면 이 곡으로 돌아온다. " + + "비우면 변경하지 않는다(이전 곡 유지) — 같은 곡을 쓰는 장소마다 넣지 않아도 된다")] + public AudioClip Bgm; } diff --git a/Assets/02_Scripts/_UI/Typewriter.cs b/Assets/02_Scripts/_UI/Typewriter.cs index a9e256d..2b65765 100644 --- a/Assets/02_Scripts/_UI/Typewriter.cs +++ b/Assets/02_Scripts/_UI/Typewriter.cs @@ -1,4 +1,5 @@ using System; +using System.Text; using System.Threading; using UnityEngine; @@ -36,10 +37,38 @@ public sealed class Typewriter // 지금 적용할 스타일 (글자 색 오버라이드 판단용). 연출 없이 표시했으면 null public TypewriterStyle Style => _style; - // Label.text에 그대로 넣을 문자열. 아직 안 드러난 뒷부분은 투명 처리돼 있다 - public string Composed => _cursor >= _full.Length - ? _full - : _full.Substring(0, _cursor) + HIDE_TAG + _full.Substring(_cursor); + // Label.text에 그대로 넣을 문자열. 아직 안 드러난 뒷부분은 투명 처리돼 있다. + // + // 뒷부분의 태그마다 을 다시 선언하는 게 핵심이다: + // 나 는 색을 (재)지정하면서 알파까지 같이 덮으므로, 앞에 한 번만 걸어둔 + // 이 풀려 아직 안 나온 글자가 보여 버린다. 뒷부분은 전부 숨겨야 하는 구간이니 + // 태그가 끝날 때마다 투명도를 다시 못박는 게 항상 옳다. + public string Composed + { + get + { + if (_cursor >= _full.Length) return _full; + + var sb = new StringBuilder(_full.Length + 32); + sb.Append(_full, 0, _cursor); + sb.Append(HIDE_TAG); + + for (int i = _cursor; i < _full.Length; i++) + { + char c = _full[i]; + sb.Append(c); + if (c != '<') continue; + + int close = _full.IndexOf('>', i + 1); + if (close < 0) continue; // 닫히지 않은 '<' — 평범한 글자로 취급 + + sb.Append(_full, i + 1, close - i); // 태그 나머지 + '>' + sb.Append(HIDE_TAG); // 색이 되돌아갔을 수 있으니 다시 숨긴다 + i = close; + } + return sb.ToString(); + } + } // 새 텍스트로 타이핑을 시작한다. style이 null이면 연출 없이 즉시 전체 표시. // token은 소유자(MonoBehaviour)의 destroyCancellationToken을 넘길 것. diff --git a/Assets/03_Shaders.meta b/Assets/03_Shaders.meta new file mode 100644 index 0000000..eee0853 --- /dev/null +++ b/Assets/03_Shaders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 175599729857da940abad2117688b2ef +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/04_Models.meta b/Assets/04_Models.meta new file mode 100644 index 0000000..8426b52 --- /dev/null +++ b/Assets/04_Models.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d7826b60f9ea7814c9eb3c483df13232 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/04_Models/GlobalObjects.prefab b/Assets/04_Models/GlobalObjects.prefab new file mode 100644 index 0000000..bbc568f --- /dev/null +++ b/Assets/04_Models/GlobalObjects.prefab @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ec7cab694c3e9358a94d4d8f7467ecb9b51040748ab23a010ac4fd8611c5881 +size 11918 diff --git a/Assets/04_Models/GlobalObjects.prefab.meta b/Assets/04_Models/GlobalObjects.prefab.meta new file mode 100644 index 0000000..1bd9b10 --- /dev/null +++ b/Assets/04_Models/GlobalObjects.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0385ad565a861bb4ebb0055e9dd83deb +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/07_Data/DialogGraph/TestDialog.dlg b/Assets/07_Data/DialogGraph/TestDialog.dlg index 49ece09..5bea627 100644 --- a/Assets/07_Data/DialogGraph/TestDialog.dlg +++ b/Assets/07_Data/DialogGraph/TestDialog.dlg @@ -19,6 +19,164 @@ MonoBehaviour: RefIds: - rid: -2 type: {class: , ns: , asm: } + - rid: 4848514907161231424 + type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Value: 0 + - rid: 4848514907161231425 + type: {class: 'Constant`1[[TypewriterStyle, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Value: {fileID: 0} + - rid: 4848514907161231434 + type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: UnityEditor.GraphToolkitModule} + data: + m_Guid: + m_Value0: 16453215471102353767 + m_Value1: 11894338102006161391 + m_HashGuid: + serializedVersion: 2 + Hash: 676104504c9055e4ef9f78207c2d11a5 + m_Version: 2 + m_Position: {x: 1022.2925, y: -108.445} + m_Title: + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: + - KeepBgm + m_ValueList: + - rid: 4848514907161231435 + m_InputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_OutputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_Collapsed: 0 + m_CurrentModeIndex: 0 + m_ElementColor: + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_HasUserColor: 0 + m_Node: + rid: 4848514907161231436 + - rid: 4848514907161231435 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Value: 0 + - rid: 4848514907161231436 + type: {class: DialogEndNode, ns: DinoLove.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 4848514907161231437 + type: {class: WireModel, ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Guid: + m_Value0: 5143976002492878297 + m_Value1: 15423880824646763031 + m_HashGuid: + serializedVersion: 2 + Hash: d9cdb899eb1263471782070be4a10cd6 + m_Version: 2 + m_FromPortReference: + m_NodeModelGuid: + m_Value0: 4814898993618757843 + m_Value1: 13954766896149483658 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: d32808d01ff5d1428ad05e788e4aa9c1 + m_UniqueId: PassOut + m_PortDirection: 2 + m_PortOrientation: 0 + m_Title: "True \u2192" + m_ToPortReference: + m_NodeModelGuid: + m_Value0: 16453215471102353767 + m_Value1: 11894338102006161391 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 676104504c9055e4ef9f78207c2d11a5 + m_UniqueId: In + m_PortDirection: 1 + m_PortOrientation: 0 + m_Title: + - rid: 4848514907161231438 + type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: UnityEditor.GraphToolkitModule} + data: + m_Guid: + m_Value0: 1938688397874075465 + m_Value1: 2789158294045304892 + m_HashGuid: + serializedVersion: 2 + Hash: 49977485d39ae71a3ca8e7dab014b526 + m_Version: 2 + m_Position: {x: 1015.285, y: 82.087524} + m_Title: + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: + - KeepBgm + m_ValueList: + - rid: 4848514907161231440 + m_InputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_OutputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_Collapsed: 0 + m_CurrentModeIndex: 0 + m_ElementColor: + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_HasUserColor: 0 + m_Node: + rid: 4848514907161231441 + - rid: 4848514907161231439 + type: {class: WireModel, ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Guid: + m_Value0: 641791417328842023 + m_Value1: 16432348670849463703 + m_HashGuid: + serializedVersion: 2 + Hash: 27b5c7c9e419e808973558c70d6e0be4 + m_Version: 2 + m_FromPortReference: + m_NodeModelGuid: + m_Value0: 4814898993618757843 + m_Value1: 13954766896149483658 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: d32808d01ff5d1428ad05e788e4aa9c1 + m_UniqueId: FailOut + m_PortDirection: 2 + m_PortOrientation: 0 + m_Title: "False \u2192" + m_ToPortReference: + m_NodeModelGuid: + m_Value0: 1938688397874075465 + m_Value1: 2789158294045304892 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 49977485d39ae71a3ca8e7dab014b526 + m_UniqueId: In + m_PortDirection: 1 + m_PortOrientation: 0 + m_Title: + - rid: 4848514907161231440 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Value: 0 + - rid: 4848514907161231441 + type: {class: DialogEndNode, ns: DinoLove.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: - rid: 6600512887158735084 type: {class: GraphModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: UnityEditor.GraphToolkitModule} data: @@ -33,9 +191,13 @@ MonoBehaviour: - rid: 6600512887158735087 - rid: 6600512887158735111 - rid: 6600512887158735114 + - rid: 4848514907161231434 + - rid: 4848514907161231438 m_GraphWireModels: - rid: 6600512887158735113 - rid: 6600512887158735115 + - rid: 4848514907161231437 + - rid: 4848514907161231439 m_GraphStickyNoteModels: [] m_GraphPlacematModels: [] m_GraphVariableModels: [] @@ -46,9 +208,9 @@ MonoBehaviour: m_LastKnownBounds: serializedVersion: 2 x: -404 - y: -43 - width: 1186 - height: 450 + y: -108 + width: 1581 + height: 497 m_GraphElementMetaData: - m_Guid: m_Value0: 8557079674992562767 @@ -90,6 +252,38 @@ MonoBehaviour: Hash: 3baeafb1da23486527c4bf3c23696024 m_Category: 2 m_Index: 1 + - m_Guid: + m_Value0: 16453215471102353767 + m_Value1: 11894338102006161391 + m_HashGuid: + serializedVersion: 2 + Hash: 676104504c9055e4ef9f78207c2d11a5 + m_Category: 0 + m_Index: 3 + - m_Guid: + m_Value0: 5143976002492878297 + m_Value1: 15423880824646763031 + m_HashGuid: + serializedVersion: 2 + Hash: d9cdb899eb1263471782070be4a10cd6 + m_Category: 2 + m_Index: 2 + - m_Guid: + m_Value0: 1938688397874075465 + m_Value1: 2789158294045304892 + m_HashGuid: + serializedVersion: 2 + Hash: 49977485d39ae71a3ca8e7dab014b526 + m_Category: 0 + m_Index: 4 + - m_Guid: + m_Value0: 641791417328842023 + m_Value1: 16432348670849463703 + m_HashGuid: + serializedVersion: 2 + Hash: 27b5c7c9e419e808973558c70d6e0be4 + m_Category: 2 + m_Index: 3 m_EntryPoint: rid: 6600512887158735087 m_Graph: @@ -128,9 +322,6 @@ MonoBehaviour: m_InputConstantsById: m_KeyList: - __option_ChoiceCount - - __option_HasHiddenBranch - - __option_HiddenGestureKey - - __option_HiddenCode - Speaker - SpeakerNameOverride - TalkText @@ -140,11 +331,10 @@ MonoBehaviour: - Bgm - Affection - Progress + - __option_HiddenBranchCount + - Typewriter m_ValueList: - rid: 6600512887158735088 - - rid: 6600512887158735090 - - rid: 6600512887158735091 - - rid: 6600512887158735092 - rid: 6600512887158735093 - rid: 6600512887158735094 - rid: 6600512887158735096 @@ -154,6 +344,8 @@ MonoBehaviour: - rid: 6600512887158735100 - rid: 6600512887158735108 - rid: 6600512887158735109 + - rid: 4848514907161231424 + - rid: 4848514907161231425 m_InputPortInfos: expandedPortsById: m_KeyList: [] @@ -173,18 +365,6 @@ MonoBehaviour: type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} data: m_Value: 0 - - rid: 6600512887158735090 - type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} - data: - m_Value: 0 - - rid: 6600512887158735091 - type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} - data: - m_Value: - - rid: 6600512887158735092 - type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} - data: - m_Value: - rid: 6600512887158735093 type: {class: 'Constant`1[[CharacterData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} data: diff --git a/Assets/07_Data/Locations.meta b/Assets/07_Data/Locations.meta new file mode 100644 index 0000000..e386f08 --- /dev/null +++ b/Assets/07_Data/Locations.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 889071c7a22f91044b06aaade91eb7e3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/07_Data/Locations/TestLocation.asset b/Assets/07_Data/Locations/TestLocation.asset new file mode 100644 index 0000000..2d9a412 --- /dev/null +++ b/Assets/07_Data/Locations/TestLocation.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d91c0960205178df00f35b628e4d9c199a6f7ac80bb10356d59e24fde361c3d +size 513 diff --git a/Assets/07_Data/Locations/TestLocation.asset.meta b/Assets/07_Data/Locations/TestLocation.asset.meta new file mode 100644 index 0000000..52340dc --- /dev/null +++ b/Assets/07_Data/Locations/TestLocation.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ddd31e95563e379458d4e8f53904b484 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: