diff --git a/Assets/01_Scenes/Chapter1.unity b/Assets/01_Scenes/Chapter1.unity new file mode 100644 index 0000000..1af32d8 --- /dev/null +++ b/Assets/01_Scenes/Chapter1.unity @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:acc537f1dec8e899a73075005da3af8515338b624e988256e19f7cbfe9f2f226 +size 36710 diff --git a/Assets/01_Scenes/Chapter1.unity.meta b/Assets/01_Scenes/Chapter1.unity.meta new file mode 100644 index 0000000..a44b7f7 --- /dev/null +++ b/Assets/01_Scenes/Chapter1.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d4aa74c1645590345beab3010ff545fe +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/02_Scripts/Communication/Dialog/DialogNode.cs b/Assets/02_Scripts/Communication/Dialog/DialogNode.cs index 99f5938..a6fe76d 100644 --- a/Assets/02_Scripts/Communication/Dialog/DialogNode.cs +++ b/Assets/02_Scripts/Communication/Dialog/DialogNode.cs @@ -9,6 +9,16 @@ public enum DialogNodeKind [InspectorName("종료")] End, // 대화를 끝낸다 (BGM 처리만 정함) } +// 이 대사에서 이미 떠 있던 화면 그림을 걷어낼지. +// 그림 지정이 "비우면 변경 없음"(BGM과 같은 규칙)이라 지우는 길을 따로 열어 둔 것이다. +public enum SpriteClear +{ + [InspectorName("없음")] None, + [InspectorName("앞 그림")] Front, + [InspectorName("옆 그림")] Side, + [InspectorName("둘 다")] Both, +} + [CreateAssetMenu(menuName = "Communication/Dialog Node")] public class DialogNode : ScriptableObject { @@ -41,6 +51,20 @@ public class DialogNode : ScriptableObject "비우면 DialogHud의 기본 스타일을 쓴다")] public TypewriterStyle Typewriter; + [Header("Sprite")] + [Tooltip("이 대사 동안 화면 앞쪽(가운데 아래)에 세울 그림. 인물이 보통이지만 물건·서류도 된다. " + + "비우면 변경 없음 — BGM과 같은 규칙이라 인물이 서 있는 동안 매 대사에 다시 꽂을 필요가 없다. " + + "대화가 끝나면 대화 전에 떠 있던 그림으로 되돌아간다")] + public Sprite FrontSprite; + + [Tooltip("이 대사 동안 화면 옆(왼쪽)에 놓을 그림. 제시한 물건을 놓고 설명할 때 쓴다. " + + "비우면 변경 없음. 대화가 끝나면 자동으로 걷힌다")] + public Sprite SideSprite; + + [Tooltip("이미 떠 있던 그림을 이 대사에서 걷어낸다. 그림 지정이 '비우면 변경 없음'이라 " + + "지울 때만 쓴다. 같은 노드에서 새 그림도 지정했다면 새 그림이 이긴다")] + public SpriteClear ClearSprite; + [Header("Flow")] public DialogNode Next; // 선택지 없을 때 자동으로 갈 노드 public List Choices; // 있으면 플레이어 선택 대기 diff --git a/Assets/02_Scripts/Communication/Dialog/DialogPlayer.cs b/Assets/02_Scripts/Communication/Dialog/DialogPlayer.cs index a6a27c7..fe61b57 100644 --- a/Assets/02_Scripts/Communication/Dialog/DialogPlayer.cs +++ b/Assets/02_Scripts/Communication/Dialog/DialogPlayer.cs @@ -202,6 +202,12 @@ 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; @@ -285,6 +291,10 @@ 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); + RestoreDefaultAnimations(); } } @@ -341,6 +351,9 @@ private async Awaitable PlayNode(DialogNode node) if (node.Sfx != null && SoundManager.Instance != null) SoundManager.Instance.PlaySFX(node.Sfx); + // 화면 그림(앞/옆) — BGM과 같은 규칙으로 비우면 변경 없음 + ApplySprites(node); + // 보이스 재생 if (node.Voice != null && node.Speaker != null) { @@ -376,6 +389,23 @@ private async Awaitable PlayNode(DialogNode node) return false; } + // 노드가 지정한 화면 그림을 반영한다. + // 비어 있는 지정은 "변경 없음"이라(BGM과 같은 규칙) 인물이 서 있는 동안 매 대사에 다시 꽂을 필요가 없다. + // 지우기를 먼저, 지정을 나중에 처리한다 — 한 노드에서 둘 다 걸면 새 그림이 이긴다. + private static void ApplySprites(DialogNode node) + { + var front = FrontSprite.Instance; + var side = SideSprite.Instance; + bool hasFront = front != null; + bool hasSide = side != null; + + 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 (hasFront && node.FrontSprite != null) front.Show(node.FrontSprite); + if (hasSide && node.SideSprite != null) side.Show(node.SideSprite); + } + // 호감도 분기 노드의 조건식 평가. // 각 조건의 JoinWithNext로 이어 붙이며, AND가 OR보다 우선순위가 높다: // A and B or C → (A and B) or C diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs index 74f01ce..cca537b 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(20, DialogGraph.AssetExtension)] // 버전 올리면 기존 .dlg 에셋이 재임포트됨 + [ScriptedImporter(21, DialogGraph.AssetExtension)] // 버전 올리면 기존 .dlg 에셋이 재임포트됨 internal class DialogGraphImporter : ScriptedImporter { public override void OnImportAsset(AssetImportContext ctx) @@ -127,6 +127,9 @@ public override void OnImportAsset(AssetImportContext ctx) dn.TalkText = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_TALK)).Value; dn.Gesture = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_GESTURE)); dn.Expression = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_EXPRESSION)); + dn.FrontSprite = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_FRONT_SPRITE)); + dn.SideSprite = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_SIDE_SPRITE)); + dn.ClearSprite = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_CLEAR_SPRITE)); dn.Voice = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_VOICE)); dn.Bgm = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_BGM)); dn.Sfx = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_SFX)); diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs index 305c35c..a641108 100644 --- a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs @@ -19,6 +19,9 @@ internal class DialogLineNode : DialogGraphNode public const string PORT_TALK = "TalkText"; public const string PORT_GESTURE = "Gesture"; public const string PORT_EXPRESSION = "Expression"; + public const string PORT_FRONT_SPRITE = "FrontSprite"; + public const string PORT_SIDE_SPRITE = "SideSprite"; + public const string PORT_CLEAR_SPRITE = "ClearSprite"; public const string PORT_VOICE = "Voice"; public const string PORT_BGM = "Bgm"; public const string PORT_SFX = "Sfx"; @@ -68,6 +71,18 @@ protected override void OnDefinePorts(IPortDefinitionContext context) context.AddInputPort(PORT_TALK).WithDisplayName("Talk Text").Build(); context.AddInputPort(PORT_GESTURE).WithDisplayName("Gesture").Build(); context.AddInputPort(PORT_EXPRESSION).WithDisplayName("Expression").Build(); + + // 화면에 세우는 그림 — Gesture/Expression(Animator)과 달리 2D 스프라이트를 직접 갈아끼운다 + context.AddInputPort(PORT_FRONT_SPRITE).WithDisplayName("Front Sprite") + .WithTooltip("이 대사 동안 화면 앞쪽(가운데 아래)에 세울 그림. 인물이 보통이지만 물건·서류도 된다. " + + "비우면 변경 없음 — BGM과 같은 규칙이라 인물이 서 있는 동안 매 대사에 다시 꽂을 필요가 없다").Build(); + context.AddInputPort(PORT_SIDE_SPRITE).WithDisplayName("Side Sprite") + .WithTooltip("이 대사 동안 화면 옆(왼쪽)에 놓을 그림. 제시한 물건을 놓고 설명할 때 쓴다. " + + "비우면 변경 없음").Build(); + context.AddInputPort(PORT_CLEAR_SPRITE).WithDisplayName("Clear Sprite") + .WithTooltip("이미 떠 있던 그림을 이 대사에서 걷어낸다. 그림 지정이 '비우면 변경 없음'이라 " + + "지울 때만 쓴다. 같은 노드에서 새 그림도 지정했다면 새 그림이 이긴다").Build(); + context.AddInputPort(PORT_VOICE).WithDisplayName("Voice").Build(); context.AddInputPort(PORT_BGM).WithDisplayName("BGM") .WithTooltip("있으면 이 대사부터 이 BGM으로 갈아탄다. " + diff --git a/Assets/02_Scripts/_UI/FrontSprite.cs b/Assets/02_Scripts/_UI/FrontSprite.cs new file mode 100644 index 0000000..64ed5e9 --- /dev/null +++ b/Assets/02_Scripts/_UI/FrontSprite.cs @@ -0,0 +1,22 @@ +using UnityEngine; +using UnityEngine.UIElements; + +// 화면 앞쪽(가운데 아래)에 크게 서는 그림 한 장 — FrontSprite.uxml의 #FrontSprite. +// +// 그 장소에 서 있는 인물이거나, 대사 중 화자다. +// 다만 인물만 서는 자리가 아니어서 이름이 Character가 아니다 — +// 대사 도중 물건·서류가 같은 자리에 뜨는 연출도 이 패널이 받는다. +// +// 누가 띄우는가: +// - 장소 : 입장 연출·트리거의 UnityEvent에서 Show(스프라이트) +// - 대화 : DialogPlayer가 노드의 Front Sprite를 그대로 넘긴다. +// 비어 있는 노드는 "변경 없음"이라 인물이 서 있는 동안 매 대사에 다시 꽂을 필요가 없고, +// 대화가 끝나면 대화 전에 떠 있던 그림으로 되돌아간다. +[RequireComponent(typeof(PanelRenderer))] +public class FrontSprite : SpritePanel +{ + protected override string ImageName => "FrontSprite"; + + // 아래에서 올라오며 등장 — 서 있는 인물에 어울린다 (인스펙터에서 바꿀 수 있다) + private void Reset() => SlideFrom = new Vector2(0f, 80f); +} diff --git a/Assets/02_Scripts/_UI/FrontSprite.cs.meta b/Assets/02_Scripts/_UI/FrontSprite.cs.meta new file mode 100644 index 0000000..94a536a --- /dev/null +++ b/Assets/02_Scripts/_UI/FrontSprite.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b097d248649282e4688a857b2d850c68 \ No newline at end of file diff --git a/Assets/02_Scripts/_UI/SideSprite.cs b/Assets/02_Scripts/_UI/SideSprite.cs new file mode 100644 index 0000000..635099f --- /dev/null +++ b/Assets/02_Scripts/_UI/SideSprite.cs @@ -0,0 +1,23 @@ +using UnityEngine; +using UnityEngine.UIElements; + +// 화면 옆(왼쪽)에 놓고 설명하는 그림 한 장 — SideSprite.uxml의 #SideSprite. +// +// 주 용도는 제시 연출이다: 증거품창에서 물건을 들이대면 그 물건이 왼쪽에 남고, +// FrontSprite의 인물이 그걸 놓고 이야기한다. 두 패널이 따로인 이유가 이것 — +// 인물은 인물대로 표정을 바꾸는 동안 제시한 물건은 그 자리에 그대로 있어야 한다. +// +// 누가 띄우는가: +// - 대화 : DialogPlayer가 노드의 Side Sprite를 그대로 넘긴다. +// FrontSprite와 같은 규칙 — 비우면 변경 없음, 지울 땐 노드의 Clear Sprite로, +// 대화가 끝나면 자동으로 걷힌다. +// - 제시 순간 연출을 직접 붙이고 싶으면 EvidenceHud의 On Presented에 +// Show(EvidenceData) 오버로드를 연결해도 된다. +[RequireComponent(typeof(PanelRenderer))] +public class SideSprite : SpritePanel +{ + protected override string ImageName => "SideSprite"; + + // 왼쪽에서 밀려 들어오며 등장 — 들이대는 느낌 (인스펙터에서 바꿀 수 있다) + private void Reset() => SlideFrom = new Vector2(-120f, 0f); +} diff --git a/Assets/02_Scripts/_UI/SideSprite.cs.meta b/Assets/02_Scripts/_UI/SideSprite.cs.meta new file mode 100644 index 0000000..be6bb0e --- /dev/null +++ b/Assets/02_Scripts/_UI/SideSprite.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e76bcffac43abeb4caf25d3e028b0bad \ No newline at end of file diff --git a/Assets/02_Scripts/_UI/SpritePanel.cs b/Assets/02_Scripts/_UI/SpritePanel.cs new file mode 100644 index 0000000..f3426fb --- /dev/null +++ b/Assets/02_Scripts/_UI/SpritePanel.cs @@ -0,0 +1,221 @@ +using System; +using UnityEngine; +using UnityEngine.UIElements; + +// 화면에 그림 한 장을 띄우는 패널의 공통 뼈대. +// FrontSprite(가운데 아래 크게 서는 그림)와 SideSprite(옆에 놓고 설명하는 그림)가 이걸 공유한다. +// +// 왜 CharacterData가 아니라 Sprite인가: +// 인물만 서는 자리가 아니다 — 대사 도중 물건·서류·풍경이 같은 자리에 뜰 수 있어서 표시 단위를 +// Sprite로 잡았다. (CharacterData.Portrait은 증거품창 인물 정보 사진 전용이라 여기선 쓰지 않는다) +// +// 그림 교체 규칙 — 셋을 구분하는 게 이 클래스의 핵심이다: +// 숨김 → 표시 : 슬라이드 + 페이드 인 +// 표시 중 교체 : 즉시. 표정만 바뀌는데 매번 페이드하면 대사 흐름이 늘어진다 +// 표시 → 숨김 : 슬라이드 + 페이드 아웃 +// +// 제네릭 상속인 것은 파생 타입마다 자기 Instance를 갖게 하기 위해서다 — +// FrontSprite.Instance와 SideSprite.Instance가 각각 따로 존재한다. +[RequireComponent(typeof(PanelRenderer))] +public abstract class SpritePanel : MonoBehaviour where T : SpritePanel +{ + public static T Instance { get; private set; } + + [Header("Transition")] + [Tooltip("등장·퇴장 연출 시간(초). 0이면 연출 없이 즉시 나타나고 사라진다")] + [Min(0f)] [SerializeField] private float _fadeDuration = 0.18f; + + [Tooltip("등장할 때 밀려 들어오는 시작 위치(px). 퇴장할 땐 이 방향으로 되밀려 나간다. " + + "(0,0)이면 제자리에서 페이드만 한다")] + [SerializeField] private Vector2 _slideFrom = new(0f, 80f); + + // UXML에서 그림을 담을 Image의 이름. 파생 클래스가 정한다 + protected abstract string ImageName { get; } + + // 파생 클래스가 컴포넌트 추가 시점의 기본 슬라이드 방향을 정할 수 있게 (Reset에서 쓴다) + protected Vector2 SlideFrom { get => _slideFrom; set => _slideFrom = value; } + + private PanelRenderer _panelRenderer; + private VisualElement _root; // 전체 토글 대상(#Body) + private Image _image; + + // 같은 버전으로 콜백이 중복 호출될 때 헛일을 막는다 (Unity 권장 패턴) + private int _uiVersion = -1; + + // 현재 표시 상태. 리로드로 요소가 새로 만들어져도 이 값으로 복원된다. + private bool _visible; + private Sprite _sprite; + + // 연출 세대 — 새 연출이 시작되면 진행 중이던 연출이 그 자리에서 물러난다 + // (EvidenceHud의 슬라이드·Typewriter와 같은 패턴) + private int _gen; + + // 지금 떠 있는 그림 (없으면 null). + // DialogPlayer가 대화 전 상태를 기억했다가 되돌리는 데 쓴다. + public Sprite Current => _visible ? _sprite : null; + + public bool IsVisible => _visible; + + private bool CanAnimate => _image != null && _fadeDuration > 0f; + + protected virtual void Awake() + { + if (Instance != null && Instance != this) { Destroy(gameObject); return; } + Instance = (T)this; + + _panelRenderer = GetComponent(); + // root가 이미 준비돼 있으면 즉시 호출되고, 이후 UI가 리로드될 때마다 다시 호출된다 + _panelRenderer.RegisterUIReloadCallback(OnUIReload); + } + + protected virtual void OnDestroy() + { + if (_panelRenderer != null) + _panelRenderer.UnregisterUIReloadCallback(OnUIReload); + if (Instance == this) Instance = null; + } + + // UI가 (재)구성될 때마다 요소를 다시 잡고 현재 표시 상태를 그대로 되돌린다. + private void OnUIReload(PanelRenderer panelRenderer, VisualElement root, int version) + { + if (_uiVersion == version) return; + _uiVersion = version; + + _root = root.Q("Body"); + _image = root.Q(ImageName); + + if (_image == null) + Debug.LogWarning($"[{GetType().Name}] UXML에서 Image '{ImageName}'을 찾지 못함: {name}"); + + _gen++; // 진행 중이던 연출은 새로 만들어진 요소에 의미가 없다 + ResetStyle(); + ApplyState(); // 첫 호출에선 _visible=false라 숨김 상태로 시작한다 + } + + // ── 공개 API (버튼·트리거의 UnityEvent에도 그대로 연결된다) ── + + // 그림을 띄운다. null을 넘기면 Hide()와 같다 — + // 덕분에 "대화 전 그림으로 되돌리기"를 Show(기억해둔값) 한 줄로 처리할 수 있다. + public void Show(Sprite sprite) + { + if (sprite == null) { Hide(); return; } + if (_visible && _sprite == sprite) return; // 같은 그림 재지정 — 연출을 다시 돌리지 않는다 + + bool wasVisible = _visible; + _sprite = sprite; + _visible = true; + + // 이미 떠 있으면 그림만 갈아끼운다 (표정 교체가 매번 페이드되면 대사가 늘어진다) + if (wasVisible) { ApplyState(); return; } + + if (!CanAnimate) { ResetStyle(); ApplyState(); return; } + _ = Transition(fadeIn: true, ++_gen); + } + + // 증거품·인물 항목을 그대로 띄운다 (제시 연출에서 SideSprite에 바로 넘기기 위한 편의 오버로드) + public void Show(EvidenceData evidence) => Show(evidence != null ? evidence.Icon : null); + + public void Hide() + { + if (!_visible) return; + + if (!CanAnimate) + { + _visible = false; + _gen++; + ResetStyle(); + ApplyState(); + return; + } + _ = Transition(fadeIn: false, ++_gen); + } + + // 연출 없이 즉시 반영 (씬 전환·초기 배치처럼 연출이 방해가 되는 자리에서) + public void SetImmediate(Sprite sprite) + { + _sprite = sprite; + _visible = sprite != null; + _gen++; // 진행 중이던 연출 취소 + ResetStyle(); + ApplyState(); + } + + // ── 내부 ───────────────────────────────────────────────────── + + // 등장/퇴장 한 번. 세대가 바뀌면(새 Show/Hide가 끼어들면) 그 자리에서 물러난다. + private async Awaitable Transition(bool fadeIn, int gen) + { + Vector2 fromT = fadeIn ? _slideFrom : Vector2.zero; + Vector2 toT = fadeIn ? Vector2.zero : _slideFrom; + float fromA = fadeIn ? 0f : 1f; + float toA = fadeIn ? 1f : 0f; + + try + { + // 들어올 때는 화면에 붙이기 전에 시작 스타일(투명 + 밀린 위치)부터 세운다 — + // 순서가 반대면 첫 프레임에 그림이 제자리에서 번쩍한다 + if (fadeIn) + { + SetStyle(fromT, fromA); + ApplyState(); + } + + await Animate(fromT, toT, fromA, toA, gen); + if (gen != _gen) return; // 도중에 새 연출이 이어받음 — 뒷정리는 그쪽이 한다 + + // 다 사라진 뒤에 실제로 감춘다 (먼저 감추면 퇴장 연출이 보이지 않는다) + if (!fadeIn) + { + _visible = false; + ApplyState(); + } + } + catch (OperationCanceledException) + { + // 오브젝트 파괴/씬 전환 — 조용히 종료 + } + finally + { + // 내 세대일 때만 제자리로 — 이미 다음 연출이 시작됐다면 그쪽이 처리한다 + if (gen == _gen) ResetStyle(); + } + } + + // fromT→toT(px), fromA→toA(불투명도)로 부드럽게 이동 + private async Awaitable Animate(Vector2 fromT, Vector2 toT, float fromA, float toA, int gen) + { + float t = 0f; + while (t < _fadeDuration) + { + if (gen != _gen || _image == null) return; + + t += Time.deltaTime; + float k = Mathf.Clamp01(t / _fadeDuration); + float e = k * k * (3f - 2f * k); // SmoothStep — 끝에서 감속해 무게감이 생긴다 + SetStyle(Vector2.Lerp(fromT, toT, e), Mathf.Lerp(fromA, toA, e)); + + await Awaitable.NextFrameAsync(destroyCancellationToken); + } + SetStyle(toT, toA); + } + + private void SetStyle(Vector2 translate, float alpha) + { + if (_image == null) return; + _image.style.translate = new Translate(translate.x, translate.y); + _image.style.opacity = alpha; + } + + private void ResetStyle() => SetStyle(Vector2.zero, 1f); + + // 캐시된 상태를 실제 요소에 반영한다. 요소가 아직 없으면(리로드 콜백 전) 조용히 넘어가고, + // 콜백이 오면 같은 함수로 복원된다. + private void ApplyState() + { + if (_image != null) + _image.sprite = _visible ? _sprite : null; + + if (_root != null) + _root.style.display = _visible ? DisplayStyle.Flex : DisplayStyle.None; + } +} diff --git a/Assets/02_Scripts/_UI/SpritePanel.cs.meta b/Assets/02_Scripts/_UI/SpritePanel.cs.meta new file mode 100644 index 0000000..d155749 --- /dev/null +++ b/Assets/02_Scripts/_UI/SpritePanel.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 12bc779bba8570147b93b9af07fb0214 \ No newline at end of file diff --git a/Assets/04_Models/Characters/MayaFey/스탠딩.png.meta b/Assets/04_Models/Characters/MayaFey/스탠딩.png.meta index 1293c1f..538c664 100644 --- a/Assets/04_Models/Characters/MayaFey/스탠딩.png.meta +++ b/Assets/04_Models/Characters/MayaFey/스탠딩.png.meta @@ -147,12 +147,56 @@ TextureImporter: forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 - sprites: [] + sprites: + - serializedVersion: 2 + name: Portrait + rect: + serializedVersion: 2 + x: 1147 + y: 3596 + width: 1151 + height: 1345 + alignment: 0 + pivot: {x: 0.5, y: 0.5} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: 0 + bones: [] + spriteID: b81f24875c977d1409b47274ada205d5 + internalID: 1179272688 + vertices: [] + indices: + edges: [] + weights: [] + - serializedVersion: 2 + name: Normal2 + rect: + serializedVersion: 2 + x: 874 + y: 2234 + width: 1728 + height: 2677 + alignment: 0 + pivot: {x: 0.5, y: 0.5} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: 0 + bones: [] + spriteID: 0dd44aa7c0ed3ab4c80f6be3fe9084aa + internalID: -733087985 + vertices: [] + indices: + edges: [] + weights: [] outline: [] customData: physicsShape: [] bones: [] - spriteID: + spriteID: 10efdff463170894e9681cb3f5015fdd internalID: 0 vertices: [] indices: @@ -161,7 +205,9 @@ TextureImporter: secondaryTextures: [] spriteCustomMetadata: entries: [] - nameFileIdTable: {} + nameFileIdTable: + Normal2: -733087985 + Portrait: 1179272688 mipmapLimitGroupName: pSDRemoveMatte: 0 userData: diff --git a/Assets/07_Data/Characters/MayaFey.asset b/Assets/07_Data/Characters/MayaFey.asset new file mode 100644 index 0000000..ffcbeb3 --- /dev/null +++ b/Assets/07_Data/Characters/MayaFey.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f6206b683b93f3c8d2375bff075588f7e1dc3ce487239ccdf2b91bb9259176ab +size 551 diff --git a/Assets/07_Data/Characters/MayaFey.asset.meta b/Assets/07_Data/Characters/MayaFey.asset.meta new file mode 100644 index 0000000..c98ba5b --- /dev/null +++ b/Assets/07_Data/Characters/MayaFey.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9a53336ba49fbe74b9af901101523504 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/07_Data/DialogGraph/TestDescDialog.dlg b/Assets/07_Data/DialogGraph/TestDescDialog.dlg index ac32cbe..caef1ed 100644 --- a/Assets/07_Data/DialogGraph/TestDescDialog.dlg +++ b/Assets/07_Data/DialogGraph/TestDescDialog.dlg @@ -19,6 +19,140 @@ MonoBehaviour: RefIds: - rid: -2 type: {class: , ns: , asm: } + - rid: 1798538364032909463 + type: {class: 'Constant`1[[UnityEngine.Sprite, UnityEngine.CoreModule]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Value: {fileID: 0} + - rid: 1798538364032909464 + type: {class: 'Constant`1[[UnityEngine.Sprite, UnityEngine.CoreModule]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Value: {fileID: 0} + - rid: 1798538364032909465 + type: {class: EnumConstant, ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Value: + m_EnumType: + m_Identification: SpriteClear, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + m_Value: 0 + m_EnumType: + m_Identification: SpriteClear, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + - rid: 1798538364032909466 + type: {class: 'Constant`1[[UnityEngine.Sprite, UnityEngine.CoreModule]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Value: {fileID: 0} + - rid: 1798538364032909467 + type: {class: 'Constant`1[[UnityEngine.Sprite, UnityEngine.CoreModule]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Value: {fileID: 0} + - rid: 1798538364032909468 + type: {class: EnumConstant, ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Value: + m_EnumType: + m_Identification: SpriteClear, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + m_Value: 0 + m_EnumType: + m_Identification: SpriteClear, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + - rid: 1798538364032909469 + type: {class: VariableDeclarationModel, ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Guid: + m_Value0: 9222896094694858974 + m_Value1: 6191622170130805114 + m_HashGuid: + serializedVersion: 2 + Hash: dee09f14224ffe7f7a7114228f0fed55 + m_Version: 2 + m_Name: MayaFey_Normal + m_UniqueId: + m_DataType: + m_Identification: UnityEngine.Sprite, UnityEngine.CoreModule, Version=0.0.0.0, + Culture=neutral, PublicKeyToken=null + m_IsExposed: 0 + m_Scope: 0 + m_ShowOnInspectorOnly: 0 + m_Tooltip: + m_InitializationValue: + rid: 1798538364032909470 + m_Modifiers: 0 + m_VariableFlags: 0 + - rid: 1798538364032909470 + type: {class: 'Constant`1[[UnityEngine.Sprite, UnityEngine.CoreModule]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Value: {fileID: -733087985, guid: b3a9a91e9ec867c4c97df35ae88c45ac, type: 3} + - rid: 1798538364032909471 + type: {class: VariableNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: UnityEditor.GraphToolkitModule} + data: + m_Guid: + m_Value0: 16565847150320723582 + m_Value1: 2873397475484516538 + m_HashGuid: + serializedVersion: 2 + Hash: 7ea648ae36b6e5e5ba40c2b0c75be027 + m_Version: 2 + m_Position: {x: 163.61578, y: 291.1487} + m_Title: MayaFey_Normal + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: [] + m_ValueList: [] + 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_DeclarationModel: + rid: 1798538364032909469 + m_DeclarationModelHashGuid: + serializedVersion: 2 + Hash: dee09f14224ffe7f7a7114228f0fed55 + - rid: 1798538364032909472 + type: {class: WireModel, ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} + data: + m_Guid: + m_Value0: 9254597602728976179 + m_Value1: 9128070447517429019 + m_HashGuid: + serializedVersion: 2 + Hash: 339fd7997cef6e801b6d049db56bad7e + m_Version: 2 + m_FromPortReference: + m_NodeModelGuid: + m_Value0: 16565847150320723582 + m_Value1: 2873397475484516538 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 7ea648ae36b6e5e5ba40c2b0c75be027 + m_UniqueId: MainPortName + m_PortDirection: 2 + m_PortOrientation: 0 + m_Title: MainPortName + m_ToPortReference: + m_NodeModelGuid: + m_Value0: 7849459999523502538 + m_Value1: 14539083487489350035 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: ca793c0648e2ee6c93d98c876633c5c9 + m_UniqueId: FrontSprite + m_PortDirection: 1 + m_PortOrientation: 0 + m_Title: Front Sprite - rid: 4848514907161231424 type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule} data: @@ -41,7 +175,7 @@ MonoBehaviour: serializedVersion: 2 Hash: ca793c0648e2ee6c93d98c876633c5c9 m_Version: 2 - m_Position: {x: 331.24704, y: -52.737083} + m_Position: {x: 385.55368, y: -62.79387} m_Title: m_Tooltip: m_NodePreviewModel: @@ -62,6 +196,9 @@ MonoBehaviour: - Typewriter - Affection - Progress + - FrontSprite + - SideSprite + - ClearSprite m_ValueList: - rid: 4848514907161231446 - rid: 4848514907161231447 @@ -76,6 +213,9 @@ MonoBehaviour: - rid: 4848514907161231456 - rid: 4848514907161231457 - rid: 4848514907161231458 + - rid: 1798538364032909466 + - rid: 1798538364032909467 + - rid: 1798538364032909468 m_InputPortInfos: expandedPortsById: m_KeyList: [] @@ -190,7 +330,7 @@ MonoBehaviour: serializedVersion: 2 Hash: 20112a13e3dce533c4baf0e2ac294bae m_Version: 2 - m_Position: {x: 901.86993, y: -46.30094} + m_Position: {x: 920.37445, y: -16.231148} m_Title: m_Tooltip: m_NodePreviewModel: @@ -270,23 +410,26 @@ MonoBehaviour: - rid: 6600512887158735111 - rid: 4848514907161231444 - rid: 4848514907161231460 + - rid: 1798538364032909471 m_GraphWireModels: - rid: 6600512887158735113 - rid: 4848514907161231445 - rid: 4848514907161231461 + - rid: 1798538364032909472 m_GraphStickyNoteModels: [] m_GraphPlacematModels: [] - m_GraphVariableModels: [] + m_GraphVariableModels: + - rid: 1798538364032909469 m_GraphPortalModels: [] m_SectionModels: - rid: 6600512887158735085 m_LocalSubgraphs: [] m_LastKnownBounds: serializedVersion: 2 - x: -404 - y: -65 - width: 1461 - height: 468 + x: -608 + y: -68 + width: 1683 + height: 533 m_GraphElementMetaData: - m_Guid: m_Value0: 8557079674992562767 @@ -344,6 +487,30 @@ MonoBehaviour: Hash: bac3dd11166ff1535d7669a3093a1889 m_Category: 2 m_Index: 2 + - m_Guid: + m_Value0: 9222896094694858974 + m_Value1: 6191622170130805114 + m_HashGuid: + serializedVersion: 2 + Hash: dee09f14224ffe7f7a7114228f0fed55 + m_Category: 1 + m_Index: 0 + - m_Guid: + m_Value0: 16565847150320723582 + m_Value1: 2873397475484516538 + m_HashGuid: + serializedVersion: 2 + Hash: 7ea648ae36b6e5e5ba40c2b0c75be027 + m_Category: 0 + m_Index: 4 + - m_Guid: + m_Value0: 9254597602728976179 + m_Value1: 9128070447517429019 + m_HashGuid: + serializedVersion: 2 + Hash: 339fd7997cef6e801b6d049db56bad7e + m_Category: 2 + m_Index: 3 m_EntryPoint: rid: 6600512887158735087 m_Graph: @@ -358,7 +525,8 @@ MonoBehaviour: serializedVersion: 2 Hash: 5e4d7654eb10d4c0f0b00185dee347ea m_Version: 2 - m_Items: [] + m_Items: + - rid: 1798538364032909469 m_Title: - rid: 6600512887158735086 type: {class: DialogGraph, ns: DinoLove.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} @@ -373,7 +541,7 @@ MonoBehaviour: serializedVersion: 2 Hash: 4f92a586a0dac076e9c61595bc9ef81c m_Version: 2 - m_Position: {x: -127.74372, y: -64.907875} + m_Position: {x: -331.78055, y: -68.42575} m_Title: m_Tooltip: m_NodePreviewModel: @@ -394,6 +562,9 @@ MonoBehaviour: - __option_HiddenBranchCount - Typewriter - Sfx + - FrontSprite + - SideSprite + - ClearSprite m_ValueList: - rid: 6600512887158735088 - rid: 6600512887158735093 @@ -408,6 +579,9 @@ MonoBehaviour: - rid: 4848514907161231424 - rid: 4848514907161231425 - rid: 4848514907161231443 + - rid: 1798538364032909463 + - rid: 1798538364032909464 + - rid: 1798538364032909465 m_InputPortInfos: expandedPortsById: m_KeyList: [] @@ -478,7 +652,7 @@ MonoBehaviour: serializedVersion: 2 Hash: 8c67ef2b20c4d9bd2233d18b090d1fc8 m_Version: 2 - m_Position: {x: -404.02042, y: 43.72515} + m_Position: {x: -608.05725, y: 40.207275} m_Title: m_Tooltip: m_NodePreviewModel: diff --git a/Assets/08_UI/DialogEnterPanelSettings.asset b/Assets/08_UI/DialogEnterPanelSettings.asset index 98adef1..33e2386 100644 --- a/Assets/08_UI/DialogEnterPanelSettings.asset +++ b/Assets/08_UI/DialogEnterPanelSettings.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2048c30d337e09b5eabd8998924f1acb0b6f137dc40445e506159b88f39c529b -size 1958 +oid sha256:053cb745b8a20dc03f52271d5b5cf9155660d5777dc6651de4b4309388981d3a +size 1959 diff --git a/Assets/08_UI/DialogPanelSettings.asset b/Assets/08_UI/DialogPanelSettings.asset index aeb1868..2376ce5 100644 --- a/Assets/08_UI/DialogPanelSettings.asset +++ b/Assets/08_UI/DialogPanelSettings.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f041b2a64e736410dd0691cd29c316e0f037df89449d5f5149f910fb10975a10 -size 1953 +oid sha256:59735cd4f22a2f2173dbf8bc9c8c909c1340687ed7985ba973753162176c1480 +size 1954 diff --git a/Assets/08_UI/DialogUI.uxml b/Assets/08_UI/DialogUI.uxml index f505f25..12cb6f4 100644 --- a/Assets/08_UI/DialogUI.uxml +++ b/Assets/08_UI/DialogUI.uxml @@ -2,9 +2,9 @@