유니티 일기

탑다운 2D RPG - 대화 애니메이션 느낌있게 만들기

mky 2025. 9. 6. 18:52

골드메탈님의 유튜브 강의를 보고 배운 것을 정리하였습니다.

 

1. 대화창 이펙트

- 애니메이터 하나 생성하여 대화 UI에 추가

- 대회 UI를 화면 아래로 내려서 안보이게 설정

- 변수 타입을 애니메이터로 교채했으므로 재할당 필

2. 초상화 이펙트

- 애니메이터, 애니메이션 1개씩 초상화 Image에 생성

- 과거 스프라이트를 저장해두어 비교 후, 애니메이션 실행

3. 타이핑 이펙트

- 타이핑이펙트 스크립트 생성

- 표시할 대화 문자열을 따로 변수로 저장

- 글자 재생 속도를 위한 변수 생성(CPS)

- 대화 문자열을 받는 함수 생성

- 애니메이션 재생을 위한 시작-재생-종료 3개 함수 생성

- TextMeshProUGUI 변수 생성, 초기화 후, 시작함수에서 공백 처리

- 시간차 반복 호출을 위한 Invoke 함수를 사용

- 1초 / CPS = 1글자가 나오는 딜레이

- 대화 문자열과 Text내용이 일치하면 종료

- 게임매니저에서 기존에 사용하던 Text 변수를 작성한 이펙트스크립트로 변경

- 확실한 소수값을 얻기 위해 분자 1.0f 작성

- AudioSource 변수를 생성, 초기화 후 재생 함수에서 Play()

- 공백과 마침표는 사운드 재생 제외

- 게임 실행시 사운드가 1회 재생되는 문제 -> 사운드소스의 Play On Awake 끄기

- 애니메이션 실행 판단을 위한 플래그 변수 생성 -> 플래그 변수를 이용하여 분기점 로직 작성

- 매니저에서도 플래그 변수를 이용하여 분기점 로직 작성

 

using TMPro;
using UnityEngine;
using UnityEngine.UI;


public class TypeEffect : MonoBehaviour
{
    string targetMsg;
    public float CPS; // 글자 재생 속도
    public GameObject EndCursor;
    TextMeshProUGUI msgText;
    AudioSource audioSource;
    int index;
    float interval;
    public bool isAnim; //애니메이션 실행 판단을 위한 플래그 변수 생성

    private void Awake()
    {
        msgText = GetComponent<TextMeshProUGUI>();
        audioSource = GetComponent<AudioSource>();
    }
    public void SetMsg(string msg)
    {
        if (isAnim) // Interupt
        {
            msgText.text = targetMsg; // 다 채우기
            CancelInvoke(); // 인보크 함수 꺼짐
            EffectEnd();
        }
        else
        {
            targetMsg = msg;
            EffectStart();
        }
    }

    void EffectStart()
    {
        msgText.text = "";
        index = 0;
        EndCursor.SetActive(false);

        // Start Animation
        interval = 1.0f/CPS;
        Debug.Log(interval);

        isAnim = true;

        Invoke("Effecting", 1/CPS);
    }

    void Effecting() // 재귀
    {
        if (msgText.text == targetMsg)
        {
            EffectEnd();
            return;
        }

        msgText.text += targetMsg[index];
        
        //Sound
        if (targetMsg[index] != ' ' || targetMsg[index] != '.')
        {
            audioSource.Play();
            Debug.Log("사운드 재생");
        }
        Invoke("Effecting", 1 / CPS);
        index++;
    }

    void EffectEnd()
    {
        isAnim = false;
        EndCursor.SetActive(true);
    }
}
using TMPro;
using UnityEngine;
using UnityEngine.UI;


public class GameManager : MonoBehaviour
{
    //public GameObject talkPanel;
    public Animator talkPanel;
    public Image portraitImg;
    public Animator portraitAnim;
    public Sprite prevPortrait;
    //public TextMeshProUGUI talkText;
    public TypeEffect talk;
    public GameObject scanObject;
    public bool isAction; // 상호작용 중인지 아닌지 판단
    public int talkIndex; // 대화 인덱스

    public static GameManager instance = null;
    void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
    }

    void Start()
    {
        Debug.Log(QuestManager.instance.CheckQuest());
        
    }

    public void Action(GameObject scanObj)
    {
        isAction = true;
        scanObject = scanObj;
        ObjData objData = scanObject.GetComponent<ObjData>(); // 오브젝트의 데이터를 가져옴
        Talk(objData.id, objData.isNpc); // 대화
        //Debug.Log(objData.id + ", " + objData.isNpc);

        // Visible Talk for Action
        talkPanel.SetBool("isShow", isAction);
    }

    void Talk(int id, bool isNpc)
    {
        // Set Talk Data
        int questTalkIndex = 0;
        string talkData = "";

        if (talk.isAnim)
        {
            talk.SetMsg("");
            return;
        }
        //talk.SetMsg("");
        else
        {
            questTalkIndex = QuestManager.instance.GetQuestTalkIndex(id); // 퀘스트번호를 가져옴
            talkData = TalkManager.instance.GetTalk(id + questTalkIndex, talkIndex); // npc id + 퀘스트 번호 = 퀘스트 대화 데이터 ID
        }

        //int questTalkIndex = QuestManager.instance.GetQuestTalkIndex(id); // 퀘스트번호를 가져옴
        //string talkData = TalkManager.instance.GetTalk(id + questTalkIndex, talkIndex); // npc id + 퀘스트 번호 = 퀘스트 대화 데이터 ID

        //End Talk
        if (talkData == null) 
        {
            isAction = false;
            talkIndex = 0;
            Debug.Log(QuestManager.instance.CheckQuest(id)); // 퀘스트 진행 상황 체크
            return; // 대화 끝
        }

        // Continue Talk
        if (isNpc)
        {
            talk.SetMsg(talkData.Split(':')[0]); // 수정

            portraitImg.sprite = TalkManager.instance.GetPortrait(id, int.Parse(talkData.Split(':')[1]));
            portraitImg.color = new Color(1, 1, 1, 1); // 알파값 1로 변경
            //Animation Portrait
            if(prevPortrait != portraitImg.sprite)
            {
                portraitAnim.SetTrigger("doEffect");
                prevPortrait = portraitImg.sprite; //갱신
            }
        }
        else
        {
            //talkText.text = talkData;
            talk.SetMsg(talkData); // 수정

            //Hide Portrait
            portraitImg.color = new Color(1, 1, 1, 0);
        }

        isAction = true;
        talkIndex++;
    }

}