2010/02/01
第01回 ダイアログモジュール
最初の記事はスクリプトの中でもとてもよく使う llDialog についてです。
タッチ、タイマー、リッスンイベント等を複合して使う必要があり、ダイアログを使いこなせればスクリプターとして一歩前進です
ダイアログスクリプトは、リッスン状態の管理にタイマーイベントを使用しますので、タイマーイベントを使用したものを作りたい場合は、ダイアログとは別のスクリプトで書いた方がいいでしょう。ですから今回のスクリプトは、「オブジェクトにタッチ→ダイアログを表示→選択したボタンに対応した命令を送信」という部分のみをモジュール化したものです。さらに、ユーザー関数を導入してコードの簡略化を図ります。
タッチ、タイマー、リッスンイベント等を複合して使う必要があり、ダイアログを使いこなせればスクリプターとして一歩前進です

ダイアログスクリプトは、リッスン状態の管理にタイマーイベントを使用しますので、タイマーイベントを使用したものを作りたい場合は、ダイアログとは別のスクリプトで書いた方がいいでしょう。ですから今回のスクリプトは、「オブジェクトにタッチ→ダイアログを表示→選択したボタンに対応した命令を送信」という部分のみをモジュール化したものです。さらに、ユーザー関数を導入してコードの簡略化を図ります。
コードはこのようになります。
順に解説していきます。まずは変数から。
integer mode
ダイアログ操作を誰でも可能にするか、オブジェクトオーナーのみに限定するかの設定です。
key toucher
ダイアログを表示させるためにタッチした人のUUIDを管理します。
integer handle
integer channel
リッスン用のハンドルとチャンネルです。チャンネルは混線しないような数字にしましょう。
list dialogbt1
list dialogbt2
ダイアログに表示させるボタン名のリストです。それぞれ動作に対応した名前にします。
次にユーザー関数 dialog (integer switch) についてです。
integer 型の引数0で呼び出された時はダイアログ動作を閉じる一連の動作。
1の場合はメインダイアログを展開、2の場合は2ページ目のサブダイアログを展開します。
具体的にはこうです。
dialog(0) で実行された時は key toucher を NULL_KEY に戻す、変数 handle を0に戻し、リッスンを解除、タイマーを切る。
dialog(1) で実行された時は、タイマーを60秒に設定して、メインダイアログを表示。
dialog(2) で実行された時は、タイマーを60秒に設定して、サブダイアログを表示。
ダイアログの動作と言うのはおおまかにこのような流れです。
1. オブジェクトをタッチする等のイベント(きっかけ)により、スクリプトが対象者の発言を指定チャンネルで聞くようにする。(リッスン開始)
2. 対象者にダイアログを表示させて、ボタンが押されたらそのボタン名が指定チャンネルで発言される。(ダイアログ表示)
3. スクリプトがこの発言を認識したら、それに対応した動作を実行する。(リッスンイベント発動)
4. ダイアログ操作の終了処理、またはタイムアウトした場合の終了処理。(リッスン状態を解除)
先に書いたユーザー関数 dialog(integer switch) の処理はこの流れで言うところの2番目と4番目です。
今回のスクリプトは、対象者にダイアログを表示させるための準備、タッチイベントが1番目の処理ですね。
default ステートでの実際の動きと合わせて見てみましょう。タッチイベント内の動作です。
タッチ動作に関しては、動作モード毎に処理を二つに分けています。
スクリプトの一行目で指定した、integer mode = 0; // ダイアログ動作モード選択(0 = 誰でも 1 = オーナーのみ)
この部分に対応しています。
まず、if (mode ==0) から書かれている部分が、誰でも操作可能にした時の挙動です。
handleに何も入っていない0の時は、key toucher に タッチした人のキーを入れ、toucher に対してリッスンを開始、ユーザー関数 dialog(1) にてメインダイアログを toucher に表示。というダイアログ表示までの処理を行います。
handleに数値が入っていて(使用中)、さらにタッチした人が key toucher と同じ人だった場合は、再度メインダイアログを表示します。ダイアログを表示させた人が、ダイアログの「無視する」で閉じてしまった時に、その人が再度タッチした時に備えた処理です。key toucher は、ダイアログ処理の最後に必ず NULL_KEY に戻すようにしていますから、これに誰かのUUIDが入っていると言うことは、スクリプトはすでにその人に対してリッスン状態にあると言うことです。ですから dialog(1) のみの処理を記述しています。
handleに数値が入っていて(使用中)、さらにタッチした人が key toucher と別の人だった場合は、「現在他の人が操作しているのでしばらく待ってね」と通知します。
次に、if (mode ==1) の所からが、オーナーのみ操作可能にした時の挙動です。
タッチした人がオーナーだった場合に、key toucher にタッチした人のUUIDを代入、その人に対してリッスン開始、メインダイアログを表示、となります。この場合、不特定多数の人がタッチして動く設定ではないので handle の状態を調べて使用中かどうかの判断はしていません。
タイマーイベントに関しては、ダイアログのタイムアウト処理です。
dialog(integer switch) 関数でダイアログを表示させる度に60秒にタイマーを設定しています。ダイアログ表示から1分間応答がなかった時にタイムアウトを通知し、リッスン解除等一連の動作を dialog(0) で行います。
最後にリッスンイベントです。
msg =="ほにゃらら" と言うのは、ダイアログのボタン名がそのまま入ります。
例えばダイアログのボタン名「close」 が押された時、toucher により指定チャンネルにて「close」と発言されます。
スクリプトはリッスンイベントでその toucher が発言した「close」を聞きとり、指定の動作を行います。ここではdialog(0)でダイアログの終了処理を行っていますね。
「button01~09」、また半角スペースの空白はメインダイアログのボタン(変数 list dialogbt1)です。これらを選択した時、llMessageLinked 関数により、「このボタンが押されたよ」という事を同オブジェクト内の全スクリプトに通知します。そして再度メインダイアログを表示させています。
「next >>」 を選択した時は dialog(2) でサブダイアログを表示、「<< back」 を選択した場合は dialog(1) でメインダイアログに戻ります。
「buttonA~B」はサブダイアログのボタンですから、dialog(2) でサブダイアログを再表示させています。
連続した操作のために、ダイアログを繰り返し表示させる。その度に llSetTimerEvent と llDialog を書き連ねるのは大変ですし美しくありません。このような時、一連の処理をまとめたユーザー関数が有用になります。
今回は2ページ分のダイアログボタン(list dialogbt1, list dialogbt2)を用意し、ユーザー関数 integer(switch) 内の処理でダイアログを2つコントロールしています。用意したいダイアログ数にあわせてここを調整すれば、3種類、4種類のダイアログに対応できるでしょう。
state_entry() と on_rez(integer param) イベントの説明を端折りましたが、dialog(0) を使って変数を初期状態にする事と、Rez された時のスクリプトのリセットです。
このダイアログスクリプトは、プリムの色を変更する等の具体的な動作をに入れ込んでいません。特に、プリムのパラメータを変更する llSetPrimitiveParams 関数や、指定した人にIMを送る llInstantMessage 関数等は一瞬で処理できませんので、処理にもたつきを感じる原因になりえます。また全て1スクリプトで済まそうとするとコードも複雑になり無理が出てくるでしょうから、あくまで、ダイアログ操作のみに機能を限定したモジュールとなっています。
次回はこのダイアログモジュールのllMessageLinked 関数を受けての、別スクリプトでの処理の書き方です。
※ 2010/02/08 記事修正 変数dflagを廃止して簡略化しました。
integer mode = 0; // ダイアログ動作モード選択(0 = 誰でも 1 = オーナーのみ)
key toucher;
integer handle;
integer channel = -100;
list dialogbt1 = [
"close"," ","next >>",
"button07","button08","button09",
"button04","button05","button06",
"button01","button02","button03"
];
list dialogbt2 = [
"<< back","buttonA","buttonB"
];
dialog(integer switch)
{
if (switch == 0){
toucher = NULL_KEY;
handle = 0;
llListenRemove(handle);
llSetTimerEvent(0.0);
}
else if (switch == 1){
llSetTimerEvent(60.0);
llDialog(toucher,"Dialog module test",dialogbt1,channel);
}
else if (switch == 2){
llSetTimerEvent(60.0);
llDialog(toucher,"Dialog module test\n>> Next page",dialogbt2,channel);
}
}
default
{
state_entry()
{
dialog(0);
}
touch_start(integer detected)
{
if (mode == 0){
if (!(handle)){
toucher = llDetectedKey(0);
handle = llListen(channel, "", toucher, "");
dialog(1);
}
else if ((handle) && (llDetectedKey(0) == toucher)){
dialog(1);
}
else if ((handle) && (llDetectedKey(0) != toucher)){
llSay(0,"Please wait. Someone is operating this now.");
}
}
else if (mode == 1){
if (llDetectedKey(0) == llGetOwner()){
toucher = llDetectedKey(0);
handle = llListen(channel, "", toucher, "");
dialog(1);
}
}
}
timer()
{
llSay(0,"Time out! dialog operation was ended.");
dialog(0);
}
listen(integer channel, string name, key id, string msg)
{
if (id == toucher){
if (msg == "close") {dialog(0);}
else if (msg == " ") {dialog(1);}
else if (msg == "button01") {llMessageLinked(LINK_SET,0,"bt01", NULL_KEY); dialog(1);}
else if (msg == "button02") {llMessageLinked(LINK_SET,0,"bt02", NULL_KEY); dialog(1);}
else if (msg == "button03") {llMessageLinked(LINK_SET,0,"bt03", NULL_KEY); dialog(1);}
else if (msg == "button04") {llMessageLinked(LINK_SET,0,"bt04", NULL_KEY); dialog(1);}
else if (msg == "button05") {llMessageLinked(LINK_SET,0,"bt05", NULL_KEY); dialog(1);}
else if (msg == "button06") {llMessageLinked(LINK_SET,0,"bt06", NULL_KEY); dialog(1);}
else if (msg == "button07") {llMessageLinked(LINK_SET,0,"bt07", NULL_KEY); dialog(1);}
else if (msg == "button08") {llMessageLinked(LINK_SET,0,"bt08", NULL_KEY); dialog(1);}
else if (msg == "button09") {llMessageLinked(LINK_SET,0,"bt09", NULL_KEY); dialog(1);}
else if (msg == "next >>") {dialog(2);}
else if (msg == "<< back") {dialog(1);}
else if (msg == "buttonA") {llMessageLinked(LINK_SET,0,"btA", NULL_KEY); dialog(2);}
else if (msg == "buttonB") {llMessageLinked(LINK_SET,0,"btB", NULL_KEY); dialog(2);}
}
}
on_rez(integer param)
{
llResetScript();
}
}
順に解説していきます。まずは変数から。
integer mode
ダイアログ操作を誰でも可能にするか、オブジェクトオーナーのみに限定するかの設定です。
key toucher
ダイアログを表示させるためにタッチした人のUUIDを管理します。
integer handle
integer channel
リッスン用のハンドルとチャンネルです。チャンネルは混線しないような数字にしましょう。
list dialogbt1
list dialogbt2
ダイアログに表示させるボタン名のリストです。それぞれ動作に対応した名前にします。
次にユーザー関数 dialog (integer switch) についてです。
dialog(integer switch)
{
if (switch == 0){
toucher = NULL_KEY;
handle = 0;
llListenRemove(handle);
llSetTimerEvent(0.0);
}
else if (switch == 1){
llSetTimerEvent(60.0);
llDialog(toucher,"Dialog module test",dialogbt1,channel);
}
else if (switch == 2){
llSetTimerEvent(60.0);
llDialog(toucher,"Dialog module test\n>> Next page",dialogbt2,channel);
}
}
integer 型の引数0で呼び出された時はダイアログ動作を閉じる一連の動作。
1の場合はメインダイアログを展開、2の場合は2ページ目のサブダイアログを展開します。
具体的にはこうです。
dialog(0) で実行された時は key toucher を NULL_KEY に戻す、変数 handle を0に戻し、リッスンを解除、タイマーを切る。
dialog(1) で実行された時は、タイマーを60秒に設定して、メインダイアログを表示。
dialog(2) で実行された時は、タイマーを60秒に設定して、サブダイアログを表示。
ダイアログの動作と言うのはおおまかにこのような流れです。
1. オブジェクトをタッチする等のイベント(きっかけ)により、スクリプトが対象者の発言を指定チャンネルで聞くようにする。(リッスン開始)
2. 対象者にダイアログを表示させて、ボタンが押されたらそのボタン名が指定チャンネルで発言される。(ダイアログ表示)
3. スクリプトがこの発言を認識したら、それに対応した動作を実行する。(リッスンイベント発動)
4. ダイアログ操作の終了処理、またはタイムアウトした場合の終了処理。(リッスン状態を解除)
先に書いたユーザー関数 dialog(integer switch) の処理はこの流れで言うところの2番目と4番目です。
今回のスクリプトは、対象者にダイアログを表示させるための準備、タッチイベントが1番目の処理ですね。
default ステートでの実際の動きと合わせて見てみましょう。タッチイベント内の動作です。
touch_start(integer detected)
{
if (mode == 0){
if (!(handle)){
toucher = llDetectedKey(0);
handle = llListen(channel, "", toucher, "");
dialog(1);
}
else if ((handle) && (llDetectedKey(0) == toucher)){
dialog(1);
}
else if ((handle) && (llDetectedKey(0) != toucher)){
llSay(0,"Please wait. Someone is operating this now.");
}
}
else if (mode == 1){
if (llDetectedKey(0) == llGetOwner()){
toucher = llDetectedKey(0);
handle = llListen(channel, "", toucher, "");
dialog(1);
}
}
}
タッチ動作に関しては、動作モード毎に処理を二つに分けています。
スクリプトの一行目で指定した、integer mode = 0; // ダイアログ動作モード選択(0 = 誰でも 1 = オーナーのみ)
この部分に対応しています。
まず、if (mode ==0) から書かれている部分が、誰でも操作可能にした時の挙動です。
handleに何も入っていない0の時は、key toucher に タッチした人のキーを入れ、toucher に対してリッスンを開始、ユーザー関数 dialog(1) にてメインダイアログを toucher に表示。というダイアログ表示までの処理を行います。
handleに数値が入っていて(使用中)、さらにタッチした人が key toucher と同じ人だった場合は、再度メインダイアログを表示します。ダイアログを表示させた人が、ダイアログの「無視する」で閉じてしまった時に、その人が再度タッチした時に備えた処理です。key toucher は、ダイアログ処理の最後に必ず NULL_KEY に戻すようにしていますから、これに誰かのUUIDが入っていると言うことは、スクリプトはすでにその人に対してリッスン状態にあると言うことです。ですから dialog(1) のみの処理を記述しています。
handleに数値が入っていて(使用中)、さらにタッチした人が key toucher と別の人だった場合は、「現在他の人が操作しているのでしばらく待ってね」と通知します。
次に、if (mode ==1) の所からが、オーナーのみ操作可能にした時の挙動です。
タッチした人がオーナーだった場合に、key toucher にタッチした人のUUIDを代入、その人に対してリッスン開始、メインダイアログを表示、となります。この場合、不特定多数の人がタッチして動く設定ではないので handle の状態を調べて使用中かどうかの判断はしていません。
タイマーイベントに関しては、ダイアログのタイムアウト処理です。
timer()
{
llSay(0,"Time out! dialog operation was ended.");
dialog(0);
}
dialog(integer switch) 関数でダイアログを表示させる度に60秒にタイマーを設定しています。ダイアログ表示から1分間応答がなかった時にタイムアウトを通知し、リッスン解除等一連の動作を dialog(0) で行います。
最後にリッスンイベントです。
listen(integer channel, string name, key id, string msg)
{
if (id == toucher){
if (msg == "close") {dialog(0);}
else if (msg == " ") {dialog(1);}
else if (msg == "button01") {llMessageLinked(LINK_SET,0,"bt01", NULL_KEY); dialog(1);}
else if (msg == "button02") {llMessageLinked(LINK_SET,0,"bt02", NULL_KEY); dialog(1);}
else if (msg == "button03") {llMessageLinked(LINK_SET,0,"bt03", NULL_KEY); dialog(1);}
else if (msg == "button04") {llMessageLinked(LINK_SET,0,"bt04", NULL_KEY); dialog(1);}
else if (msg == "button05") {llMessageLinked(LINK_SET,0,"bt05", NULL_KEY); dialog(1);}
else if (msg == "button06") {llMessageLinked(LINK_SET,0,"bt06", NULL_KEY); dialog(1);}
else if (msg == "button07") {llMessageLinked(LINK_SET,0,"bt07", NULL_KEY); dialog(1);}
else if (msg == "button08") {llMessageLinked(LINK_SET,0,"bt08", NULL_KEY); dialog(1);}
else if (msg == "button09") {llMessageLinked(LINK_SET,0,"bt09", NULL_KEY); dialog(1);}
else if (msg == "next >>") {dialog(2);}
else if (msg == "<< back") {dialog(1);}
else if (msg == "buttonA") {llMessageLinked(LINK_SET,0,"btA", NULL_KEY); dialog(2);}
else if (msg == "buttonB") {llMessageLinked(LINK_SET,0,"btB", NULL_KEY); dialog(2);}
}
}
msg =="ほにゃらら" と言うのは、ダイアログのボタン名がそのまま入ります。
例えばダイアログのボタン名「close」 が押された時、toucher により指定チャンネルにて「close」と発言されます。
スクリプトはリッスンイベントでその toucher が発言した「close」を聞きとり、指定の動作を行います。ここではdialog(0)でダイアログの終了処理を行っていますね。
「button01~09」、また半角スペースの空白はメインダイアログのボタン(変数 list dialogbt1)です。これらを選択した時、llMessageLinked 関数により、「このボタンが押されたよ」という事を同オブジェクト内の全スクリプトに通知します。そして再度メインダイアログを表示させています。
「next >>」 を選択した時は dialog(2) でサブダイアログを表示、「<< back」 を選択した場合は dialog(1) でメインダイアログに戻ります。
「buttonA~B」はサブダイアログのボタンですから、dialog(2) でサブダイアログを再表示させています。
連続した操作のために、ダイアログを繰り返し表示させる。その度に llSetTimerEvent と llDialog を書き連ねるのは大変ですし美しくありません。このような時、一連の処理をまとめたユーザー関数が有用になります。
今回は2ページ分のダイアログボタン(list dialogbt1, list dialogbt2)を用意し、ユーザー関数 integer(switch) 内の処理でダイアログを2つコントロールしています。用意したいダイアログ数にあわせてここを調整すれば、3種類、4種類のダイアログに対応できるでしょう。
state_entry() と on_rez(integer param) イベントの説明を端折りましたが、dialog(0) を使って変数を初期状態にする事と、Rez された時のスクリプトのリセットです。
このダイアログスクリプトは、プリムの色を変更する等の具体的な動作をに入れ込んでいません。特に、プリムのパラメータを変更する llSetPrimitiveParams 関数や、指定した人にIMを送る llInstantMessage 関数等は一瞬で処理できませんので、処理にもたつきを感じる原因になりえます。また全て1スクリプトで済まそうとするとコードも複雑になり無理が出てくるでしょうから、あくまで、ダイアログ操作のみに機能を限定したモジュールとなっています。
次回はこのダイアログモジュールのllMessageLinked 関数を受けての、別スクリプトでの処理の書き方です。
※ 2010/02/08 記事修正 変数dflagを廃止して簡略化しました。
この記事へのトラックバックURL
http://redzone.slmame.com/t824233
Your comment will be displayed after accepted.


