나는 예전에도 게임을 만든 적이 있었다. 다만 그 게임은 "똥겜" 이라고 부르기도 민망한 수준의 조잡한 게임이었다. 친구 한 명을 대상으로 만든 게임인데, 대학교 1학년 당시 Visual Basic으로 몇 개월에 걸쳐서 만들었다. 전체 코드가 대략 만 줄 혹은 그 이상이었던 것 같은데, 덕분에 버그도 많고 밸런스도 잘 안맞아서 플레이 중간중간 패치를 하면서 진행했다. 그 게임을 플레이해 준 친구가 이전 글에서 언급한 블루 아카이브를 권한 친구이다. 그땐 되게 조잡해서 실망도 많이 했지만, 지금 와서 다시 생각해보면 아주 좋은 경험이었던 것 같다. 아무튼, 이번에는 뭔가 "그럴듯한" 게임을 만들어보고 싶은 마음이 들었다.

 

"그럴듯한" 게임을 만드려면 어떻게 해야할까? 게임을 할 때 첫인상은 굉장히 중요하다. 누가봐도 "똥겜"같으면 플레이 하고 싶은 욕구가 팍 떨어진다. 세상에는 뛰어난 게임들이 많기 때문이다. 그래서 인트로 화면을 어떻게 하면 좋을까를 먼저 생각했다. 대부분의 게임들을 보면 처음에 검은 화면에서 제작사의 로고가 뜨고, 이내 게임 화면으로 넘어간다. 아마 로딩의 목적도 있을테지만, 이 행위 자체가 대부분 대형 게임에서 채택하고 있기 때문에 뭔가 "그럴듯해 보이는" 느낌이 날 것 같았다.

 

그래서 곧바로 프로젝트를 생성했다. Unity 버전은 미리 깔려있던 2020.3.21f1으로 했고, 2D로 프로젝트를 생성했다. 프로젝트를 실행하자마자 이런저런 오류가 떠서 오류를 없애는 것부터 했다. 처음 프로젝트를 만들면 굉장히 휑하다. 무슨 일이든 시작이 힘들듯 무엇부터 해야 할지 고민이 됐다.

 

초반에 게임개발부에서 테일즈 사가 크로니클을 플레이할 것이기 때문에, 해당 배경을 먼저 준비하기로 했다.

인터넷에 찾아보니 큰 이미지가 있었다. 그런데 화질이 별로 좋지 않았다.

 

waifu2x (udp.jp)

 

waifu2x

waifu2x 컨볼루션 신경망(CNN) 시스템을 통한 딥 러닝으로 2차원 이미지의 해상도를 개선합니다. 그림뿐만 아니라 사진도 지원합니다. 실제 예제 보기 | GitHub에서 보기 This website uses Google Analytics to h

waifu2x.udp.jp

그래서 인터넷에서 이미지 화질을 개선해주는 사이트에서 화질을 높였다.

화질이 눈에 띄게 좋아졌는데, 아쉽게도 TV 화면이 원본과 약간 달라졌다. 그래도 자세히 보지 않는 이상 크게 눈에 띄지는 않았기 때문에 냅두기로 했다.

 

그리고 Sprite를 활용해 해당 이미지를 넣었다. 이미지를 넣고 나니 이전에 회사에 재직하던 시절에 해상도 대응과 관련된 일이 떠올랐다. 생각해보니 이미지는 Canvas 안에 집어넣어야 했던 게 기억이 나서 Canvas를 생성해서 집어넣었다.

이후 Canvas 내의 설정 중 Render Mode를 Screen Space - Camera로, Render Camera에 Main Camera를 등록해줬다. Canvas Scaler가 해상도 대응에 쓰였던 기억이 나서 UI Scale Mode를 Scale With Screen Size로, Reference Resolution은 1980x1080으로, Screen Match Mode는 Match Width Or Height로 설정했다.

이런 대응을 안해주면, 이미지의 비율이나 크기가 화면과 맞지 않을 경우 이상하게 보여지는 문제가 있다. 이런 것들이 기억이 나는 것을 다행이라고 느꼈다. 이렇게 설정을 하고 나니 뭔가 채워져서 그림이 보이기 시작했다.

