∇ 스크립트 강좌

 ▼ 스크립트의 활용도

N/A

소스無


롤플레잉 게임에는 수많은 이벤트와 대화가 있다. 그런데 이 이벤트와 대화들은 상황에 따라서 항상 변화될 수 있는 것들이다. 이벤트의 경우에는 특정 조건이 만족 했을 때만 발생할 수 있도록 만들어야 하고, 대화의 경우에도 특정 조건에서는 다른 대화를 할 수 있어야 한다. 따라서 이런 것들을 보다 쉽게 처리하기 위해서 스크립트라고 불리는 것을 작성하고, 실행 시간에 직접 해석해서 결과를 나타낸다.

스크립트는 마치 고급 언어와 같이 깔끔한 문법과 예약어를 갖춘 것으로 만들 수도 있고 단순한 텍스트의 배열처럼 만들 수도 있는데 이러한 결정은 게임 제작 기획 초기에 게임의 스크립트 수준을 정해두면 된다. 예를 들어서 연애 시뮬레이션 장르를 구상한다면 스크립트가 50% 이상의 작업이 될 것이기 때문에 고급 언어와도 같은 복잡한 스크립트 언어를 만들고 그것을 해석하는 코드를 제작할 수 있다. 하지만 대화의 비중이 그리 높지 않은 액션 게임을 만든다면 단순한 텍스트의 나열만으로도 충분히 그 기능을 할 수 있다.

1. 롤플레잉 게임의 스크립트

    롤플레잉 게임은 NPC들과의 대화가 필수 조건이라고 할만큼 많은 대화량이 필요하다. 물론 일본식 롤플레잉 게임의 대화처럼 한 사람이 하나의 정보를 제공하기 위한 기능을 하기 위해 똑같은 말만 되풀이 하도록 만들었다면 그렇게 많은 대화량은 필요 없을지도 모른다. 아마 아래와 같은 구조 정도만 되면 될 것이다.


    01:  #이름: "우민1"
    02:  #초기좌표: (10,10)
    03:  #생김새: 10
    04:  #대화: "마을 남쪽의 동굴에 용이 살고 있다는 전설이 있어"
    05:  
    06:  #이름: "우민2"
    07:  #초기좌표: (12,10)
    08:  #생김새: 2
    09:  #대화: "옆 친구의 말은 믿으면 안돼"
    10:  
    11:  … …

     

    사실 이 정도의 정보만 있으면 지도 상에 고정되어 있는 어떤 사람의 대화를 나타낼 수 있다. “우민1”이라는 이름을 가진 10번 모양의 사람이 (10,10)에 위치하고 있으며 그에게 말을 걸게 되면 “마을 남쪽의 동굴에 용이 살고 있다는 전설이 있어”라는 말을 해준다는 의미이다. 만약 게임에서 더 추가하고 싶은 캐릭터 속성이 있다면 모두 여기에 추가로 기입하면 되고, 스크립트에 기입되지 않은 것은 디폴트 속성으로 주면 된다.

2. 연애 시뮬레이션 게임의 스크립트

    실제로 스크립트의 효과를 가장 많이 보는 것은 연애 시뮬레이션이라는 장르이다. 특히 일본 쪽에서 속칭 미소녀 게임이라고 불리는 게임들은, 회사가 다르더라도 같은 스크립트 엔진을 쓰고 있는 경우가 많다. 이들의 스크립트 언어는 거의 C 언어에 필적할 정도의 언어 체계를 가지고 있으며 게임에 적합하게 특화 되어 있는 언어로 구성된다. 아래에 든 예는 연애 시뮬레이션의 스크립트의 일부이다.


    01:  def proc9: {
    02:    [1011] := 1;
    03:    c19(1);
    04:    T := S;
    05:    select({
    06:      "취소",
    07:      "불러오기",
    08:      "저장하기",
    09:      if (T != 0): "게임을 종료합니다.";
    10:    });
    11:    [1011] := 0;
    12:    if (S = 2) {
    13:      select({
    14:        "취소",
    15:        "불러오기1",
    16:       "불러오기2",
    17:        "불러오기3",
    18:        "불러오기4",
    19:        "불러오기5";
    20:      });
    21:      if (S > 1) {
    22:        flagio(0, S - 2);
    23:      };
    24:    }
    25:    elseif (S = 3) {
    26:      select({
    27:        "취소",
    28:        "저장하기1",
    29:        "저장하기2",
    30:        "저장하기3",
    31:        "저장하기4",
    32:        "저장하기5",
    33:      });
    34:      if (S > 1) {
    35:        [21] := 1;
    36:        flagio(1, S - 2);
    37:        proc10;
    38:        "저장했습니다.";
    39:        wait;
    40:        [21] := 0;
    41:      };
    42:    }
    43:    elseif (S = 4) {
    44:      select({
    45:        "취소",
    46:        "종료한다";
    47:      });
    48:      if (S = 2) {
    49:        runmes("si_os.mes");
    50:      };
    51:    };
    52:  };
     

    1. <01>의 ‘def proc9:’은 함수를 정의하는 방법이다.
    2. 각 단락의 시작은 C 언어와 마찬가지고 ‘{‘와 ‘}’를 사용한다.
    3. 변수(아마도 이벤트 플래그인듯)는 ‘[‘와 ‘]’ 사이에 숫자로 구분한다.
    4. 대입문은 파스칼처럼 ‘:=’를 사용한다.
    5. <05>의 select문을 만나게 되면 거기서 제시한 스트링을 바탕으로 메뉴가 구성된다.
        그리고 선택한 아이템의 번호는 S라는 변수에 자동으로 들어 온다. (첫 메뉴가 1)
    6. <49>의 runmes() 명령어는 다른 스크립트를 호출하는 명령이다.

