Adsense

2009年9月25日金曜日

[LSL] llDialog - ダイアログの切り替え

ひさしぶりのスクリプト ネタになります。
こちらのブログにも「ダイアログ」「切り替え」「12以上」(ボタンですね)といったキーワードで検索されて閲覧されている方がそこそこいらっしゃいました。

MLDU(ダンス マシン)は llDialog を使った複数ページ(ダイアログ)でさまざまな設定やダンス アニメーションの開始を指示します。ある程度ダイアログの切り替えに関しての知識やテクニックがたまったかもしれませんので、ちょっとご紹介します。

複数ページ ダイアログの作り方

単純な複数ページのダイアログについて考えてみます。
といっても、メニュー>サブメニュー ではなく、最終的にボタンとなって表示されるアイテムが 12 個以上ある場合の、複数ページを使って表示するシンプルなものについてです。

スクリプトって、いろいろな書き方ができるので、このやり方だけではないことを前提させてくださいね。

以下のようなリストにある要素をボタンとして表示して、次のページ、次のページで順番に表示させるようなものを考えてみます。

list buttonList = [“1”,”2”,“3”,”4”,“5”,”6”,“7”,”8”,“9”,”10”,“11”,”12”,“13”,”14”];

最低でも必要なボタンは [“次のページ”]ですよね。できれば [“前のページ”] ボタンもほしいところ。私はなるべくダイアログの [無視する] ボタンを押されたくないので、だいたいダイアログを閉じるための [“閉じる”] ボタンをいれるクセがついています。

そうすると、1つのダイアログに表示できる要素数は 9 つ (12-3) となります。
この 9 つのリスト要素をどう処理するかになります。

いろいろやり方あるけど、いきついたのは「ページ管理」です。

現在のページ数をおぼえておき、それらから表示するボタンを選ぶ方法ですが、実装方法としては for 文をまわす方法とリスト操作する方法があります。for をまわすよりリスト操作のほうが比較的軽い処理になりますが、for 文には for 文のよさもあったりします。
以下サンプル。わかりやすいようにしたため、実用的ではない「お勉強スクリプト」です。たとえば実際は list base = [“前のページ”,”閉じる”,”次のページ”] とは書きませんね。
それぞれの要素を string prev = “前のページ”; と宣言して、list にいれたほうが、あとあと if 文の比較のときに string prev を比較すればいいですから。。。と、、いうことを前提に。

list bList = ["1","2","3","4","5","6","7","8","9","10","11","12","13","14"];
list base = ["前のページ","閉じる","次のページ"];
integer dChan = –10; //ダイアログ用チャンネル(本当はランダムにしたほうが実用的)
integer dHandle;
integer page = 0;
integer pageMax;
integer pageItem = 9; //これも通常は初期化処理で計算かな。
key avatar = "";
string dialogMsg = "次、前のページでダイアログがかわります。";
float timeout = 20.0;

default{
      state_entry(){
            page = 0;
            avatar = "";
      }

      touch_start(integer total_number){
            if(avatar == ""){  //page管理しているので一時点に1人だけ対応するため
                  page = 0;
                  integer len = llGetListLength(bList);
                  pageMax = len/pageItem;
                  if(len%pageItem == 0){  //pageItem数で割り切れているときは
                        pageMax--;          //page数が1つ多いのでマイナス1
                  }
                  avatar = llDetectedKey(0);
                  dHandle = llListen(dChan, "", avatar, "");
                  llListenControl(dHandle,TRUE);
                  llDialog(avatar,dialogMsg,base+llList2List(bList, page*pageItem, page*pageItem+pageItem-1), dChan);  //開始と終了の計算
                  llSetTimerEvent(timeout);  //無視ボタンなどを押されたときの対応のため
            }
      }

      listen(integer chan, string name, key id, string msg){
            llSetTimerEvent(0.0);
            if(msg == "閉じる"){
                  llListenControl(dChan, FALSE);
                  llListenRemove(dHandle);
                  avatar = "";
                  jump exit;
            }else if(msg == "次のページ"){
                  page++;
                  if(page > pageMax){ //ページ数が最大数より大きくなったら 0 に戻す
                        page = 0;
                  }
            }else if(msg == "前のページ"){ 
                  page--;
                  if(page < 0){ //ページ数がマイナスになったら最大ぺージ数に
                        page = pageMax;
                  }
            }else if(msg == "1"){
                  //実際は個別の処理です。受け取ったメッセージ(ボタンのラベル)をそのまま
                  //llSayのように使うのなら、本来条件分岐の必要はありません。
                  llSay(0,msg+" が押されました。");
            }else if(msg == "2"){
                  llOwnerSay(msg+" が押されました。");
            }else if(msg == "3"){
                  llInstantMessage(id,msg+" が押されました。");
            } // この繰り返しをボタン分かく場合もあります。
            llDialog(avatar,dialogMsg,base+llList2List(bList, page*pageItem, page*pageItem+pageItem-1), dChan);  //開始と終了の計算
            llSetTimerEvent(timeout);
            @exit; //閉じるの場合のとび先はここ
      }

      timer(){
            llSetTimerEvent(0.0);
            llListenControl(dChan, FALSE);
            llListenRemove(dHandle);
            avatar = "";
            page = 0;
      }
}

[追記 2009.09.27] 上記ソースコード、そのままコピペするとダブルクォーテーション(“)がおかしくてコンパイルエラーになります。ちょっと大きめの斜めってる “ を変更してください、、、。回避方法はおいおい探します。。。
[追記 2009.10.26] “ を replace してみました。今度はだいじょうぶです。

これで以下のようなダイアログがでて、次のページ、前のページで切り替わります。

dialog1    dialog2

これがダイアログ操作の基本形ですね。
ご参考になれば。