본문 바로가기

IT/유니티

9. 유니티 교육 (유니티 기본4)

1. C#은 힘들어.


지난번 가지 퀘스트 시작과 활성 그리고 완료 까지 해보았는데, 

문제는 퀘스트를 성공했을때 성공 메세지가 나와야 하는데 나오지 않았다.

그이유는 WaitForSeconds였다.

자바스크립트에서는 그냥 같다쓰면 바로 되는것이 C#에서는 따로 함수를 만들어서 해줘야했기때문이다.

즉, 자바랑 c랑 문법이 달랐다는 이야기인데,

문제는 유니티공홈에 있는 대로 따로 함수를 만들어서 해도 되지 않는것이 문제였다.

아무리 해도 안되고, 구글링을 해도 결국 공홈이랑 똑같이 이야기만 하고...

그러던 중 결국 구글링해서 답을 발견할수 있었다.

C#에서 WaitForSeconds를 하기 위해서는 메소드를 따로 만들고, 그 메소드를 

coroutine으로 호출해야 한다.


여기서 coroutine은 다중쓰레드를 이야기하는것인데, 이것을 간과해서 그냥 갔다쓰면 되는줄알았는데,

다중쓰레드이므로, 

StartCoroutine()라는 함수 이외의 것이 함수안에 같이 있으면, 그 함수의 쓰레드를 그냥 계속한다는것이다.

결국 StartCoroutine으로 새로 생긴 쓰레드를 무시하고 함수안의 자기 쓰레드를 움직인다는것으로,

결국 WaitForSeconds는 무시되고 만다.


그렇게 때문에 결국 함수안에는 StartCoroutine만 넣어두고 

StartCoroutine에서 호출되는 함수안에서 모든것을 코딩해두면 문제를 해결할수 있다.


단순한 대기함수 하나 쓰기도 힘들다................거지같내.


소스코드는 다음과 같다.


void OnMouseDown()

    {

        StartCoroutine(QuestComplete());


        

    }


    IEnumerator QuestComplete()

    {

        if (TextBoxOnCheck == 0)

        {

            TextBoxOnCheck = 1;

            TextBox.GetComponent<Text>().text = "You found the loot.";

            MessageBox.SetActive(true);


            Debug.Log("About to StartCoroutine");

            yield return new WaitForSeconds(3);

            Debug.Log("Back from StartCoroutine");


            gameObject.SetActive(false);

            MessageBox.SetActive(false);

            QuestItemToClose.SetActive(false);

            QuestItemToShow.SetActive(true);

        } 

    }



2. 소리가 안들려!!


아..정말 유니티가 불안정하고는 들었는데, 이렇게까지 거지같을줄이야.

연습을 하고 있는데, 갑자기 사운드가 안들려서 사운드 클립 문제인제, 스피커를 음소거했는지..

별의별 원인을 상상하고 찾아 봤는데 아무 문제가 없어서 또 몇시간을 헤매다가

구글님께서 가르쳐주신거는 껏다 켜봐라!

그래서 윈도운도 유니티도 다 다시 해봤는데, 역시나!! 안됬다는..

그럼 어떻게 해결했냐!

그냥 다른 프로젝트 실행시키고 다시 원래 프로젝트 실행시키니까 소리가 들린다...이런 ㅄ같은 경우가..



3. 숲이 생겼어요.


지난번에 나무를 몇개씩 임포트해서 지형에 넣었는데,

만약 숲을 만들고 싶다고 어떻게 해야할까?

하나씩 다 찝어서 심어줘야 될까?

그럴라면 왜 엔진을 사용하겠는가. ㅋㅋ 간단한 방법이 있다.


지형에서 지형을 높이거나 낮게 하거나 하는 쪽에서보면 나무모양의 아이콘이 있는데,

그 나무모양의 아이콘을 클릭하면 밑에 Edit Tree버튼을 누르면 나무를 추가할수 있다.


추가된 나무를 클릭하면 브러시 사이즈와 밀집도를 조정해서 한번만 클릭하면 딱!

그냥 쉽게 숲을 만들수 있다. 랜던하게 찍히니깐 아주 간편하다.


여기서 중요한것은 그냥 외부 모델을 가져와서 사용하면 충돌체크가 되어 있지 않기때문에

그럴경우에는 박스나 실린더 콜라이더를 추가시키고 그나무를 애셋으로 따로 저장하면 

충돌체크가 되는 나무로 따로 오브젝트를 관리할수 있다. 그 나무를 지형에서 edit tree에서 추가하면

아주 쉽게 숲을 만들수 있다.






4. 효과음으로 더 잼나게!


기본 유니티 캐릭터는 걷기나 점프등의 기본 효과음이 있지만, 

우리가 전에 만든 동전을 먹을때 나 배경은 사운드는 없다.

배경음악은 간편하게 add component에서 사운드 소스를 추가하고,

