본문 바로가기

유니티 개발 정보/개발 팁

유니티 개발자로서 내가 배웠던 최고의 5가지



* 해당 글은 원문은 에서 보실 수 있습니다.



유니티 개발자로서 내가 배웠던 최고의 5가지



유니티는 환상적인 게임 개발 플랫폼인 이유가 여러가지 있지만, 깔끔하고, 접근이 쉬운 컴포넌트 기반 플랫폼으로 디자인된 것이 그 이유 중 하나이다. 
예를 들어, 약간의 코드를 함께 놓는것은 매우 쉽고, 하루 또는 이틀만에 실행가능한 프로토 타입을 만들 수 있다.  
그러나 이런 다재다능함에도 불구하고, 나는 몇년동안 유니티로 특별하게 더 잘 할 수 있는 몇가지 사례들을 발견했다.


나의 새로운 게임 The Fall과 함께 나는 이들의 많은 사례들을 얻었고 그것을 조합했다. 그 결과 나는 빠르고 쉽게 그리고 상대적으로 버그가 더 적은, 매끄러운 개발 프로세스를 경험하였다. 나의 The Fall의 발표소식을 축하는 것을 거들기 위해 그리고 새로운 킥스타터 캠페인에 홍보하기 위해,나의 게임으로 당신을 도울지도 모르는 나의 흥미로운 디자인 사례들을 공유하고자 이 글을 작성하고 있다.

당신이 이 글을 통해 몇 가지 값진 것들을 얻었다면, 킥스타터에서 The Fall을 체크아웃 해달라!


1: 당신의 코드를 장기간의 회사 재산으로 생각해라.

좋은 프로그래머는 코드를 작성할 때, 단지 일하고 있는 프로젝트의 재산 뿐만아니라, 당신의 비즈니스의 재산으로 생각하라고 말한다. 예를 들어, The Fall에서는 두 플레이어의 아마타 간에 대화를 보여주는 다이얼로그 시스템을 가지고 있다.





이 다이얼로그 시스템은 완전히 독립적으로 그리고 유니티에서 약간의 설정만을 요구한다. 나는 조금의 작업만으로도 이 다이얼로그 시스템을 가질 수 있으며, 그것을 내가 만들고 하는 미래의 다음 게임에 적용할 수 있다. OnGUI함수의 간단한 조절로 대부분의 경우에 이론상으로 작동할 수 있는, 완벽하게 기능적인 다이얼로그 시스템을 재가공할 수 있다. 
말할 것도 없이, 이것은 많은 시간과 돈 그리고 증가된 작업속도와 창조적인 프로세스를 용이하게 해준다.

기억하라: 만약 코드를 작성하는데 시간을 투자해야 한다면, 반드시 당신의 비즈니스와 창조적인 프로세스에 가치를 추가해야한다.


2: 전역 클래스 인스턴스 변수를 사용해라.

이 디자인 패턴은 나의 컴포넌트를 독립적이고 깨끗하게 유지하는데 많은 도움을 줬다. 
여기 이 아이디어는 개별적 혹은 당신의 주된 스크립트에 대한 것으로, 인스펙터창에서 스크립트를 가지지 않더라도 어느 곳에서나 해당 인스턴스에 접근하기 위해서, 당신은 스크립트의 특정 인스턴스를 가리키는 전역 변수를 설정할 수 있다.

나의 메인 DialogueSystem.cs 스크립트 안에, 나는 맨 윗부분에 다음과 같이 쓰여진, 스크립트가 포함하고 있는 클래스와 똑같은 타입을 가지는 변수를 가진다.

public static DialogueSystem instance;

또는 자바스크립트에서

static var instance : DialogueSystem;
 
그리고, 스크립트의 Awake() 함수에서, 당신은 간단하게 다음과 같이 작성해라:

instance = this; 

당신의 씬에서 이 스크립트를 어떤 한 객체에 첨부하면, 유니티가 씬을 로드할 때, 저 특별한 스크립트 인스턴스는DialogueSystem.instance 변수에 자기 자신을 대입할 것이다. 왜 이것이 유용한 가? 만약 나의 게임 안에서, 어디에서나 나의 dialogue system에 접근하기를 원한다면, 나는 다음과 같이 입력을 하면 된다.

DialogueSystem.instance.SomePublicFunctionName();

그리고 유니티는 이 씬에서 내가 DialogueSystem의 특정 인스턴스에 있는 함수를 호출하기를 원한다는 것을 알 것이다.

중요 - 이 디자인 패턴은 당신이 이 주어진 씬 안에서 오직 하나의 스크립트 인스턴스만을 하는 것을 가지도록 하는데 목적이 있다.  그러므로 이것은 dialogue System같은 큰 컴포넌트에 효과적이며, 한 씬에 여러 번 인스턴스가 될 수 있는 객체에 사용하는 것은 쓸모가 없거나 문제가 될 수 있다고 생각된다. 만약 이 패턴의 더 안전한 구현에 흥미를 가지고 있다면, 구글에서 "Singleton"이라고 검색하면 된다. - 이 글에서의 구현은 이해를 돕기 위해서 매우 간단하게 되어 있다.