인트로에는, 개발사 등의 글씨를 넣을 수는 없으니 이 게임이 블루 아카이브의 공식 게임이 아닌 팬 게임임을 알리는 텍스트를 넣어야겠다고 생각했고, 기본 Text에 비해 TMP(TextMeshPro)가 여러 기능이 있어 편리했던 기억이 나서 TMP를 사용했다. 글꼴은 인터넷에서 찾은 블루 아카이브의 글꼴을 참조했고, 이를 토대로 TMP에서 사용할 수 있는 SDF 파일을 만들어 글씨를 사용했다. 블루 아카이브의 글씨체가 나오니 뭔가 기분이 좋았다. 뭔가 둥글둥글하니 귀여워서 마음에 든다.

 

그리고, 이 텍스트가 검은 화면에서 Fade In되고, 다시 Fade Out 되는 기능을 만들기 위해 GameManager를 만들었다. 그리고 GameManager에서 코드를 치다보니 Text를 사용할 일이 많을 것 같아 따로 TextManager로 분리시키기로 했다.

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class TextManager : MonoBehaviour
{
    [SerializeField]private TMP_Text guideText;
    [SerializeField]private float timeMultiplier;

    private IEnumerator FadeInText(float timeSpeed, TMP_Text text)
    {
        text.color = new Color(text.color.r, text.color.g, text.color.b, 0);
        while(text.color.a < 1.0f)
        {
            text.color = new Color(text.color.r, text.color.g, text.color.b, text.color.a + (Time.deltaTime * timeSpeed));
            yield return null;
        }
        yield return new WaitForSeconds(1f);
        StartCoroutine(FadeOutText(timeMultiplier, guideText));
    }

    private IEnumerator FadeOutText(float timeSpeed, TMP_Text text)
    {
        text.color = new Color(text.color.r, text.color.g, text.color.b, 1);
        while (text.color.a > 0.0f)
        {
            text.color = new Color(text.color.r, text.color.g, text.color.b, text.color.a - (Time.deltaTime * timeSpeed));
            yield return null;
        }
    }

    public void TextOpening()
    {
        StartCoroutine(FadeInText(timeMultiplier, guideText));
    }
}

c# - Using coroutines to fade in/out TextMeshPro Text Element - Stack Overflow

 

Using coroutines to fade in/out TextMeshPro Text Element

I am setting up a "scene intro" with some text saying 'Level 1' using TextMeshPro. I have created the text element in canvas and i am trying to find a way to make it fade in, then wait, and then fa...

stackoverflow.com

Fade In과 Fade Out을 시간에 걸쳐서 표현하기 위해서 Coroutine을 사용한 코드를 참고했다.

 

그러던 도중, 코루틴의 반환형은 IEnumerator이 실제로 무슨 역할을 할까 궁금하여 찾아보게 되었다.

IEnumerator란 무엇인가? (slideshare.net)

 

IEnumerator란 무엇인가?

유니티3D를 공부하면서 IEnumerator가 무엇인지 공부한 내용입니다.

www.slideshare.net

굉장히 이해하기 쉽고 재밌게 되어있었다. 이전에 어느 부분까지 실행했는지 기억해주는 역할을 하는 모양이다. 평소엔 그냥 별 생각 없이 썼는데, 알고 나니 앞으로 다른 코드들을 더 쉽게 이해할 수 있을 것 같다.

아, 그리고 언제 나온 기능인지는 모르겠지만 이렇게 내가 하려는 내용을 미리 예측하고 자동완성을 해주는 기능이 있었는데, 굉장히 편했다.

 

아무튼, 이렇게 하니 벌써 약간은 그럴듯한 인트로가 완성되었다.

이 다음에 메인화면과 배경 음악이 나오면 되겠다고 생각했다. 그런데 배경 음악에서 약간의 문제가 있었다.

