[前へ] [目次へ] [次へ]

伝言イベントを作ってみる

今回は大抵のゲームに存在するお使いイベント・・・のさらに簡略版、伝言イベントを作ってみます。


●伝言イベントの概要
   イベントキャラクターAとBの伝言役をさせられます。
   イベントキャラクターA→B→Aの順番で話すことで完了とし、
   進行に合わせて違うメッセージを表示します。
   
   準備としてイベントキャラクターAとBになるイベントを二つ、作成しておきます。


   グラフィックを設定し、イベント名を「伝言A」にしておきます。


   同じ要領で「伝言B」も作成します。



●有限状態機械
   このイベントは「どこまで伝言が進んでいるか」という進行状態を管理する必要があります。
   即ち、「未開始」→「Aから伝言を受け取った」→「Bに伝えて返信を受け取った」→「Aに返信を伝えた」という
   4段階の進行状態があるわけです。(このような考え方を「有限状態機械」と呼びます)
   このイベントは単純なので順番に進む以外の展開はありませんが、
   複雑なイベントでは途中に分岐が挟まったり巻き戻ったりと様々な変化があります。
   
   「有限状態機械」は真面目な解説を読むとなんだかものすごいもののように見えるかもしれませんが、
   本質的には「〜をした状態で〜する」とか「〜が発生した状態で〜する」といったことを表現しており、
   ゲームのイベント進行はそのまま「有限状態機械」で対応できるものです。
   
   この「有限状態機械」はプログラム設計でもそのまま応用できる汎用性があり、
   ゲームを作るなら習得しておいて損はないでしょう。
   
   
   プログラム上で「有限状態機械」を扱うなら少なくとも、
   「現在の状態を記憶」し、「現在の状態に応じて動作を変更」し、「結果として現在の状態を変更」できる必要があります。
   「現在の状態を記憶」は 第8回 で扱った「ゲーム変数」で対応できます。
   「現在の状態に応じて動作を変更」は 第9回 で扱った「条件分岐」で対応できます。
   「結果として現在の状態を変更」も「ゲーム変数」を上書きすることで対応できます。


●具体的に詰めていく
   このイベントを完成させるために、具体的に内容を決めていきます。
   必要なのは「何をしたら状態が切り替わるか」と「各状態の時に関係するイベントはどうするか」です。
   今回、状態の数は前述の通り4個、関わるイベントは2キャラで、それぞれAとBとします。
   
   イベント中の各キャラクターについて、今回はこんな感じになるでしょう。
   ●「未開始」状態(状態ID0、初期状態)
      ・遷移
         Aと話す→「Aから伝言を受け取った」
      ・関係者の動作
         A:話すと伝言を託され、状態を「Aから伝言を受け取った」に変更する
         B:挨拶をする
   
   ●「Aから伝言を受け取った」状態(状態ID1)
      ・遷移
         Bと話す→「Bに伝えて返信を受け取った」
      ・関係者の動作
         A:伝言を早く伝えるよう催促される
         B:話すと伝言を伝えて、返信を託される。状態を「Bに伝えて返信を受け取った」に変更する
   
   ●「Bに伝えて返信を受け取った」状態(状態ID2)
      ・遷移
         Aと話す→「Aに返信を伝えた」
      ・関係者の動作
         A:話すと伝言を伝えて、お礼を言われる。状態を「Aに返信を伝えた」に変更する
         B:伝言を早く伝えるよう催促される
   
   ●「Aに返信を伝えた」状態(状態ID3)
      ・遷移
         なし
      ・関係者の動作
         A:お礼を言われる
         B:お礼を言われる
   
   後はこれを実際に記述していくだけです。
   まず、現在のイベント状態を保存するためのゲーム変数を作成します。
   今回は同じマップ内の2キャラが関わるので、「マップ」分類のゲーム変数を使います。

   ●イベントキャラクターAの内容
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
< 27>
< 28>
< 29>
< 30>
< 31>
< 32>
< 33>
< 34>
< 35>
< 36>
< 37>
< 38>

//ゲーム変数[マップ整数]の取得 (ゲーム変数名,デフォルト値)
Integer event_state=GetMapInt('伝言イベント進行状態',0);

if(event_state==0){//未開始
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"イベントBに伝言を頼むよ。");
   
   
//ゲーム変数[マップ整数]の設定 (ゲーム変数名,設定値)
   
SetMapInt('伝言イベント進行状態',1);
   
}
elif(event_state==1){//Aから伝言を受け取った
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"早くBに伝えて欲しいんだよ。");
   
}
elif(event_state==2){//Bに伝えて返信を受け取った
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"伝言を受け取ったよ。ありがとう。");
   
   
//ゲーム変数[マップ整数]の設定 (ゲーム変数名,設定値)
   
SetMapInt('伝言イベント進行状態',3);
   
}
elif(event_state==3){//Aに返信を伝えた
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"伝言してくれてありがとう。");
   
   
   ●イベントキャラクターBの内容
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
< 27>
< 28>
< 29>
< 30>
< 31>
< 32>
< 33>
< 34>
< 35>
< 36>

//ゲーム変数[マップ整数]の取得 (ゲーム変数名,デフォルト値)
Integer event_state=GetMapInt('伝言イベント進行状態',0);