3: 콜백을 사용하라(delegates 혹은monobehaviour.SendMeesage())

만약 당신이 초심자라면, 이것은 약간 복잡할 수도 있다. 다음을 고려하라:

예를 들면, 우리는 플레이어가 걷거나, 멈추거나, NPC와 대화를 하고, 대화가 끝났을 때 플레이어의 제어를 반환하는 등의 NIS(non-interactive sequence, 비상호적인 사건)을 가지고 있다고 생각해보자. 이 글의 두 번째 주제에서 공부했기때문에, NIS 스크립트에서 다음과 같이 호출함으로서, Dialogue System을 간단하게 활성화 시킬 수 있다는 것을 알 수 있을 것이다.

DialogueSystem.instance.SomeFunctionToActivateDialogue() 

하지만 다이얼로그가  끝이 났을 때, NIS 스크립트가 플레이어의 제어를 반환하거나, 혹은 우리가 원하는 다른 작업을 하기 위해서, 우리는 다이얼로그가 끝이 났다는 사실을 어떻게 알 수 있을까? 우리는 callback을 사용할 수 있을 것이다. 우리가 DialogueSystem.instance.Whatever() 호출 할 때, 함수에게 호출이 끝이 났다는 것을 알리기 위해 약간의 매개변수를 전달할 수 있을 것이다. 이것을 하기 위한 몇가지 방법이 있다. 나는 C#의 delegate를 사용하는 것을 강력히 추천한다. 왜냐하면 delegate가 약간 더 깔끔하기 때문이지만, 만약 당신이 자바스크립트를 사용한다면 이는 대안이 아니다.

Delegates (C#)

delegates(델리게이트)를 함수를 가리키는 기본적인 변수로 생각해라. delegates를 사용함으로서, 함수 B가 완료되었을 때 함수 B가 함수 A를 호출하기 위해서, 당신은 함수 A를 함수 B의 매개변수로 전달할 수 있다. 나는 여기서 delegates를 생성하는 것에 대해 자세하게 다루지는 않을 것이다. 하지만 그것은 꽤 간단하며 다음의 링크에는 이것을 배우기에 적합한 장소이다.


MonoBehaviour.SendMessage(string)
유니티는 SendMessage 함수를 통해 약간의 콜백 기능을 제공하는데, 이는 함수의 이름을 나타내는 string을 통해 기본적으로 스크립트안에서 함수를 호출할 수 있게 해준다. 예를 들어, 우리는 "FooFunction"라는 함수를 가지는 "Foo"라는 이름의 스크립트를 가지고 있다고 하자. 다른 스크립트에서, 우리는 "Foo" 스크립트의 인스턴스를 나타내는 가리키는 변수가 필요한데, 우리는 그 변수를 "ourFooVariable"이라고 하겠다. 우리는 다음과 같은 호출할 것이다.

ourFooVariable.SendMessage("FooFunction");

우리는 이것을 콜백을 위해 사용할 것이다. 왜냐하면 우리는 스크립트의 인스턴스와 또 다른 함수의 이름 전달할 것이기 때문이다. 예를 들어, 자바스크립트에서:

Script A:
-----------
function Start(){ 
ScriptB.MyFunction(this, "MyCallback"); // 'this'는 스크립트 A 의 this 인스턴스를 의미한다.
}
function MyCallback(){
//This will get fired after script B is finished.
//여기는 스크립트 B가 끝났을 때 실행이 될 것이다.
}
------------
Script B:
------------
function MyFunction(callback : MonoBehaviour, cbName : String){ 
//do some stuff 
//..... 
//call back to script A: 
callback.SendMessage(cbName);
}
----------- 

전역 클래스 인스턴스 변수와 함께 콜백을 사용하는 것은 당신이 독립적인 컴포넌트를 만드는데 도움을 줄 것이다. The Fall에서, 플레이어가 NIS상태에 있고, 다음 다이얼로그 시스템이 발동되게끔 해야 할 때, NIS는 간단하게 dialogue 시스템을 호출할 것이고, 그곳에 콜백을 전달할 것이다. 그 다음 dialogue system이 작동할 때는 아무 것도 발생하지 않을 것이다. dialogue system이 끝이 났을 때 dialogue system은 원래의 NIS에 있던 콜백함수를  실행할 것이고, 다시 NIS의 일을 수행할 것이다.
서로 연결되어야 하는 것은 아무 것도 없다. 인스펙터창에서 이러한 System을 훌륭히 작동시키기 위해서 어떠한 일을 할 필요가 없다. 어떤 NIS 스크립트도 dialogue system의 코드를 가지고 그것을 작동시키기 위해서, dialogue system과 어떠한 관계도 맺지 않는다. 그것은 단지 작동한다.


4. 스스로 스크립트를 켜고 끌 수 있게 하라

다시 우리의 dialogue system 예제를 보자. The Fall에서 dialogue system이 활성화 되었을 때, 계속해서 GUI를 그린다. 나는 스크립트에서 그것이 반드시 dialogue를 보여줘야 되는지 아닌지를 확인하지 않는다. 그것은 간단하게 항상 실행되기 때문이다. 왜냐하면 dialogue system은 그들 스스로 활성화 또는 비활성화를 할 수 있기 때문이다. 이는 위의 #2, #3 단락의 이유때문이다.

나의 dialogue system에서 dialogue를 활성화 시킬 때, dialogue system이 스스로 활성화 시키기 위해서 다음의 코드가 첫 부분에 위치한다.

this.enabled = true;

dialogue가 완료되었을 때 callback이 보내지고, 코드의 마지막 부분에 스스로 비활성화를 시킨다.

this.enabled = false;

이래저래 dialogue system은 리소스라고 불릴만한 것이 많이 없으며, 다시 필요로 할 때 까지 가만히 대기하고 있을 것이다.


5. 모든 사항에 대해서 Update를 사용하지 않도록 노력해라 - 코루틴을 사용하도록 노력해라.


유니티 개발에서 가장 흔한 디자인 패턴은 해당 코드가 정말 가끔 필요로 할 때, 함수안에 간단한 코드를 매프레임마다 실행시키는 것이다. 예를 들어 there's a popular Fade-In-and-Out solution에서는 매 프레임마다 스크린이 어두워지고 있는지 확인하고 숫자를 0에서 1로 움직이고 있다. 이것은 대부분의 시간은 그것을 고려할 필요가 없음에도 불구하고, 항상 매프레임마다 실행된다.

스크립트를 오랜 기간 동안 멈추게 하고, 그리고 그것을 서서히 명령을 받아서 실행을 시키야 하는 많은 경우가 있다. 나는 코루틴이 이러한 상황을 발생시키는데 최고의 방법임을 발견했다.

기본적으로, 코루틴으로 유니티가 몇 프레임 동안 어떤 작업을 수행하고, 그 작업이 끝나면 나가게끔 할 수 있다. 항상 update하는 fade-in-and-out 스크립트를 가지는 대신에, 완료가 되었을 때 간단하게 멈추는 face in or out을 하는 코루틴을 만들 수 있다. 그래서 Update는 계속해서 실행하지 않을 것이다.

자바스크립트에서, 간단한 코루틴은 다음과 같이 보일 것이다:

var fadeAlpha : float = 0;
function FadeIn(seconds : float){ 
while(fadeAlpha != 1) 
    fadeAlpha = Mathf.MoveTowards(fadeAlpha, 1, Time.deltaTime*(1/seconds)); 
    yield; 
}
}