약관을 참조하면 블루 아카이브의 BGM을 그대로 사용해도 아마 문제가 없겠지만, 이미 많은 유저들은 이 노래를 반복적으로 들었을 것이다. 노래 자체에서 블루 아카이브의 느낌이 느껴지기도 하니 다른 부분에서 활용할 수는 있어도, 메인 테마만큼은 다른 음악을 사용해야겠다고 생각했고, 이 음악으로 어떤 것을 사용할 것인가를 정하는 것이 문제였다.

 

처음에는 이전 글에서 언급했던 AI그림처럼 AI작곡을 활용해보면 어떨까? 하는 생각이 들었다. 그렇게 사용하게 된 프로그램이 Musia인데, 랜덤으로 적절한 멜로디를 만들어주고, 수많은 악기 중에서 사용자가 원하는 악기를 선택할 수 있었다. 다만, 문제는 최대 2개의 악기만 동시에 재생이 가능했고, 제대로 된 음악을 만들기 위해서는 사용자가 이 멜로디를 이용해 편집을 해야했다. 나에겐 좋은 음악을 만들만한 소양이 없었기 때문에 조금 알아보다가 포기했다. 생각해보면 그렇다. 다양한 악기로 여러 멜로디를 연결해서 완성되는 음악이 단순히 랜덤으로 나오기도 쉽지 않다. 악기의 종류도 많고, 어울리는 악기의 종류들도 다르고, 음악에도 도입부나 클라이맥스 부분 등이 있기 때문에 그리 쉽게 대단한 노래가 나오지는 않는 것이 어찌보면 당연하다. 아무튼, 아직까지는 작곡을 하는 사람들을 도와주는 수준에 그치는 것 같았다.

 

Musia에 대해 검색하던 도중, Musia를 활용해 음악을 만든 사람이 있었다. 음악을 무료로 사용가능하다고도 되어있었다. 심지어 상업적 용도로 사용해도 된다고 했다. 그래서 무료로 사용 가능한 음악들을 유튜브에서 찾아 활용하면 되겠다는 생각에 이르렀다. 처음엔 한글로 검색을 했다.

그런데 한국에서 무료로 사용 가능하다고 되어있는 것들은 대부분 유튜브 한정이었다. 사용 가능한 범위를 딱 정해주니 아무리 비상업적 용도라고 해도 마음대로 가져다 쓸 수는 없었다.

그래서 영어로 검색해보았다. Royalty Free와 Copyright Free가 있었는데, Royalty Free는 Copyright Free에 비해 조금 더 조건이 엄격해보였다. 내가 사용할 수 있을만한 것은 Copyright Free였고, 그 중에서도 게임에서 활용이 가능한 것이었다. 대부분의 노래들은 유튜브에서 사용만을 허가했다. 사실 사용해도 크게 문제가 되지는 않겠지만, 하지 말란 짓을 해서 좋을 것은 없다.

그러면서 일본쪽의 노래도 나와서, 著作権フリー(저작권 Free) 라는 키워드로도 검색해보았다. 다행히도 일본쪽은 대체로 자유로운 사용이 가능한 편이었다. 게임에 사용해도 괜찮다는 얘기도 많이 있었고, 심지어 상업적인 사용을 허락하는 경우도 많았다. 그리고 내 감성에 맞는 음악들도 많았다. 유튜브에는 참 많은 음악들이 있었고, 그 중 마음에 드는 음악을 찾기란 어려웠다. 거의 하루종일 음악을 듣고, 마음에 드는 노래들을 저장했다.

 

이러면서 게임 음악을 선정하는 것도 굉장히 큰일이겠구나, 싶은 생각이 들었다. 음악은 사람의 마음을 움직이는 것이니만큼 굉장히 중요한 부분을 차지하고 있고, 게임의 전체적인 분위기와 어울려야하고, 게임에서 표현하는 상황과도 걸맞아야 한다. 어떤 느낌의 음악을 원하는지 표현하는 것도 굉장히 어려울 것 같았고, 그 표현을 듣고 그에 걸맞는 음악을 만들어내는 것도 굉장히 어려울 것이다. 하지만 확실히 알게된 것은, 작곡가마다 각각의 스타일이 있다는 것이다. 어떤 노래를 듣고 좋다고 생각했을 때, 그 작곡가의 다른 노래들을 들어보면 마음에 드는 경우가 많다. 나의 경우 영웅전설3의 ost는 대체적으로 마음에 들고, 블루 아카이브의 노래들도 굉장히 좋다. 블루 아카이브의 노래들은 대부분 엄청나게 반복해서 들었는데, 생각해보면 내가 블루 아카이브를 좋아하는 이유 중에 "노래가 좋아서"가 많은 부분을 차지하고 있는 것 같다.

 

