2008年10月30日木曜日

[MLDU] ノートカードの編集

(ありがとうございます。LSLCON ではすでに 60名以上の方に MLDU4RC お試し版をお持ちいただけました。11月2日まで LSLCON 会場で MLDU4RC お試し版を 0L$ プライスレスで配布中です。)

MLDU4 と MLDU3 でのコンセプトの違いは NOTECARD 編集にそれほど時間をかけなくてもダンスを楽しめることかもしれません。
もともとクラブ雷神の LINZOO さんの依頼で作成し始めたとき、MLDU の特徴のひとつである「スムーズなダンスのつなぎ」を実現するために、NOTECARD によるそれぞれのダンス アニメーション再生時間は必須、、、と考えていました。

ですが、、、この NOTECARD 編集、結構手間なんです。(笑

MLDU3 でも NOTECARD に時間指定がないときはデフォルト24秒の再生時間を割り当てていましたが、MLDU4 では以下の機能でさらに時間指定がなくても楽しめるようにしました。

1) [次のダンス]、[前のダンス] ボタンでダンスを切り替えることができる
2) [間隔設定] からダンス アニメーション再生時間を随時変更できる(NOTECARD に再生時間の指定のないものだけが対象)

これにより、NOTECARD 編集の負担が減ったのではないかと思っています。

NOTECARD で指定されたダンス アニメーションはスクリプト リセット時にコンテンツ フォルダ内の存在チェックをします。
もし、NOTECARD に記述しているダンス アニメーションがフォルダ(MLDU)内になければエラー表示をして処理が止まる仕様になっています。

0

[10:28]  MLDU4(Snu): ノートカード 4.DANCES に記載されているアニメーションがありません。 タイプミス、大文字・小文字、半角空白などを確認してください。: weloveyou- boy
[10:28]  MLDU4(Snu): ノードカードに記述されているダンスアニメーションが存在しないので 4.DANCES は処理を中断しました。

というメッセージが表示され、処理が中断しています。
メッセージにあるように、大文字、小文字(Upper/Lower Case) や単純なタイプミスに注意してほしいのですが、なかなかどこが違うのか、何が悪いのかわからない典型的な例がいくつかあります。

1) 半角ブランクが2つあった、、、(SineWave)
SineWave の "4 and 2 the floor" というダンスがあります。そのままノートカードに打ち込むとなぜかエラー、、、
半角ブランクを □ に置き換えると、このダンス、なぜか 4□□and□2□the□floor という、4 と and の間に2つのスペースが入っていました。

2) 読み込めない、読み込めない、、、(SineWave)
これも SineWave なのですが、ダンス名にカンマ(,)が入っていると、、、今の MLDU ではノートカード読みこみの際にダンス名が途中で切られてしまうため、処理を続けることができません。ダンス名と再生秒をカンマで区切る方法をとっているためです。
おおよそ有料のダンス アニメーションは Mod 不可なので、名前を変更することができません。幸い、上記のケースは稀なので、とくに 2) は「ごめんなさい」なのですが、1) の場合などを回避するためになるべくキーボードで名前を打たないで、コピー&ペーストすることをお勧めします。
ダンス アニメーション上で右クリック(Win)でメニューを出して [プロパティ] を選択し、名前をマークして Ctrl+C(Win) でコピーして、ノートカードに貼り付ける方法をとれば、1) のようなトラップにかかりません。またタイプミスの心配もありません。
ノートカードの編集はプロパティから名前の Copy & Paste をしてくださいね。

2008年10月19日日曜日

LSLCON に出展・展示中~

