0. 개요 혹은 잡설...
부족하지만 강좌를 하다 보니 4회군요.
이번에는 코루틴 쪽 기본개념을 간단히 알아봐야겠네요.
현실세계에서 인간이 무슨일을하다가 잠시 중단하고 다른일을하고 다시돌아와 하던일을 계속하는건 아주 자연스러운일입니다.
이것을 프로그램으로 대입해서 생각하면 하나 이상의 함수 호출 엔트리포인트와 exit포인트를 가질수있다는 것입니다.
이부분은 사실 goto문을 남발하는 코딩과 별차이가 없다라고 생각할수도있습니다.
Knuth 와 Dijkstra는 goto문을 두고 설전을 벌이신것으로 유명하신 분들입니다.
Dijkstra가 많은 업적을 남기셧지만 그중에 '갑'은 goto문을 쓰면 해롭다는 종교적인 신념을 만들어낸 것 입니다.
(goto문이 선악의 기준이 되다니 얼마나 대단합니까?)
반면 Knuth는 goto문은 해로운것이 아니며 꼭 써야하는 경우도 있다며 반기를 드셨죠.
Knuth 가 하신말씀중에 '서브루틴은 코루틴의 특수한 형태이다.' 라는 말씀이 있습니다.
1. 게임 로직의 핵심 상태 제어엔 FSM
게임프로그램에서 큰특징중 하나가 실시간 처리입니다. 다른 분야 프로그래머들이 게임 프로그래머로 전향을 할때 가장 어려운점이 어떻게 동시에 여러개의 움직임을 제어 할것인가 라는 문제입니다.
대부분 그런 질문은 'NPC AI를 어떻게 해요?' 제목으로 글을 많이 남깁니다.
글을 올린 분의 사정을 자세히 들어보면 AI관련 내용이라기보단 이런 내용입니다.
'간단한 알피쥐게임을 만드는데요. 마을에 NPC들이 돌아다니는걸 어떻게 해야할지 모르겠어요?
씨로 다음과 같이 했거든요.'
게임오브잭트::생활하기() {
상점가기();
담배사기();
집으로가기();
}
게임루프() {
while( 게임종료되었나() ) {
전사.생활하기();
법사.생활하기();
}
}
'근데 한캐릭터가 생활을 하는동안 화면에 아무것도 움직이지않고 키보드 입력조차 받을수없는데 이것을 어떻게 해야죠?'
이러면 여러가지 답글이 달리게됩니다. 대부분 많은 분들이 FSM을 언급하시면서 대충 정리하자면 이런식으로 해라는 답변글이 올라오게 됩니다.
게임오브잭트::생활하기() {
switch(단계)
{
case 1:
상점가기();
단계++;
case 2:
담배사기();
단계++;
case 3:
집으로가기();
단계 = 1;
break;
}
처음에 나온 로직이 아래와 같이 처리가 되었다면...
전사:상점가기() -> 전사:담배사기() -> 전사:집으로가기 -> 법사:상점가기() -> 법사:담배사기() -> 법사:집으로가기()
전사:상점가기() -> 법사:상점가기() -> 전사:담배사기()-> 법사:담배사기() -> 전사:집으로가기 -> 법사:집으로가기()
게임루프상에서는 전사와 법사의 일을 번갈아나가며 처리를 하게됩니다. 두 오브잭트는 마치 동시간대에 존재하는것처럼 보이게됩니다.
머 사실 이것으로 충분하기는 합니다.
굳이 코루틴같은걸 굳이 써야하는건 왜일까요?
프로메테우스 영화에서 보면 인간이 있는데 왜 인간이 존재하는데 인간하고 똑같은 안드로이드를 굳이 왜 만든건지 물어보는 장면이 나오죠.
답은 '할 수 있으니깐!'(물론 하고싶다고 다하고 살면 결과가 좋지 않다라는 교훈을 주기위한것같긴 합니다만....)
2. 양보하기 있긔 없긔?
일단 유니티 예제를 보도록 하시죠.
coroutin1.js 를 만들고 내용을 다음과 같이 코딩해줍니다.
#pragma strict
function Start () {
Debug.Log( gameObject.GetInstanceID() + ": step1 ");
Debug.Log( gameObject.GetInstanceID() + ": step2 ");
Debug.Log( gameObject.GetInstanceID() + ": step3 ");
}
function Update () {
}
참고로...
소스에서 나오는 gameObject.GetInstanceID()는 현재 게임오브잭트의 고유한 아이디값을 가져옵니다.
그리고 wiz, wory 두개의 게임오브잭트를 만들고 방금 코딩한 스크립트를 연결시켜줍니다.
연결 시켜줍니다.
콘솔창에서 결과를 확인합니다.
일반적인 서브루틴이 호출이 만들어내는 당연한 결과가 출력이됩니다.
그럼 소스내용을 고쳐 봅시다.
step1 과 step2 사이에 yield를 추가해줍니다.
유니티에서 yield문은 말그대로 양보를 해줍니다. 어떤 서브 루틴을 실행을 하다가 yeild문을 만나면 서브루틴실행을 잠시 보류하고 서브루틴을 빠져 나옵니다. 그리고 이 서브루틴을 호출했던 부모서브루틴의 다음 라인을 실행합니다. 그리고 게임루프상에서 다음 턴이 오면 보류했던 위치 부터 실행을 재계합니다.
빨간색 선은 첫번째턴, 파란색은 두번째턴
호출한 함수(Start)보다 호출받은 함수(DoLiving)가 나중에 종료된다.
3. 코루틴 과 yield
코루틴은 단일쓰레드지만 마치 멀티쓰레드처럼 동작을합니다. 그러나 바늘과실(쓰레드의 어원))은 하나 이므로 이점은 유의를 하셔야합니다.
세마포어가 여러개의 바늘과 실을 일렬로 늘어놓아 마치 하나의 실과 바늘처럼 쓰도록 도와주는 것이라면 코루틴은 하나의 실과 바늘을 어지럽게 꼬아서 여러개의 바늘 과 실처럼 보이게 하는것입니다.
멀티쓰레드는 크게 두가지로 나누어지는데요. 선점형과 비선점형으로 나누어집니다.
비선점형의 특징이 프로그래머가 콘텍스트 스위칭 위치를 직접 정해줄때만 스위칭이 일어날 수 있다는 특징이 있습니다.
근데 바로 코루틴이라는게 비선점형 멀티쓰레드와 유사하고 차이점은 단일쓰레드라는 것입니다.
4. 결론
비선점형 멀티쓰레드의 단점중에 하나가 버그로인한 무한루프나 io작업오류등으로 블럭이 되면 그상태를 빠져나올수없다는 단점이 있습니다.
유니티는 그래서 아애 코루틴내의 루푸문에서 yield가 없으면 컴파일 에러가 뜨더군요. 아주 아주 실용적인 부분같습니다. 이런게 바로 유니티의 숨겨진 매력인듯합니다.
이번회에는 주로 yield를 예를 들어 설명을 했는데요
다음회에는 StartCoroutine함수로 코루틴을 사용한 예제와 같이 더 설명을 하겠습니다.
참고자료
http://unity3d.com/support/documentation/ScriptReference/index.Coroutines_26_Yield.html
요건 씨샵~
http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx
html5
http://stackoverflow.com/questions/2297286/javascript-check-yield-support
'프로그래밍' 카테고리의 다른 글
[네트워크 게임 튜터리얼 번외편] C/C++로 Socket.IO에게 돌직구를 던져보았다. (4) | 2012.06.21 |
---|---|
PC에서 3D 입체 영상 게임 개발하기 #3 (3) | 2012.06.21 |
데이터타입을 가지고 있는 메시지ID (6) | 2012.06.18 |
std::function이 좋네. (12) | 2012.06.17 |
유니티는 C#으로 작성했다? (9) | 2012.06.17 |