3. '데자뷰'의 스크립트 

     데자뷰의 스크립트는 어떤 의미에서는 본격적인 롤플레잉의 스크립트라고 할 수 있다. 하나의 스크립트는 하나의 맵에 대한 정보를 담고 있으며, 맵에 대한 정보, 이벤트에 대한 정보, NPC의 위치 및 대화 정보를 가지고 있다.

    먼저 맵에 대한 정보를 보자.


    01:  MAP HEADER DEFINITION
    02:  
    03:     map name : Abellis
    04:     map x max : 124
    05:     map y max : 130
    06:     friendship level : 0
    07:     music data file : Town1
    08:     tile data file : tile
    09:     object data file : objects
    10:     equipment data file : equips
    11:     field data file : fields
    12:     character data file : charas
    13:     exit point : (0,0)
    14:     start point : (91,79)
    15:     boundary exit : false
    16:     handicap : none
    17:     map recording : true
    18:     person recording : true
    19:     map code : T1
    20:     if exit point : (0,0) then
    21:        map name : World1
    22:        start point : (31,70)
    23:     end if
    24:  
    25:  END DEFINITION
     

    이 부분은 스크립트의 가장 처음에 위치하는 것이며 맵에 대한 정보를 text로 주고 있다. 이렇게 함으로서 맵 데이터는 단순한 지형 정보만을 제공하며 실제적인 속성은 모두 스크립트가 가지게 되는 것이다. <20-23>에서 보면 이 맵에서 밖으로 나갈 때는 어느 맵의 어떤 위치인지까지 알려주고 있다.


    01:  BEGIN EVENT SCRIPT
    02:     if event map is(-1,75) then
    03:        if not flag is set(102) then
    04:           if system time is(7,18..19) then
    05:              set color(15)
    06:              talk from(6,"[your name+C] 잠깐만 나하고 이야기 좀 하자.")
    07:              press any key; set flag(101)
    08:           end if
    09:        else
    10:           set event time(1,0,0,2,0)
    11:           write map(3,103,75,0); write map(3,104,75,0)
    12:        end if
    13:     end if
    14:  
    15:  END EVENT SCRIPT
     

    이것은 이벤트에 대한 정보를 담고있는 스크립트이다. ‘if’, ‘else’, ‘end if’등의 익숙한 예약어가 보일 것이다. 이벤트가 일어 나는 것이 조건을 만족해야 발생하는 것이 대부분이므로 이런 식으로 if문이 많이 쓰이게 된다. 위의 상황대로라면, 이벤트가 일어난 위치의 y값이 75이고, 아직 그 이벤트가 안 일어 났으며, 현재 시간이 7시나 18, 19시일 때는 누군가가 나에게 말을 거는 이벤트가 발생한다. 만약 위치는 맞지만 그 이벤트를 봤다면 else문이 있는 곳으로 분기하여서 타임 이벤트 1번을 생성한다.

    참고로 <06>의 문자열에 있는 ‘[your name+C]’은 주인공의 이름에 “아” 또는 ‘야”를 붙여 문자열을 완성하라는 의미이다. 예를 들어, 주인공의 이름이 “몽룡”이라면 “몽룡아”라는 문자열로 대치할 것이고, “방자”라면 “방자야”라는 문자열로 대치된다.


    01:  BEGIN TIME EVENT SCRIPT
    02:     if event time is(1) then
    03:        register message(0,"언제나 정겨운 우리집..",2)
    04:     end if
    05:  
    06:  END TIME EVENT SCRIPT
     

    이 코드는 타임 이벤트에 대한 스크립트이다. 만약 타임 이벤트 1번이 일어 났다면 위와 같은 혼잣말을 하게 하는 내용이다. 타임 이벤트 1번이라면 좀 전의 코드에서 생성시켰었다.


    01:  :Gamen
    02:  MOVEMENT DEFINITION
    03:     name<가멘>
    04:     face<7>
    05:     ability<2000,1000,5000,3000,5000,3000,1000,1000,50,1000>
    06:     skill<10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10>
    07:  
    08:     time<21..8>
    09:        (73,89)
    10:     time<8..19>
    11:        (81,89)-(81,94)-(80,94)-faced(down)
    12:     time<19..21>
    13:        (81,94)-(81,89)-(79,89)-faced(up)
    14:  
    15:  END DEFINITION
    16:  
    17:  add talk(1); add talk(13); add talk(36)
    18:  BEGIN SCRIPT
    19:     if you talked(1) then
    20:        if system time is(8..18) then
    21:           talk("살 물건이 있으면 또 오너라.")
    22:        else
    23:           talk("오전 8시부터 저녁 7시 사이에 이 가게에 오면")
    24:           talk("여러 물건들을 살 수 있을 거야.")
    25:        end if
    26:     end if
    27:     if you talked(13) then
    28:        talk("어서 오너라, [your name+C].")
    29:        remove talk(13)
    30:     end if
    31:     if you talked(36) then
    32:        remove talk(13)
    33:        if system time is(8..18) then
    34:           execute special script(12)
    35:        else
    36:           talk("미안하다, 이 가게는 지금 영업을 안 한단다.")
    37:        end if
    38:     end if
    39:  
    40:  END SCRIPT 
     

    이벤트 스크립트 부분이 끝나게 되면 그 다음부터는 NPC에 대한 정의가 들어 간다. 이 마을에 대한 스크립트만 2400줄 정도가 되기 때문에 모든 것을 다 설명할 수는 없고 그 중에 제일 짧은 NPC 스크립트 예제를 든 것이다.

    먼저 <01>은 이 NPC에 대한 레이블이다. 레이블 형식으로 한 것은 대화 이벤트가 일어 났을 때 최대한 이쪽 스크립트로 점프할 수 있도록 하기 위해서 인데 스크립트를 불러올 때 각 레이블에 대한 점프 포인터를 테이블로 가지고 있어야 한다. <02-17>은 이 캐릭터에 대한 이름과 모양과 속성을 설정하는 단계이다. 특히 ‘time’이라는 예약어로 만들진 부분은 이 사람이 시간대 별로 어떤 동작을 해서 어떤 위치로 가게 되는지를 지정한다. 이때, 각각의 좌표는 스크립트 로딩 시에 해석되어서 테이블 형태로 보관한다. <18-40>의 부분은 대화 이벤트가 일어 났을 때 실행되는 부분인데, 대사별, 시간별 상황에 따라 각각 다른 말을 하고 있는 것을 볼 수 있다. 특히 <34>의 ‘execute special script(12)’는 상점의 물건을 사기 위한 특수 스크립트를 호출하는 것인데, 스크립트만으로 묘사하기 어려운 기능에 대해서는 이러한 방식으로 코드 상에서 정의된 특정 부분을 호출하는 방법으로 해결한다.

