System4.0 Tips

 

System4.0のSACT標準MENU_SELECTの改造

 

※ 注意 ※

このtipsはSACT内部を改造しています。
SACTを改造することによって、予期せぬ動作を引き起こす可能性があります。

 

06/12/24 update!
CSactMenu.jaf の改造箇所にバグがあったのを修正しました。

 

MENU_SELECTのバグ

 実はSACT標準のMENU_SELECTには、バグがあります。

1.sys40ひそひそ話 で述べられているような、MENU_SELECTからのコールバック処理
2.MENU_SELECT中での選択コールバック中でのMENU_SELECT

1の方は、MENU_SELECT自体の仕様的には仕方の無いところはありますが、
2の方は、完全なバグだと考えられます。

 

 しかしながら、MENU_SLECT系コマンドが呼び出す CSactMenu クラスは、
かなり頑丈な作りになっています。
 そのため、少し改造するだけで、上述のバグは修正可能となります。

 

remenu.jaf をダウンロード

改造場所はこのファイルの記述を参考にしてください。

テキストエディタによっては、行数が表示されなかったり、途中改行で行数が合わない場合があります。


使い方

 remenu.jafの先頭コメントにある改造を行ったものとして話を進めます。

 まずは、初期化時に

   MENU_INIT_FIRST();

 を、呼び出します。これが無いと、ぬるぽです 選択肢系命令でエラーが起きます。
 そのため、MENU_SET_METRICS などで選択肢レイアウトを設定する前に呼び出してください。

 逆に言うと、これを呼び出しておけば、MENU_ から始まる命令を実行可能となります。

 

 コールバックを使用する場合は、コールバックを登録する際に

   MENU_KEY_WAIT_CALLBACK( コールバック関数名 );

を実行してください。

  // 例
     MENU_KEY_WAIT_CALLBACK("コールバックA");                // 追加すべきコード
     REGISTER_MSG_KEY_WAIT_CALLBACK(&コールバックA);   // 通常のコールバック登録

 

 また、コールバック時にセーブを行いたい場合は
SACT_RESUME_SAVE をよびだした戻り値がロードの時に

   MENU_RESUMESAVE_LOAD();

を呼び出す必要があります。

  // 例

   MENU_START();                // 追加すべきコード
   MENU_ADD(1, "セーブ");
   MENU_ADD(2, "ロード");
   MENU_ADD(3, "戻る");

   int nSelect = MENU_SELECT();

   if (nSelect == 1) {
      int res;
      if(SACT_RESUME_SAVE("key","save.asd",res)==0){
         // ロードされた
         // 何か他に再構築するものが有れば、再構築するコト。
         //
注意:ここでMENU_INIT_FIRST();が呼び出されないようにしてください。
         MENU_RESUMESAVE_LOAD();                // 追加すべきコード
      }
   }
   else if (nSelect == 2) {
      SACT_RESUME_LOAD("key","save.asd");
   }
 

 

 

実際に使用する直前に以下の命令を発効します。

   MENU_START();

つまり、以下のような流れになります。

  // 例
       MENU_START();                     // 追加すべきコード
       MENU_ADD(0,"1番");
       MENU_ADD(1,"2番");
       MENU_ADD(2,"3番");
       int sel=MENU_SELECT();

 

 必要最低限の変更で済ませたい場合は、
MENU_START 命令を、上から表示されるメニューにのみ適用しても構いません。

 つまり、コールバック関数での選択肢、選択時自動実行関数での選択肢 の2つの時です。
なお、選択時自動実行関数というのは、具体的には以下のものです。

void あなたはギターを演奏するか(){
       'する、あなた、プレイ、グィター? 'B;R; 
       MENU_ADD(0,"はい",&Yes);
       MENU_ADD(0,"いいえ",&No);
       MENU_SELECT();
}

void Yes(){
       'はい、わたしは、する。'A;
}

void No(){
       'いいえ、わたしは、するではない。'A;
}

 


どうやってるの?

 この改造で、どうして動作するようになるのか。

 まず、重要なことは、CSactMenuクラスは複数の選択肢表示に対応しているが、
MENU_SELECT系コマンドは、複数表示に対応していないと言うことです。

 そのため、通常のMENU_SELECT系命令で、無理矢理に複数表示しようとすると
2つの内容がくっついてしまうなどの問題が生じます。

 なぜ、MENU_SELECT系の命令は、1つしか対応していないのか。
これは、CSactMenuクラスのインスタンスをグローバル変数で保持し、
そのインスタンスへのラッパとして、MENU_SELECT系コマンドが存在するためです。

 そのため、今回の改造では、
CSactMenuクラスのインスタンスへの参照をグローバルで保持し、
MENU_START を実行することで、参照先を切り替えるという手段をとりました。

 こうすることにより、複数表示が可能となります。


 ですが、ここで問題が出てきます。
上から表示されるメニューと、下から表示されるメニューが重なってしまいます。
そのため、UPDATEされない間で、スプライト表示を高速で切り替えています。
こうすることで、一番上のメニューのみしか表示されなくなります。

 また、SACT_RESUME_SAVEを行った場合、
メニューのスプライトが消えてしまいます。
( ※ SACT_RESUME_SAVEではスプライトの中身は保存されない )
そのため、MENU_RESUMESAVE_LOAD を実行してもらって、
全ての選択肢のスプライトを再作成するようにしています。

 

 感想

   問題は無いハズですが、ちょっと怖いです(ぇ
  使用される方は、各自問題がないかどうか確認しながら実装してください m(_ _)m