>작업 진행도 

더보기
# 대화 플로우 작업 근황

## 개요
- **1차 최적화 리팩토링 이후 중단되었던 대화 씬(Dialog Scene) 작업 재개**
- 기존 작업 기록을 확인한 결과, **기존 코드를 이어서 작업하기보다는 리팩토링 기반으로 재구성하는 방향으로 진행**

### 진행 방향 변경 이유

**1. 스토리 모드 프레임워크 피드백 반영**
- 팀장과 스토리 모드 프레임워크 회의를 진행
- 여러 피드백 및 레퍼런스를 기반으로 **기존 코드 구조를 확장성을 고려한 구조로 업그레이드**

**2. 기능 확장성 고려**
- 기존 코드에 기능을 덧붙이는 방식보다  
- **로직 구조 자체를 업그레이드하는 방식이 이후 기능 추가 및 유지보수에 더 유리하다고 판단**

---

# 현재 작업 진행 상황

## 1. DialogManager 외부 데이터 로딩 구조 설계
**Excel → JSON 역직렬화 구조 설계**

- `Type` 헤더 값을 기준으로 각 상황에 맞는 동작을 수행하도록 설계
- Type 카테고리는 아직 최종 확정 단계는 아님

### 예정된 Type 카테고리

| Type | 설명 |
|-----|-----|
| talk | Actor(캐릭터)가 대사를 출력할 때 사용 |
| narration | 나레이션 대사 출력 |
| emote | 감정 표현 Spine Animation 실행 |
| skin | Actor 의상 / 얼굴 Spine Skin 변경 |
| choice | 선택지 제공 |
| answer | 선택지 결과 처리 |
| move | 대상 오브젝트(캐릭터, 배경 등)의 좌표 이동 |
| fade | 대상 오브젝트 Fade In / Fade Out |
| transition | 화면 전환 처리 |
| play | BGM / SFX 재생 |
| effect | 화면 특수효과 (애니메이션 / 파티클) |

---

# 2. Asset 구조 분리

대화 시스템에서 사용하는 리소스를 **Common Asset** 과 **Dialog Asset** 기준으로 분리

## Common Asset
프로젝트 전체에서 공통적으로 사용되는 리소스

- BGM
- SFX
- Effect (화면 연출용 파티클 및 애니메이션)
- 나레이션 UI 연출 프리팹

## Dialog Asset
특정 Dialog Script에서만 사용되는 리소스

- 캐릭터 Spine Skeleton Asset (스탠딩 캐릭터 애니메이션 포함)
- 배경 이미지
- 특정 CG 이미지

---

# 3. DialogManager 신규 기능

## 3-1 Actor Spine기능
- Actor **Spine Skin 조합 및 애니메이션 재생**

## 3-2 Actor 이동
- Actor 캐릭터 **좌표 이동 애니메이션**

## 3-3 대상 오브젝트 좌우반전 처리
- Actor / Emote / TextBox **좌우 반전 기능**

## 3-4 Emote 처리
- Emote 애니메이션 재생
- Actor 대상에 따른 **Emote 위치 자동 설정**

## 3-5 TextBox 처리
- 대사 출력
- Actor 대상에 따른 **TextBox 위치 자동 설정**

## 3-6 배경 및 CG
- BG / CG Image 설정 기능
- CG **비활성화 기능**

---

# 4. DialogManager 추가 예정 기능

## 4-1 Effect
- Effect 애니메이션 재생
- 화면 파티클 연출 기능

 

 

해당 작업을 하며 배운 기술 리스트 공유

- Spine 객체 스킨 조합 

더보기

Spine Asset에 포함되어있는 Skin을 한번에 여러개 출력하려면 
Skin 출력시마다 해당 스킨 조합을 만들어서 적용해야된다. 

using UnityEngine;
using Spine.Unity;
using Spine;

public class SpineController 
{
    public SkeletonGraphic Graphic { get => graphic; set => graphic = value; }
    public TrackEntry TrackEntry => trackEntry; 

    private TrackEntry trackEntry;

    private SkeletonDataAsset dataAsset;
    private SkeletonGraphic graphic;

    public void CombineSkin(params string[] skinNames)
    {
        var skeleton = graphic.Skeleton;
        var data = skeleton.Data;

        Skin combinedSkin = new Skin("CombinedSkin");

        for (int i = 0; i < skinNames.Length; i++)
        {
            string skinName = skinNames[i];

            if (string.IsNullOrWhiteSpace(skinName))
                continue;

            var skin = data.FindSkin(skinName);

            if (skin == null)
            {
                Debug.LogWarning($"[SpineSkinCombiner] Skin not found: {skinName}");
                continue;
            }

            combinedSkin.AddSkin(skin);
        }

        skeleton.SetSkin(combinedSkin);
        skeleton.SetSlotsToSetupPose();
        graphic.Update(0);
    }
}

 