원하는 음악을 audio clip에 드래그 해놓은다음, 캐릭터 밑에다가 자식으로 추가시켜놓으면 된다.

play on awake와 loop는 체크 해놓는게 좋다.

구지 설명 안해도 무슨 말인지 알리라 믿는다.


동전 먹기 효과음도 간단하다.

동전에 add component에서 사운드 소스를 추가하고,

전에 만들어 놓은 스크립트에서 

GetComponent<AudioSource>().Play()를 하면 된다.






5. 타이틀 화면 만들기


이제 대충 겜이 만들어 졌으니, 타이틀 화면을 만들어 보자.

이게 참 잼났는데, 우리가 지금까지 만든 모든 산이나 강 집 캐릭터 등은 결국 scene에 있는 것이였다.

그렇다면 만약 전혀 다른 환경에서 타이틀 화면을 만들다면 그냥 씬을 하나 더 추가하면 되는것이다.


씬을 하나 추가하고 구릉을 하나 만들고 그 구릉의가운대를 다시 파고 

파인 구덩이의 가운대를 다시 올려놓고 그 위에 나무를 하나 싶어 놓는다.

그리고 구릉의 주변을 다시 조금 높은 산으로 만들어 놓으면 기본 배경은 끝난다.


여기서 큐브를 하나 만들고 메쉬 랜더러를 해재하면 안보이게 되는데,

이큐브를 나무밑에다가 가져다 놓는다. 

그리고 카메라는 나무밑에 있는 큐브에다가 놓으면

나무를 중심으로 카메라가 있게 되는데, 카메라는 아래의 화면처럼 웅덩이와 나무가 보이게 조정한후

큐브에다가 rotate함수를 주면 마시 나무를 중심으로 빙글빙글 도는 화면을 연출할수 있다.

(회전 함수의 중싱은 자신의 부모가 되므로, 현재 카메라의 부모는 나무에 붙어 있는 객체

결국 회전의 중심은 나무가 되게 된다.)


정말 간단한게 타이틀화면을 만들수 있지 않는가?

참 쉽게 된다....이놈에 유니티는...

5년전에 했었어야 했는데.....내가 멍청했다.


그리고 버튼 2개를 추가해서 게임시작과 나가기를 만든다.

void OnGUI()

    {

        if (GUI.Button(new Rect(Screen.width/2-50, Screen.height/2, 100, 30), "Play Game"))

        {

            Application.LoadLevel(1);

        }


        if (GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 2+ 40, 100, 30), "End Game"))

        {

            Application.Quit();

        }

    }

코드는 위와 같은데, 

Application.LoadLevel(1);의 의미는 조금 있다가 이야기 하겠다.






6. 빌드해보기


이제 타이틀 화면도 있겠다 한번 빌드해보자.

file에서 build setting에 들어가면 윈도우,ios,안드로이드 등 다양한 플랫폼을 설정하고 있고,

여기서 중요한것은 scenes in build라고 할수 있다.

여기서 우리가 만든 겜과 타이틀 씬을 추가할수 있는데, 추가되는 순서에 따라서 번호가 정해진다.

위에서 애기한 Application.LoadLevel(1)의 의미는

1번 씬으로 가라는 이야기 이며, 타이틀은 0번 씬이 되겠다.

0번이 어딨어라고 말하면, 프로그래밍 고만해라.




7. 노말맵으로 보다 현실적으로


전에 만든 벽돌집들을 한번 봤는데, 너무 밋밋하지 한거 같다.

그래서 이번에는 그 밋밋한 벽돌집을 좀더 현실적으로 보여주기 위해서 노말맵을 이용해 보자.

전에 썻던 벽돌집 텍스쳐를 하나 복사하고 클릭하면 오른쪽 인스펙터에 텍스쳐 타잎이 있는데

그것을 노말맵으로 하면 무슨 노이즈같은게 보이는데 이것이 바로마법의 노말 맵이다.

 

이 노말맵 텍스쳐를 전에 사용한 메터리얼을 클릭하면 normal map이 있는데 여기에 

방금 만든 텍스쳐를 추가하면 오잉!! 아래와 같이 현실감 넘치는 집으로 변하게 된다!




8. 페이드인, 아웃 그리고 커서


게임에서 화면 전환을 할때 그냥 갑자기 팍!하고 화면이 바뀌는가?

아니다. 보통 페이드 아웃하고 아이드 인을 하게 된다.

즉, 흐려지다가 밝아지면서 화면 전환이 된다.


유니티에서도 이런건 아주 간단히 할수 있다.

gameObject에서 UI그리고 그 밑에 raw Image를 클릭하면 캔퍼스에 자동적으로 들어가있다.

이 이미지를 캔퍼스에 가득 채우고 검은색으로 바꾼다.

이러고 실행시키면 까만 화면만 보인다.