実行委員関係のお友達からお願いされ、まったく気軽に展示してしまいました、、、MLDU4 RC。 (笑
多人数同期ダンスをしたくて作った MLDU3 をこの半年(!!!) くらいかけて、少しずつ改良、機能を追加していったものです。
なので、、、もともとあまり商売っ気がないダンサー/スクリプタですが、、、まだ売ってません(笑

LSLCON JAPAN 2008 の空中会場のテレポート近くのブースで展示中です。

LSLCON01

この数ヶ月、いろいろな方の助けをもらいながら試していたのが時間差ダンスやシャッフルダンス。以前の投稿にも書きましたが、時間差ダンスのアイディアはすぐに出たのですが、やっぱり、この手のものは半分は「エラー処理」になってしまいますね。基本的なところから例外的なところまでいろいろエラー処理をいれてきました。 Mono 化でメモリーに余裕がでたおかげです。

使い方や、MLDU4 の仕組みみたいなことはブースのパネルでも説明しています。(ええ、トリセツがなくて本当に迷惑をかけていた MLDU3 でした、、、)発売するときはこのパネルもいれようかと思っています。

で、、、展示2日目でエラー発見、、、、@@

ダンススロット管理は MLDU のもっとも重要なところです。たとえば、踊りながら落ちちゃった、他の SIM にいってしまった、、、などという場合は使用しているダンススロット(スクリプト)を解放して、次の人が使えるようにしないといけません。
相当このあたりは MLDU3 からテストしてきたつもりだったのですが、今日ブースにいくとだれも踊っていないのに1つのスロットが使用されている状態になっていました。
そこからいろいろと可能性を考えたのですが、どうやればこの状態になるかしばらくわかりませんでした。

llStartAnimation を Avatar に対して行って、もし Avatar がいなければそれによりスロットの解放をするので、llStartAnimation は動いていないと想定。とすると、、、以下の場合があてはまるのではないかと予想しました。
llRequestPermissions により「アバターに動きをつける?」ダイアログが表示されているときに落ちる。

試してみると、、、見事に(?)スロット利用状態のままでした。。。(追記 注2)
たしかに、、、llDialog は使っていませんが、llRequestPermissions によりダイアログは表示されているわけです。通常 listen イベントで受け取るダイアログとは違って、llRequestPermissionsrun_time_permissions イベントでうけとる特殊なダイアログですが、ダイアログはダイアログですよね。

ダンススロットが埋まって解放されないのは致命的なので、、、以下のようなエラー処理をいれました。

llRequestPermissions(id, PERMISSION_TRIGGER_ANIMATION);
llSetTimerEvent(60.0); // 60 秒タイマーをかける
run_time_permissions(integer perm) {
llSetTimerEvent(0.0); //なんらかの返信があったのでタイマーを止める
if (perm&PERMISSION_TRIGGER_ANIMATION) {
llStartAnimation("animation file name");
llInstantMessage(id, "ダンスを止めたい場合は、、、、");
} else reset(); // [ いいえ ] が押されたのでダンススロット解放
}
timer() {
llSetTimerEvent(0.0);
if (llKey2Name(id)!="") {
llInstantMessage(id, "タイムアウトです~");
}
reset(); //ダンススロットの解放サブルーチン
}

ダンスを踊るかどうか聞く前にスロットを一時的に予約して先にタッチした人を優先させるMLDU の管理・アニメ処理方法が、上記のエラー処理を必要としているかもしれません。その部分のロジックを変更するのはつらいので上記の方法をとりましたが、もう1年近くも使ってきて、llRequestPermissions のあとで llSetTimerEvent って、、、ぜんぜん気が付きませんでした。

llRequestPermissions をタイムアウトさせてしまう副作用は、、、、あるんです。
llRequestPermissions によるダイアログへの返答、はい、でも、いいえでも、タイムアウトした後で押すと、、、run_time_permissions イベントに記述されている関数が、、、動きます。@@

これを無効にする方法が、、、、、ないんです。

そんな、、、と思われるでしょうが、この状態でスクリプトをリセットしても、、、、表示されているタイムアウトにしたダイアログに返答すると run_time_permissions のスクリプトが動くのです。。。@@

つまり、、、あくまで想像ですがこの状態になったときの返答への処理は、スクリプトをよんで処理するのではなく、そのブロックがメモリというかスタック上にあって、それを使うような気がします。(注1)

ですが、、、この例外対応、この状態になったら「落ちている」のがほとんどのはず。故意に60秒間放置して、、、という状態の対応は優先順位が低いと考えました。たしかに「はい」を押すと、run_time_permissions イベントのブロックに書かれている初期のダンスアニメーションは始まりますが、それ以上はなにも起こらないので、タイムアウトの際のメッセージに「いいえ、を押して再度ダンスボールを押してください」という運用の対応にしてみました。
スクリプトって楽しいけど、難しいですねぇ、、、

[追記] 注1
基本的に LSL もそのときそのときにスクリプトファイルからスクリプトを読むようなことはしていないはず。なので、該当のスクリプトをリセットしても、そのスクリプトから出された llRequestPermissions に対する処理である run_time_permissons の部分がスタック上クリアされていない、、、というのが正しいのかもしれません。(すみません、本当に想像です。。。) run_time_permissions イベントにある llSay や llStartAnimation は効きますが、上記の例だと reset() というユーザー関数は呼ばれません。さすがにそこはスクリプトのリセットによりクリアされているのでしょうね。

[追記] 注2
注、、、というほどのものではないのですが。(笑
通常のご利用だと、今回示した llSetTimerEvent を llRequetPermissions のあとに入れなくても、スロットが解放されないことはありません。というのも、他のだれかがダンスを開始して管理スクリプトから実行ダンススロットのスクリプトにアニメーション開始命令が出された時点でアバターがいないチェックが入るので、このスロットは解放されます。他の人が使える状態になります。
考えすぎかもしれませんが、、、展示品の状態で「アニメーション許可のダイアログに答えずに落ちる」をスロットの数ぶん10回繰り返されると、スロットが埋まってしまい、このときオーナー(私 ^^)以外はスクリプトのリセットをかけられないので、展示品のダンスだまでは誰も踊れなくなる、、、というのを回避したかったのです、、、。

[追記] 上記のように思っていたのですが、llRequestPermissions に対する返答をしていない状態なので llStartAnimation がダンススロット側で開始されていません。管理スクリプトからダンス開始命令をだしても、ダンススクリプトは保留中のまま、、、でした。なので、やはりこの処理はもしものために必要だと考えたほうがよさそうです、、、

2008年10月4日土曜日

Torley のビデオ チュートリアル

ひさしぶりに Torley のビデオ チュートリアルを見たら知らないことがたくさん!

いろいろな方が翻訳とか紹介とかしていると思うので既知のものもあると思いますが、近々の面白かったものをピックアップしてみました。

Lip Sync
恥ずかしながら知りませんでした。7月23日追加なので情報としては古いですが、いつのまにか Voice Chat とアバターの「口バク」を同期させる機能がついていました、、、


この仕組みからする新たに口バクのアニメーションが追加されているか、既存のものを転用しているのか、ちょっと気になりました。あとで wiki で調べてみようと思います。
[追記] よくよくみたら、、ビデオの中で紹介してますね、、スピーチジェスチャ(笑
[追記] でも、クチパクは別みたいですね (^^;

他のAvatarのFaceLightやパーティクルをオフにする
他の Avatar がアタッチ(装着)しているライトやパーティクルをオフにする方法があるんですね。
時々、横の Avatar の FaceLight がきつくて白浮きする時があるんですが、これでやると回避できますね。(あれ、、、自分の Facelight も、、、消えるのかな?これは後でチェックしてみないと、、)

Windlight プリセットパック
前に見たことがあるような気がしますが、Torley が作った Windlight のプリセットのダウンロードと設定の仕方を説明しています。 Windlight は細かい設定ができる反面、個々の調整のバランスが難しいと感じるときもあるので、このようなプリセット集はうれしいですね。

お月様を変える
ある意味、、、期待していたものと違った(笑 のですが、夜に出てくる月を変える方法を紹介しています。

やはり定期的にチェック必須ですね。

2008年9月17日水曜日

Sunrise - 時間差ダンス

この前の週末に ASUKA DJ RELAY というイベントがあって、DJ さんがリレー形式でクラブ雷神で曲をかけるというイベントがありました。で、そのときの模様を動画でとって Yaz さんの Sunrise で編集してみました。

今、新しいダンス玉のテスト中で、今回のダンス玉の特徴は「時間差ダンス」です。

同期(シンクロ)ダンスはよくありますが、0.5秒とかわざとずらしてダンスを開始させるものを試しています。
今回の動画でもその「時間差ダンス」をみることができます。

時間差ダンスの仕組みはとても簡単ですが、Mono のおかげでちょっと気がラクになったかも・・・です。人数分の llStartAnimation の命令をちょっとずつ時間をずらして行うわけですから、軽い処理とはいえません。

しばらくクラブ雷神で様子をみながらテストを繰り返すことになると思います。

もちろん、時間差から同期の切り替えはボタンでオッケーです。


あ~、エンディングの日付・・・おもいっきり「未来」になってるし、、、開催されたのは9月13日でした。。。

2008年8月31日日曜日

新しい関数 llGetAgentLanguage

Second Life で外人の方とのコミュニケーションは珍しくありませんよね。
ダンスクラブに来られた外人の方にダンスの開始方法を教えるのも、、、まぁ、英語ならなんとかチャットで教えてあげていました。

サーバー 1.24 でいくつかの新しい関数が追加されていますが、llGetAgentLanguage という関数は、その Avatar の Agent が使っているビューワーの言語のバージョンを取得する関数です。これを使うと、スクリプトのメモリが許せば、日本語、英語、がんばれば他の外国語のメッセージをスクリプト内に保存しておいて、Agent が利用しているビューワーの言語情報によって切り替えることが可能ですね。

MLDU(ダンス玉) でいうと DDManager みたいなダイアログの操作でストリング(文字列)とリストを多用していると、メモリの問題から1つのスクリプト内で処理するのはつらいのですが、個々のアバターのパーミッションを管理して、ダンスの開始、停止を行うスクリプトなどは、複数の言語メッセージを保管しておいて、llGetAgentLanguage の情報からメッセージを切り替えることが可能になりますね。

こんな感じです。

string msg1 = "こんにちは!";
string msg1e = "Hello!!";
    if (llGetAgentLanguage(id)=="ja"){
            llInstantMessage(id, msg1);
    } else llInstantMessage(id, msg1e);
日本語以外だったら英語、という単純なものですが、ダンスの停止方法などをビューワーの言語設定にあわせて切り替えて表示させるには重宝しますね。
あたりまえですが、引数にはアバターの Key が必要です。ですから、Detected 系の関数をつかってあらかじめ対象となるアバターの Key を取得して llGetAgentLanguage に渡すことになりますね。戻り値は ja や en-us といった文字列になります。
list を使って利用する方法もありますね。
list msgList01 = ["ja","こんにちは!!","en-us","Hello!!","fr","Salut!!!"];
default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }
    touch_start(integer total_number)
    {
        integer i = 0;
        for(; i<total_number; ++i) {
            string lang = llGetAgentLanguage(llDetectedKey(i));
            integer x = llListFindList(msgList01,[lang]);
            if(x==-1) x=2; //言語対応してない場合のデフォルトを指定します。
            llInstantMessage(llDetectedKey(i), llList2String(msgList01,x+1));
        }
    }
}
ちょっとしたメッセージだと、この方が list に言語と対応するメッセージを追加するだけでよく、 if 文を変更せずに済むので楽なのかもしれませんね。
ちょっとした小ネタでした~。

2008年8月23日土曜日

Mono がやってきます

Linden の Official Blog に「Mono Launch」という記事がついに投稿されましたね。

Linden Official Blog - Mono Launch

20日から 1.24 サーバーの展開が始まって、その 1.24 サーバーには Mono が含まれている、ということらしいです。
サーバーが 1.24 になっただけでは Mono は使えず、スクリプト作成者はクライアントビューワーの 1.21 RC が必要になるということです。1.21 RC のビューワーではスクリプトエディタの下に [Mono] というチェックボックスが追加されるので、それをチェックして保存、コンパイルで Mono 対応に移行完了、ということらしいです。逆に [Mono] にチェックをいれなければ、従来のまま動くようです。
ちなみに サーバー 1.24 のリリースノートによれば、ビューワーの 1.21 RC は 8月25日の週にリリースする予定ですね。

Mono はスクリプトを動かすための新しいエンジンで、従来のものと並存して提供されるとのことです。すべてのスクリプトを変換する必要はなく、また、LSL の書き方が変わるわけではありません。ちょっと安心ですね。

この Mono ですが、この新しい実行環境はスクリプトの「実行スピード」と「メモリー管理」が現行のスクリプト エンジンよりも強化されている点が特徴です。

この 1ヶ月ほど、ダンス玉 (次の MLDU) に機能をどんどん追加していったらメモリーがかなり厳しくなって、スクリプトを分けないと 10人以上踊れない、、、という状況になっていてのこの記事だったので、早速ベータ グリッドにいって試してみました。メモリーに関していえば、これまで 1つのスクリプトは 16KB しかメモリーを使えませんでしたが、Mono では 64 KB まで拡張されていることは知っていました。

今の環境だと、一番メモリーがくるしい DDManager (Dance Slot と Dialog 管理スクリプト) は残りのメモリーが 1.1K しかありません。これ、リストの操作で空のリストを使う方法じゃないと、エラーでスクリプトが止まる状態だったりします、、、

mono003s


ベータ グリッドにアクセスするには Preview Grid Viewers でダウンロードできる 1.21 ベータをインストールして、1.24 サーバーになっている Sandbox のリージョンにいくと Mono のテストが可能です。
早速、スクリプトエディタを開いてみると、ありました、[Mono] のチェックボックスが。
mono002s
MLDU ダンス玉のスクリプトの [Mono] をすべてチェックして、メモリーの状況を確認してみました。
mono001s
1.1 KB しか余裕がなかった DDManager は 29.5KB(!!!) までフリーメモリーが増えました。(^^)v

リストやデータを保持するための単位必要メモリーの容量が大幅に変わっていなければ、これでかなり余裕 (というか、16KB より大きいしw) ができたことになりますね。
あと、やはり実行速度も速くなっています。メモリーの状況を表す上の画面の Text の表示や、MLDU 初期化時のアニメーションチェックなど、今のものよりも速くなっていますね。
ということで、スクリプト分割大改造せずに Mono を待つことにしました。(笑

2008年8月17日日曜日

YazMania Videos Clip

NMR10~、遅ればせながら参加できました。

nmr10d

ファンの人からの「この曲が好き、なぜなら、、、」みたいなやり方って、ほんと楽しかったし。
それぞれが、それぞれの思いあるから、「へぇ~、そうなんだー」みたいなのがあって、新鮮でした。
Yaz Yaz で素人なりにビデオつくってきて良かったなぁ~、と思いました。それぞれの思いがある曲もみんな違うし、それぞれの考え方や印象もみんな違う。それって、いろいろな思いをいだかせることができる才能のある人がなせる業w

すごいなぁ、、、Yaz さんって、いろいろな人にいろいろな感情・体験を持たせているんだぁ、、、と改めて実感しました。
で。。。。

こんなん、作ってみました~。
High Quality な、、、ですでに動画を見てる人は問題なさげ~、ですがw
[追記] サービス終了にて削除しました。
これからも多くのファンを楽しませてください~w

2008年8月12日火曜日

llDialog 再び

造形のセンスまったくなしなので、クールな HUD を作ってスクリプトを埋めこむ、、、なんてことができないので、どうしても llDialog を使うことになってしまいます。

llDialog で表示するダイアログには12個のボタンしか表示できない、ボタン内に表示できる文字数に制限がある、同じ場所から出てくるので「複数ダイアログ表示」できないことはないのですが、あまり使い勝手が良くないなど、制約が多いのですが、、、これを使うしかないわけで(笑

そこそこ多くのダイアログを作ってみて、気がついた点などを再度まとめてみようと思いました。

1) 複数の人がダイアログを押して、それを処理するような状況になるべくしないほうがいいかも

用途にもよりますが、複数の人がダイアログを押すような状況を仮定してスクリプトを組まないほうが懸命のようです。できないことはないのですが、if 文を駆使して、そのロジックをいろいろと悩むよりも、「前の人の処理が終わるまで待ってもらう」としたほうが当然のことながらスクリプトがシンプルになります。

[追記] ダンス玉(つまりアニメーション操作)など Permission を必要とするものなどは、touch されたら、アバターの Key を他のスクリプトに渡し、そちらで処理する方法があります。この場合は、複数の人が1つのプリムをタッチしても、個別に処理するスクリプトの数の最大に達するまで処理をさせることが可能になります。1つの Prim には複数のスクリプトを入れることが可能なので、touch を管理するスクリプトを親として、詳細の処理を子スクリプトに渡す、、、ということができます。

2) state を使ったほうがラクかも

これも人によりけりでしょうが、私の場合は、タッチされたら(もしくは、なんらかのイベントが発生してダイアログを呼ぶようなことになったら)、そのアバターの key を拾って state を変える方法を使うことが多くなりました。メリットは llListenRemove を使わなくても state が変わることによって listeners を溜めなくてすむこと(64 だったかな?、Remove せずにそのくらい溜まるとエラーになります)、他の人が touch したときの処理をシンプルにできること、だと思っています。また、state_entry, state_exit を使った初期化処理/終了処理が可能でスクリプトが見やすくなることもあげられます。デメリットは必ず llSetTimerEvent を使ってタイムアウトを検知させること。これをやらないでダイアログの [無視] ボタンを押されたら元の state に戻って来れません。
(以下は可読性を高めるためにグローバル変数をあまり使ってません)

key toucher = NULL_KEY;
list buttons = ["button1", "button2", "button3"];

default {
    state_entry() {
       toucher = NULL_KEY;
    }
    touch_start(integer detected_num) {
       toucher = llDetectedKey(0); //一番最初にタッチいた Avatar の Key を取得
       state dialog; //ダイアログ・ステートに移動
    }
}

state dialog {
    state_entry() {
       integer hDialog = llListen(-5555, "", toucher, ""); //-5555チャンネルで、タッチした Avatar の声だけ聞く設定
       llListenControl(hDialog, TRUE); //リスナーを追加(要は聞き始める、ということ)
       llDialog(toucher, "好きなボタンを押してください。", buttons, -5555); //ダイアログを表示。返信は -5555 チャンネル
       llSetTimerEvent(30.0); //30秒でタイムアウトにさせるため
    }
    touch_start(integer detected_num) { //ダイアログ処理中にタッチした Avatar への処理
       integer i = 0;
       for(; i<detected_num; i++) { //タッチした人分の処理をする for ループ
           llInstantMessage(llDetectedKey(i), "ダイアログ使用中です。しばらくまってください。");
       }
    }
    timer() {
        llInstantMessage(toucher, "タイムアウトになりました。");
        llSetTimerEvent(0.0);
        state default;
    }
   listen(integer channel, string name, key id, string message) {
        if (llListFindList(buttons, [message]) != -1) { //一応のチェック処理。この程度なら必要ないですが・・・
            llSetTimerEvent(0.0);
            if (message == "button1") {
                xxxxxxxx;
            } else if (message == "button2") {
                yyyyyyyy;
            } else if (message == "button3") {
                zzzzzzzz;
            }
        state default;
        }
   }
   state_exit() {
        toucher = NULL_KEY;
        llSetTimerEvent(0.0); //しつこいですが、、、過去に timer が戻らなかったことがあったので・・・
   }
}

3) ダイナミックにボタンを変えてみましょう

「前ページ」「次ページ」ボタンで複数ダイアログを使いこなすのもおもしろいですが、操作する側にとっては望まない複数ダイアログは結構迷惑、、、だったりします。
たとえば、ダンスを踊る、止める、みたいな場合、最初のころ [踊る] [止める] のボタンを用意していましたが、よくよく考えると踊っている時は [踊る] ボタンを押すわけもなく、だったら [踊る] のボタンを [止める] にすればいいわけです。このため、状態を保持するフラグを使用しなくてはなりませんが、ボタン使用の節約には効果大です。
この場合は、フラグを元にボタンのリストに対して llListFindList を使い、該当する List 要素の順番をとり、llListReplaceList で置き換える、ということをやります。
意外に、、、この手の「対称な意味を持つ」ボタンって、、、多かったりします。
以下の複数ページダイアログの例の「ノートカードが変更されたら Close ボタンを RESET ボタンに変更する」などもこの考えでボタンを変更しています。

4) 複数ページダイアログの例

以前にご紹介している ネトラジ・チェンジャーは複数ページダイアログでネトラジ局をボタンにしています。その方法をご紹介します。
以下のスクリプトの前段階処理で、ノートカードからネトラジ局の情報を読み込んで contentList という List に入れています。
ダイアログの一番下の段は [< Prev] [Close] [Next>] という3つのボタンがくるので、1つのダイアログには9つのラジオ局のボタンが表示できます。その9つの部分を変えていくわけですね。
現在のページ数を currentPage として、0ページ目(そう、この世界ってはじめはゼロのほうが都合がいいです)の場合は 0 番から8番まで、1ページ目の場合は 9番から17番、、、、としていきます。
最後のページは List の項目数を 9 で割った商になります。たとえば0ページから「前ページ」を押されたら、最大ページに移る、、、みたいなこともあらかじめ最後のページ数をとっておいて、もし、「前ページ」がマイナスになるのであれば、最大ページをいれる、次ページが最大ページを超えるようであれば、0ページを入れる、とすれば、複数ページのラウンドロビンが可能ですよね。
以下は NRC のソースの default ステートを除いたそのものです。オレンジにした部分がページとボタン操作になります。
state wait {
    state_entry()
    {
        llMessageLinked(LINK_THIS,myNumber,"wait",gToucher);
        currentPage = 0;
        buttonList = [];
        gToucher = "";
        integer handle = llListen(gChan,"","","");
        llListenControl(handle,TRUE);
    }
    link_message(integer sender_number, integer number, string message, key id)
    {
        if (message == "start") {
            if (number == myNumber) {
                gToucher = id;
                currentPage = 0;
                state active;
            }
        }
       
        if (message == "contentChanged")
        { // ノートカードが変更されたら Close ボタンを RESET ボタンに変更する
            fixedList = llListReplaceList(fixedList,["RESET"],llListFindList(fixedList,["Close"]),llListFindList(fixedList,["Close"]));
        }
    }
    listen(integer channel, string name, key id, string message)
    {
        if (llStringTrim(llToUpper(message),STRING_TRIM)=="HIDE TEXT") {
            llMessageLinked(LINK_THIS,myNumber,"HIDE TEXT","");
        } else if (llStringTrim(llToUpper(message),STRING_TRIM)=="SHOW TEXT") {
            llMessageLinked(LINK_THIS,myNumber,"SHOW TEXT","");
        }
    }      
}
state active {
    state_entry()
    {
        integer handle = llListen(gChan,"",gToucher,"");
        llListenControl(handle,TRUE);
        buttonList = fixedList + llList2List(contentList,currentPage*9,(currentPage+1)*9-1);
        gDialogMsg2 = "登録数 "+(string)gCount+", "+"ダイアログチャンネル: "+(string)gChan;
        gDialogMsg3 = "オブジェクト上の文字を消したい場合は"+"\n"+"/"+(string)gChan+" hide text をダイアログを閉じてから行ってください。";
        dMsg = gDialogMsg+"("+(string)(currentPage+1)+"/"+(string)(maxPage+1)+")"+"\n"+gDialogMsg2+"\n"+gDialogMsg3;
        llDialog(gToucher,dMsg,buttonList,gChan);
        llSetTimerEvent(0.0);
        llSetTimerEvent(gInterval);
    }
    state_exit()
    {
        llSetTimerEvent(0.);
    }
    timer()
    {
        if (llKey2Name(gToucher) != "") {
            llInstantMessage(gToucher,gMsgTimeout);
        }
        state wait;
    }
    listen(integer channel, string name, key id, string message)
    {
        if (llListFindList(fixedList,[message])!=-1) {
            buttonList = [];
            if (message == "Next >"){
                if (currentPage+1 > maxPage) currentPage=0;
                else currentPage = currentPage+1;
            } else if (message == "< Prev") {
                    if (currentPage-1 < 0) currentPage=maxPage;
                else currentPage = currentPage-1;
            } else if (message == "Close") {
                state wait;
            } else if (message = "RESET") {
                llResetOtherScript("dialogMgr");
                state wait;
            }
           
            buttonList = fixedList + llList2List(contentList,currentPage*9,(currentPage+1)*9-1);
            gDialogMsg2 = "登録曲数 "+(string)gCount+", "+"ダイアログチャンネル: "+(string)gChan;
            dMsg = gDialogMsg+"("+(string)(currentPage+1)+"/"+(string)(maxPage+1)+")"+"\n"+gDialogMsg2+"\n"+gDialogMsg3;
            llDialog(gToucher,dMsg,buttonList,gChan);
            llSetTimerEvent(0.0);
            llSetTimerEvent(gInterval);
        } else if (llListFindList(contentList,[message])!=-1) {
            llSetParcelMusicURL(llList2String(urlList,llListFindList(contentList,[message])));
            llSetText(message,<1,1,1>,0.8);
            state wait;
        }
    }
単純といえば単純ですが、、、なんらかのご参考になれば幸いです。

[追記]
最初にものすごく悩んで、今は普通にやっていることを書き忘れました。
llDialog が使用するチャンネルについてです。

5) llDialog が利用するチャンネルは決めうちにしないほうが良い場合が多い

ダンスボールや、音楽玉、ビデオ玉を作り始めのころに、ダイアログの混線の問題がありました。
たとえば、自分の作ったダンスボールを複数 Rez したとき、llDialog が利用するチャンネルを決めうちにしている場合は、それら Rez したダンス玉を交互に操作するとダイアログ・チャンネルの混線が発生します。
llDialog って、結局は llSay でいうことを代わりにやってくれているようなものなので、それぞれのダイアログの返答がどのダイアログからきているかを判定するもの(値)がないんです。
よって、それぞれのダイアログを判定する方法は、llDialog で使用するチャンネルを変える事になります。
私は llFrand を使って、ランダムにダイアログのためのチャンネルを Rez や初期化時に生成して利用するようになりました。
絶対同じものにならない、、、というわけではありませんが、同じオブジェクトを複数 Rez しても、それぞれのダイアログが利用するチャンネルが違う確率が高いので、この方法をいつも無意識に使うようになりました。
以下のようなユーザー関数を使って、ランダムに生成されたチャンネルを使います。
これは 1000 から 9999 までのランダムな数字を作る例です。

integer randChan() {
    return 1000 + (integer)llFrand(8999);
}

llFrand(8999) によって、0 から 8999 までの間の数字がつくられます。 0 が作られたら、それに 1000 を足したもの、つまり 1000 が返され、8999 だったらそれ1000を足した 9999 が返されます。
もちろん、これをマイナスの値にしてあげれば、さらに良くなりますね。

2008年7月6日日曜日

Electrogram NITORO Black Side

ビデオ、つくっちゃいました~。

Video: Electrogram NITORO Black Side

KOBIKI-Z, The Black Stripes のみなさま、楽曲を使用しないでごめんなさい~。
ライブの模様を・・・ということでおゆるしください。
また、特設タワー崩壊のシーンもいれてみました。(事情をしらない人はかなり驚いたようです)

Yaz さんの曲で Live の模様の動画つくったの、、、N.M.R.7 ぶり?

[追記...] GION Experiment があったじゃない!(笑

みりおんきっし~ず

High Quality な動画を楽しむ その2

エレクトログラム、最高でした。
残念ながら1日目は参加できませんでしが、今日の2日目。
われらが Yaz さんはもちろんですが、、、KOBIKI-Z 最高でした、、、てか反則w

「おもい・・」「おもい・・・」「どげんかせんと・・・」

ほんと、面白かったぁ。

ビデオは撮ってみました。でも、楽曲が Yaz さんのものしか手元にないので、、、、ビデオ編集悩みそうです。

で、前回から引き続いて High Quality な動画その2は Instant Sorrow でw
[該当の動画サービスが終了予定なので以下は削除いたしました。YouTube でお楽しみください。]


動画エリアの下のほうにマウスをおくとコントロールがでるので、再生ボタンをぽっちっとな、、してください。
(注)この動画を見るには専用プラグインのインストールが必要です。1-2分程度で終了します。
[追記] Firefox3 だとちょっとうまくいかないようです。FireFox3 の方は今しばらくお待ちください。。。
[追記] Firefox3 でもOKになりました。


ビデオおたのしみにぃ~w