if(event_state==0){//未開始
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"こんにちは。");
   
}
elif(event_state==1){//Aから伝言を受け取った
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"伝言を受け取ったよ。
      返答をAに伝えてくれるかな。"
);
   
   
//ゲーム変数[マップ整数]の設定 (ゲーム変数名,設定値)
   
SetMapInt('伝言イベント進行状態',2);
   
}
elif(event_state==2){//Bに伝えて返信を受け取った
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"早くAに伝えて欲しいんだよ。");
   
}
elif(event_state==3){//Aに返信を伝えた
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"伝言してくれてありがとう。");
   

   実際に動かして確認してみてください。


● switch による分岐
   上記のスクリプトは 第9回 で扱った if による分岐で記述しました。
   FGESにはもう一つ、 switch による分岐があります。
   switch は前回の選択肢を挿入した時に挿入されたものでもあります。

   switch は今回のように「特定の変数がいくつの時」といったパターンに適しています。
   今回の内容を switch で書き直すと以下のようになります。

   ●イベントキャラクターAの内容
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
< 27>
< 28>
< 29>
< 30>
< 31>
< 32>
< 33>
< 34>
< 35>
< 36>
< 37>
< 38>
< 39>
< 40>

//ゲーム変数[マップ整数]の取得 (ゲーム変数名,デフォルト値)
Integer event_state=GetMapInt('伝言イベント進行状態',0);

switch(event_state){
case 0://未開始
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"イベントBに伝言を頼むよ。");
   
   
//ゲーム変数[マップ整数]の設定 (ゲーム変数名,設定値)
   
SetMapInt('伝言イベント進行状態',1);

   
break;
case 1://Aから伝言を受け取った
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"早くBに伝えて欲しいんだよ。");
   
   
break;
case 2://Bに伝えて返信を受け取った
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"伝言を受け取ったよ。ありがとう。");
   
   
//ゲーム変数[マップ整数]の設定 (ゲーム変数名,設定値)
   
SetMapInt('伝言イベント進行状態',3);
   
   
break;
case 3://Aに返信を伝えた
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"伝言してくれてありがとう。");
   
   
break;

   ●イベントキャラクターBの内容
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
< 27>
< 28>
< 29>
< 30>
< 31>
< 32>
< 33>
< 34>
< 35>
< 36>
< 37>
< 38>

//ゲーム変数[マップ整数]の取得 (ゲーム変数名,デフォルト値)
Integer event_state=GetMapInt('伝言イベント進行状態',0);

switch(event_state){
case 0://未開始
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"こんにちは。");
   
   
break;
case 1://Aから伝言を受け取った
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"伝言を受け取ったよ。
      返答をAに伝えてくれるかな。"
);
   
   
//ゲーム変数[マップ整数]の設定 (ゲーム変数名,設定値)
   
SetMapInt('伝言イベント進行状態',2);
   
   
break;
case 2://Bに伝えて返信を受け取った
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"早くAに伝えて欲しいんだよ。");
   
   
break;
case 3://Aに返信を伝えた
   
   //メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"伝言してくれてありがとう。");
   
   
break;
   主な変化としては毎回 event_state を書く必要がなくなり、
   case が「〜の場合」を扱い、分岐終了のための break が増えています。
   これだけだとどっちもどっちな感じがありますが、
   switch だけの特性として「 break しない場合、次の case の部分も実行される」というものがあります。

<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
< 27>
< 28>
< 29>
< 30>
< 31>
< 32>
< 33>
< 34>
< 35>
< 36>
< 37>
< 38>
< 39>
< 40>
< 41>
< 42>
< 43>
< 44>
< 45>
< 46>
< 47>
< 48>
< 49>
< 50>
< 51>
< 52>
< 53>

//ゲーム変数[インスタンス整数]の取得 (ゲーム変数名,デフォルト値)
Integer cnt=GetInstanceInt('回数',0);

cnt++;

switch(cnt){
case 1:
   
//メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
         
"やぁ、");
   
case 2:
   
   
//メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"こんにちは。");
   
case 3:
   
   
//メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"breakしないと");
   
case 4:
   
   
//メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"こんなふうに");
   
case 5:
   
   
//メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"どんどん下へ進むんだ。");
   
   
break;
case 6:
case 7:
   
//メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"ここは6回目と7回目しか出ないよ。");
   
default:
   
   
//メッセージの表示(待機,消去する) (表示内容)
   
ECL::WaitLastMessage(
      
"defaultは他のcaseが全て不成立の時に来るんだ。");
   
}

//ゲーム変数[インスタンス整数]の設定 (ゲーム変数名,設定値)
SetInstanceInt('回数',cnt); 
   この内容を設定したイベントに何回も話しかけると、
   1〜5回目はだんだん話す内容が短くなっていき、6回目から内容が変わります。
   どちらかといえば突き進む特性は6、7回目のように複数の状態で同じ内容を扱いたい時に便利です。
   別々の case を指定しているため、これらは連続の値である必要がなく、
   「いくつかの状態の時だけ違う反応をする」といったイベントの記述に便利です。
   8回目以降は全ての case が不成立となるので、 default から実行されるようになります。




[前へ] [目次へ] [次へ]

最終更新 2017/07/11