이 함수가 실행되면, 그것은 fadeAlpha의 값을 1로 몇 초동안 변경할 것이다. 당신은 위에 있는 링크 안에 있는 OnGUI 함수안의 스크립트와 유사한 방식으로 fadeAlpha 값을 사용할 수 있을 것이다.


모든 것을 다함께 적용하자

이 모든 아이디어를 사용하여, FadeInOut 스크립트를  생각해보자. 우리는 FadeInOut 스크립트를 만들고, 씬에 오직 하나의 인스턴스만을 둔다음 그것을 disable 한다. 그 스크립트가 instance라고 정적 변수를 가지는지 확인해라. 그리고 그 스크립트의 Awake()함수가 실행할 때, 그것은 instance 변수를 자기자신으로 설정할 것이다.

다음, 우리는 위와 유사하게 스크립트안에 약간의 함수를 만든다. FadeIn()의 앞 부분에는 스크립트를 enable하고 FadeOut의 마지막 부분에는 스크립트를 disable한다.

우리가 원한다면, 우리는 콜백 시스템을 구현할 수 있다. 그러나 이 스크립트는 아마 매우 쉽게 그것을 보증할 것 이다.

그 다음, 우리가 게임의 스크린이 fade out되기를 원한다면, 우리는 FadeInOut,instance.FadeOut(1)을 호출하고 게임은 몇초동안 fade out 될 것이다.
우리가 다시 씬이 fade in 되기를 원하면 우리는 FadeInOut.instance.FadeIn(1)을 호출하고, 게임은 1초동안 다시 fade in 될 것이다.

이리하여, 우리는 간단한 FadeInOut 스크립트를 만들었다. 그것은 완벽하게 독립적으로 작동하고, 자원을 낭비하지 않으며, 어떤 유니티 게임의 어떤 씬안에 들어갈 수 있고, 어떠한 인스펙터창의 연결과 설정없이도 작동할 것이다.


결론

모듈을 만드는 것은 단지 조직을 위해서 좋을 뿐만아니라, 시간이 흐를수록 당신을 더욱 값지게 만들 것이다. 이 프로세스와 비슷하게 동작하는 다른 많은 디자인 패턴이 있을 것이다. 
나는 이 글로 약간의 도움이 되었으면하고 나의 설명이 너무 혼란스럽지 않았길 바란다.