프로젝트L에 사용하는 코드 일부를 가져옴.

 

얼굴에서 사용되는 face/Idle과 outpit/School 이라는 스킨을 동시에 사용하려면

spineController.CombineSkin("face/Idle", "outpit/School");

이런식으로 사용

 

p.s new 할때마다 Heap 메모리에 쌓이고 GC가 동작할 여지를 주는데

대화씬이기에 최적화가 필요한 구간이 아니라 그냥 사용하기로 하였다.

- Spine 애니메이션 완료 이벤트 구독 및 실행

더보기

Spine 애니메이션이 재생이 완료가 됬으면 동작시킬수 있는 로직을 추가하고싶을때 사용하는 메서드이다.

 

Spine API에 기록되어있는 TrackEntry의 델리게이트와 이벤트 목록들
해당 이벤트 등록시 매개변수로 TrackEntry를 필요로함

 

해당 코드는 Spine 라이브러리에 있는거라 캡쳐해서 가져옴

 

우선 사용 로직은 이렇다.

//track = 애니메이션 트랙 ( 코드상에서 설정 가능)
//animationName = 애니메이션 이름
//loop = 반복 여부

private TrackEntry trackEntry;

// 1.이벤트 구독을 하고싶은 애니메이션을 설정 후 trackEntry에 초기화한다.
trackEntry = graphic.AnimationState.SetAnimation(track, animationName, loop);

//--- 

// 2. 이벤트를 구독할 함수를 만든다. (함수명은 자유 , 매개변수는 무조건 TrackEntry 선언)

    private void OnAnimationComplete(TrackEntry trackEntry)
    {
        // 재등록/중복호출 방지용 해제
        trackEntry.Complete -= OnAnimationComplete;

		//애니메이션 재생이 완료되면 해당 Spine 객체를 비활성화
        gameObject.SetActive(false);
    }

//---

// 3. 해당 애니메이션에 이벤트를 구독 
spineController.TrackEntry.Complete += OnAnimationComplete;

 

필요한 메서드들만 떼서 작성했는데 , 이 로직을 따라가면 Spine API가 이벤트를 재생 시켜준다. 

 

- 부모가 다른 RectTransform 위치 좌표 이동시 월드좌표 수정

더보기

당연하다면 당연한건데,

부모가 다른 객체를 해당 객체를 그대로 좌표 이동하면 위치가 다른 현상이 있다.

부모 객체가 다른 UI에 해당 좌표로 이동을 시켜야 될때

 

부모가 같으면 해당 코드를 그대로 사용해도 문제 없다.

textbox.GetComponent<RectTransform>().anchoredPosition = ActerPosList[0].anchoredPosition;

 하지만 계층구조로 인해 부모가 다를 경우 위치뿐만 아니라 크기,회전 등 전부다 틀어질 가능성이 생긴다.

 

이런 상황일때는 실제로 렌더링되서 비춰지는 월드 좌표를 스크린 좌표로 바꿔주고 해당 스크린 좌표를 

옮기려는 좌표의 부모 기존 로컬 좌표로 변환해줘야한다.

 /// <summary>
    /// 부모가 다른 RectTransform 의 위치값을 변경할때 사용
    /// </summary>
    /// <param name="source"></param>
    /// <param name="target"></param>
    public static void MatchUIPosition(RectTransform source, RectTransform target)
    {
        RectTransform targetParent = target.parent as RectTransform;
        Canvas canvas = target.GetComponentInParent<Canvas>();

        Camera cam = null;
        if (canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay)
            cam = canvas.worldCamera;

        // source의 pivot 월드 위치 -> 스크린 좌표
        Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(cam, source.position);

        // 스크린 좌표 -> target 부모 기준 로컬 좌표
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            targetParent,
            screenPoint,
            cam,
            out Vector2 localPoint
        );

        target.anchoredPosition = localPoint;
    }

좌표 변환(월드좌표->스크린 좌표 , 스크린 좌표-> 부모기준 로컬좌표)은 Unity 라이브러리에서 제공되는 RectTransformUntility 를 사용

RectTransfrom 사용시 도움이 되는 API

 

 

- Spine 객체 좌우 반전 

더보기

Skeleton의 ScaleX값을 음수화 해주면된다...

너무 간단해서 놀램

 private SkeletonGraphic graphic;

 graphic.Skeleton.ScaleX = -1 ; //(좌/우 반전)
 graphic.Skeleton.ScaleX = 1 ; //(좌/우 반전 해제)

 

+ Recent posts