이 객체를 애니메이터로 알파값을 조정해서 밝게 한다.

그럼 객체 안에 이미 애니메이터 컴포넌트가 추가되어져 있다.


그리고 empty객체를 만들고 그 객체안에 다음과 같은 스크립트를 입력한다.


// Use this for initialization

void Start () {

        StartCoroutine(screenAnim());

    }

// Update is called once per frame

void Update () {

}


    IEnumerator screenAnim()

    {

        yield return new WaitForSeconds(1f);

        FadeScreen.GetComponent<Animator>().enabled = true;

        BlackScreen.SetActive(false);

        yield return new WaitForSeconds(0.99f);

        FadeScreen.GetComponent<Animator>().enabled = false;

        FadeScreen.SetActive(false);

    }



스크립트를 보면 알겠지만, 단순히 애니메이터를 키고 크는 역활만 한다.

물론 첨에는 애니메이터를 커놓은 상태이다.


그리고 FPS겜에서 가장 중요한 커서를 만드는 건데, 쩝 이건 뭐 간단하다.

그냥 방금전과 같은 raw image를 추가하고 그 이미지는 흰색으로 그리고 위치는 가운데로 해 놓으면,

바로 커서가 된다.

넘 단순해서 블로그에 쓰는것도 쪽팔리내 ㅠㅠ




9. 시작 퀘스트 만들기


퀘스트 만드는건 뭐 이제 일도 아니다.

숲도 만들었겠다 주인공이 첨에 나오면, 숲에서 시작되서 숲을 나오는 퀘스트를 생성해보자.

그냥 빈오브젝트를 넣고 그 오브젝트에 다음과 같은 스크립트는 넣는다.


public GameObject QuestUpdate;

public GameObject PlayerText;

public GameObject TextDisplay;


IEnumerator updateQuest()

    {

        transform.position = new Vector3(0, -1000, 0);

        QuestUpdate.GetComponent<Text>().text = "Active Quest: Exit the Wood";

        yield return new WaitForSeconds(3f);

        TextDisplay.SetActive(true);

        PlayerText.GetComponent<Text>().text = "Where am I?";

        yield return new WaitForSeconds(1f);

        PlayerText.GetComponent<Text>().text = "I need to find a way of this wood";

        yield return new WaitForSeconds(2f);

        TextDisplay.SetActive(false);

        gameObject.SetActive(false);

    }


퀘스트 업데이트는 퀘스트 텍스트이고, 플레이어 텍스트는 기본 대화 화면 텍스트다.

그리고 텍스트 디스플레이는 대화 화면에 창이다.


휴..뭐 이건뭐 길게 설명할것도 없다. 

코드보면 다 써있으니 쉽게 이해할수 있을거라고 생각한다.


그럼 숲에서 빠져나와서 마을에 도착하면 이 퀘스트는  끝나야 한다.

결국 어떤 특정 포인트에 도착하면 퀘스트 완료가 되야 한다는 뜻인데 이런식의 퀘스트는 많이 있을수 있을것이다.

그럼 어떻게 구현하면 되는가!


지금까지 우리가 공부한것을 응용하면 쉽게 할수 있을것이다.

우선 큐브를 하나 만들고, 그 큐브를 마을의 범위만큼 만든다음 캐릭터가 그 큐브안에 들어오면 

퀘스트를 끝내거나 새로운 퀘스트를 실행시키면 된다.


OnTriggerEnter를 이용하면 할수 있는데, 첨에 이렇게 했을때 캐릭터가 큐브에 들어가지도 않았는데

멋대로 실행되서 버그인가 했는데, 역시 계획없이 만들면 거지같은 코드가 나오게 되는거 같다.


OnTriggerEnter라는 함수는 그 객체에 어떤다른 객체라도 들어오면 실행되는 것인데,

현재 객체는 광범위하게 넓혀져 있기대문에 여러 객체들이 이미 들어와있는 상태인것이다.

하지만 개발을 하는 사람들은 마치 OnTriggerEnter에 충돌할수 있는건 캐릭터뿐이라는 착각에 빠져있기때문에

방금과 같은 버그가 계속 나왔던 것이다.


결국 캐릭터에게 태그를 부여하고 그 태그를 가지고 체그 하면 가능하지만, 이 이외에도 여러 문제가 발견되었다.

한번 들어간다음에 다시 들어가도 실행된다.

그래서 한번들어가면 그 객체를 삭제하려고 했지만, 슬립함수를 이용하기 때문에 삭제를 하면 또 문제가 생긴다.

결국 퀘스트를 따로 스크립트를 짜서 정교하게 하지 않으면 계속 문제가 생길것이다.


이래서 기획이 중요한것이다.

코딩은 누구나 할수 있는거... 

몸만있으면 벽돌을 달라서 벽을 쌓을수는 있지만,

집은 설계도 없이 제대로 지을수 없는 것이기 때문에.