4. '그녀의 기사단'의 스크립트 

     ‘그녀의 기사단’의 스크립트는 ‘데자뷰’에 사용된 스크립트를 기반으로 해서 이 게임에 맞게 새로 디자인된 것이다. 이것 역시 전형적인 롤플레잉 게임의 스크립트라고 할 수 있다. 이 게임의 스크립트 규모를 보자면, 21줄짜리 프롤로그 스크립트부터 9790줄짜리 멜로암 도시 스크립트까지 총 225개의 스크립트 파일로 구성되어 있다. 각 도시는 보통 3000-5000줄 가량의 스크립트로 되어 있다.


    01:  Normal Event Begin
    02:  // 일반적인 이벤트를 처리하는 부분이다.
    03:     if flag is set(113)
    04:        if not flag is set(138)
    05:           if in a room()
    06:              transparent person([get number("레토2")],0)
    07:              warp person([get number("레토2")],0,0)
    08:              transparent person([get number("리렐2")],0)
    09:              warp person([get number("리렐2")],0,0)
    10:              warp person([get number("리렐1")],89,64)
    11:              set flag(138)
    12:              write map(6,38,73,4)
    13:              write map(6,39,73,4)         
    14:  
    15:     if event map is(83,21)
    16:        if flag is set(104)
    17:           run script("event001")
    18:  
    19:     if event map is(84,21)
    20:        clear monologue(1)
    21:        if in a room()
    22:           add monologue(1,"다시 밖으로 나갈까?",255,255,255)
    23:        else
    24:           add monologue(1,"여기는 멜-로암 대학 본부야.",255,255,255)
    25:           add monologue(1,"베오딜 총장님이 계시는 곳이지.", 255,255,255)
    26:           add monologue(1,"총장님께 가서 상담이나 해볼까?", 255,255,255)
    27:        set monologue(1,0,1,0)
    28:  
    29:     << 중략 >>
    30:  
    31:  Normal Event End
     

    이 스크립트는 일반 이벤트에 대한 스크립트이다. 데자뷰 때의 문법과 크게 바뀌지는 않았고 큰 차이점이라면 들여쓰기를 통해 단락을 구분한다는 점이다.


    01:  Time Event Begin
    02:  // 시간 관련 이벤트를 처리하는 부분이다.
    03:     if event number is(10)
    04:        clear monologue(1)
    05:        add monologue(1,"우웅.. 심심해..",255,255,255)
    06:        add monologue(1,"리렐도 데이트하느라 놀아주지도 않고..",
                              255,255,255)
    07:        add monologue(1,"그냥 카미암관으로 돌아갈까?",255,255,255)
    08:        set monologue(1,0,2,0)
    09:  
    10:     if event number is(11)
    11:        if not flag is set(113)
    12:           if not flag is set(148)
    13:              clear monologue(1)
    14:              add monologue(1,"그나저나.. 오늘 리렐과 놀긴 틀렸나봐..",
                                    255,255,255)
    15:              add monologue(1,"이따가 데이트하면 꼽사리 낄까~~?",
                                    255,255,255)
    16:              set monologue(1,0,2,0)
    17:        else
    18:           clear monologue(1)
    19:           add monologue(1,"우웅.. 심심해..",255,255,255)
    20:           add monologue(1,"리렐도 데이트하느라 놀아주지도 않고..",
                                 255,255,255)
    21:           add monologue(1,"그냥 카미암관으로 돌아갈까?", 255,255,255)
    22:           set monologue(1,0,2,0)
    23:  
    24:     << 중략 >>
    25:  
    26:  Time Event End
     

    이 부분은 타임 이벤트에 대한 것을 처리하는 스크립트이다. 타임 이벤트가 설정되면 게임 시스템은 그것을 기억하고 있다가 이벤트 발생 시점이 되면 자동으로 이벤트 번호와 함께 이쪽의 스크립트를 실행한다. 보시는 바와 같이 타임 이벤트에서는 “if event number is(10)”과 같이 이벤트 번호로 타임 이벤트의 종류를 구분한다.

     


    01:     Name <로단2>
    02:     Fixed
    03:     Ani <12>
    04:     Face <18>
    05:     Pitch <0>
    06:     Discription < 로단씨로군..
    07:                  뭐지 저 분위기는.. >
    08:     Time <0..23>
    09:        (120,120)-faced(right)
    10:  
    11:  Scription Ready
    12:     add talk(2,"안녕하세요?")
    13:  Scription Begin
    14:     if you talked(1)
    15:        remove all talk()
    16:        talk("......")
    17:  
    18:     if you talked(2)
    19:        add var(품위,-1)
    20:        remove all talk()
    21:  
    22:        if not flag is set(103)
    23:           talk("안녕하세요? 공주님..")
    24:           talk("또 만났군요..")
    25:           talk("지금은 조금 바빠서..")
    26:           talk("다음에 다시 즐겁게 이야기 나눠요~")
    27:  
    28:        else
    29:           talk("말씀드렸죠.. 이전 관계 다 청산한다고..")
    30:           talk("이건 그 동안 관계를..")
    31:           add talk(4,"로단씨..")
    32:  
    33:     if you talked(4)
    34:        add var(품위,-1)
    35:        remove all talk()
    36:        talk("지금은 분위기가 좀..")
    37:        talk("내일 찾아뵙지요..")
    38:        add talk(1,"그럼..")
    39:  
    40:  Scription End
     

    이것은 일반 NPC용 스크립트이다.
    구조 자체는 아주 쉽게 때문에 각 라인 별 설명은 생략한다.