아무튼, 메인 화면에 어울릴만한 테마는 게임개발부에 걸맞는 게임적인 느낌이 나면서 신나는, 새로운 모험을 시작하는 것 같은 느낌이 드는 노래를 원했다.

 

내가 찾은 노래 중 가장 적합한 노래는 DOVA-SYNDROME의 yuhei komatsu 작곡가의 Restart였다.

https://youtu.be/W09US2ycxsg

이 노래를 재생하고 관리하기 위해서 AudioManager를 만들었다.

 

Ch 12. 오디오 매니저(풀링 개념) - 평생 공부 블로그 : Today I Learned‍ 🌙 (ansohxxn.github.io)

 

Ch 12. 오디오 매니저(풀링 개념)

케이디님의 [유니티 강좌] 리듬 게임 유튜브 강의를 듣고 정리한 필기입니다. 😀 🌜 강의 들으러 가기 Click

ansohxxn.github.io

AudioManager를 만들 때는 이 사이트의 내용을 참조했다. 노래를 틀어보니 볼륨이 커서 배경음악의 볼륨을 조절하는 함수도 추가했다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class Sound
{
    public string name;
    public AudioClip clip;
}

public class AudioManager : MonoBehaviour
{
    public static AudioManager instance;

    [SerializeField] Sound[] sfx = null;
    [SerializeField] Sound[] bgm = null;

    [SerializeField] AudioSource bgmPlayer = null;
    [SerializeField] AudioSource[] sfxPlayer = null;

    void Start()
    {
        instance = this;
    }

    public void PlayBGM(string p_bgmName)
    {
        for (int i = 0; i < bgm.Length; i++)
        {
            if (p_bgmName == bgm[i].name)
            {
                bgmPlayer.clip = bgm[i].clip;
                bgmPlayer.Play();
            }
        }
    }

    public void StopBGM()
    {
        bgmPlayer.Stop();
    }

    public void PlaySFX(string p_sfxName)
    {
        for (int i = 0; i < sfx.Length; i++)
        {
            if (p_sfxName == sfx[i].name)
            {
                for (int j = 0; j < sfxPlayer.Length; j++)
                {
                    // SFXPlayer에서 재생 중이지 않은 Audio Source를 발견했다면 
                    if (!sfxPlayer[j].isPlaying)
                    {
                        sfxPlayer[j].clip = sfx[i].clip;
                        sfxPlayer[j].Play();
                        return;
                    }
                }
                Debug.Log("모든 오디오 플레이어가 재생중입니다.");
                return;
            }
        }
        Debug.Log(p_sfxName + " 이름의 효과음이 없습니다.");
        return;
    }

    public void SetBGMVolume(float volume)
    {
        bgmPlayer.volume = volume;
    }
}

어제 했던 부분은 여기까지다. 음악을 고르느라 별로 많이 진행하진 못했다. 오늘도 여태까지의 과정을 이렇게 글로 쓰고, 중간중간 더 좋은 노래는 없을까 싶어서 찾아보다보니 벌써 저녁 시간이다. 딱히 딴짓을 하지도 않았는데 시간이 금방 간다. 게임 개발을 할 때면 게임을 할 때보다 시간이 더 잘 가는 것 같다. 아무래도 내 천직인 것 같다.

오늘 하루는 이제 별로 남지 않았지만 인트로 화면이 너무 단조로워서 뭐라도 추가할 걸 찾으면 좋을 것 같고, 메인화면에 쓸만한 그림들을 찾아서 추가할 수 있으면 좋을 것 같다. 그리고 게임 타이틀을 어떻게 만들면 좋을지 단서를 찾을 수 있으면 좋겠다.

+ Recent posts