聞き耳を立てる(初級スクリプト第七回)

Miz

2007年04月11日 12:15


照明スクリプトの続きです。
前回作ったスクリプトの改良になりますので、実習する際には前回分からやってみて下さい。

前回の問題点

前回はタッチするたびに赤、緑、青、消灯と変化する照明を作りましたが、この照明は好きな色にする際に何度もタッチしなければならないという問題点があります。
いきなり青い光をつけたいとか、赤い光で点けていたが消灯したいというようなとき、数回クリックするのは面倒です。
ラグが発生しているときなどには、誤って二度、三度とタッチしてしまい、一気に色が変わってしまうようなこともあります。



何度もタッチすることなく好みの色にしたり、消灯するようにしたいところです。
例えば、タッチのたびに色を変えるのではなく、タッチしたあと、チャットで色を指定するとその色に変わるような仕組みはどうでしょうか。
"red"と言えば赤く光り、"blue"と言えば青、"off"で消えるような照明です。
lslでは、チャットの発言をチェックするための機能が用意されています。
これをリッスン(listen)機能と言います。
スクリプトが発言を「聴く」わけです。

リッスンはイベント(きっかけ)になります。
「発言があったときに」というイベントですね。
ただし、このイベントは常に発動するわけではなく、あらかじめスクリプトのほうで、
「これから発言に耳を傾けるようにします」
と設定しておかなければいけません。
マイクのスイッチを入れるようなものだと思ってください。

ですので今回の改良は、
(1)タッチしたときにlistenをONにする
(2)チャットで色名が発言されたら照明の色を変える
この2点になります。

さっそくlistenの使い方から見ていきましょう。

listenのON

まずはlistenをONにする命令からです。

  integer llListen(integer channel, string name, key id, string msg)

ちょっと難しそうです(^^;
ですが、この命令はスクリプトでは非常に使用頻度が高い命令ですので、きっちり使い方を覚えておくべきです。

引数が4つありますので、順番に説明していきます。

integer channel

聞き耳を立てるチャットチャンネルを指定します。

チャットチャンネルというのは、チャットの周波数みたいなものです。
普段使っているチャットは0チャンネルで送受信されています。
それ以外にもチャンネルは無数にあり、例えば2147483647チャンネル(驚)なんかはスクリプトのエラー情報が飛び交っているチャンネルです。
2チャンネルでは常に最新のネタが受信可能です(違

チャット欄から0以外のチャットチャンネルで発言するには、
  /1 メッセージ
のように入力します。
/の後ろに書いてある数字がチャンネルです。

例えば、色名の入力を7チャンネルで行うと決めたら、llListen()に指定するチャンネルを7にするわけです。
その場合はユーザーは「/7 red」とか「/7 off」のように発言することでスクリプトに命令することができます。

0チャンネル以外のメッセージは、通常チャットログには表示されません。
「red」「off」のような発言がログに流れるのはうっとおしいですし、不特定多数の人がいるような場所ではスパム扱いされる可能性もあります。
ですので、スクリプトに対するコマンドは0チャンネル以外を使うのが望ましいでしょう。

string name

誰の発言に耳を傾けるかです。
アバターに限らず、オブジェクトの名前を指定することもあります。

string型というのは文字列を扱う型で、スクリプトの中で文字列を書くときには""で囲みます。
例えば私の名前であれば、
  "Miz Cremorne"
のように書きます。

全ての人の発言に耳を傾けるときは、空文字を指定します。
空文字とは、
  ""
のことです。

key id

これも誰の発言に耳を傾けるか、です。
ただしこちらは名前ではなくUUIDを指定します。

UUIDというのは、SLの世界に存在するもの全てに割り振られているユニークなIDのことです。
認識番号みたいなものですね。
この番号は"66864f3c-e095-d9c8-058d-d6575e6ed1b8"のような形式になっており、スクリプトではアバターやアイテムを特定するためにしばしばこのIDを使います。
ですが、キーの実際の中身("66864f3c-e095-d9c8-058d-d6575e6ed1b8"のような)を、具体的に調べなければならないことはそれほど多くはありません。
ほとんどの場合は、key型の変数を用意し、その中にこの英数字を格納して使いますので、変数名をやり取りするだけになります。

ついでに説明しておきますが、key型の変数の初期値は、"00000000-0000-0000-0000-000000000000"です。
この値はNULL_KEYという特別な定数名で扱うことができます。
NULL_KEYというのが出てきたら、それは"00000000-0000-0000-0000-000000000000"のことであり、key型の初期値なんだということだけ分かっていればOKです。
この値は「空っぽ」「存在しないもの」と同義になりますので、NULL_KEYで示されるアバターやアイテムは存在しません。

llListen()でNULL_KEYを使った場合は、全ての人/物の発言に聞き耳を立てるようになります。
「存在しないもの」の発言を聴く=誰のものでもない発言を聴く=誰のであろうが発言全部を聴く、ということです。

string msg

何という言葉に耳を傾けるかです。
例えばここに"red"と指定すると、"red"という発言以外は一切聴いてくれません。
"I love you"と指定したら、"I love you"以外は完全無視する恋は盲目的な厄介なスクリプトになります。

"red"も"green"も"blue"も"off"も聴いてくれよ~という場合は、空文字""を指定します。
空文字""は「何であろうと発言を聴く」ことになりますので、想定外の言葉も全部聴こえてきます。
聴きたくないことも中にはあるでしょうが、こればっかりはどうしようもありません。
従って、想定外の言葉に対する反応は別途考えておく必要があります。

以上4つの引数を使ってllListen()命令を実行すると、integer型の数値が戻ってきます。
この数値は「ハンドル」と呼ばれます。
ONにしたマイクの番号みたいなものだと思ってください。

例えば、llListen()を二度実行したりすると、このハンドルはそれぞれ違う値が割り当てられます。
マイク1とマイク2がONになるようなものですね。

具体的にllListen()の書き方を例示しておきましょう。

  integer handle = llListen(7, "", llGetOwner(), "");

この例では、チャットチャンネル7での発言を聞き取ります。
発言者の名前にはこだわりません。
ですが、llGetOwner()でUUIDを指定していますので、実際はただ一人の人物の言葉だけを聴きます。

llGetOwner()は初登場ですが、オブジェクトのオーナーのUUIDを取得する関数です。
従って上記の例はオーナーの発言しか聞き取りません。

最後の引数が空文字ですので、発言内容に関わらず聞き取ります。
罵りであろうが、侮蔑であろうが、愚痴であろうが、何でも聞いてくれる健気なスクリプトです。

このllListen()で有効になったハンドルが、変数handleに入ります。
後述しますが、この番号は使い終わったlistenをOFFにするときに使います。

さて、これでlistenをONにする方法はわかりました。
listenがONになると、listenイベントが発生するようになります。
続けてlistenイベントの処理の仕方を見てみましょう。

listenイベント

llListen()で指定した条件に当てはまる発言があると、listenイベントが発生します。
listenイベントは以下のようになります。

  listen(integer channel, string name, key id, string message){
    //処理
  }

llListen()と同じような引数があります。
簡単に説明すると、

integer channel

発言があったチャンネルです。
llListen()で指定したチャンネル以外はあり得ませんが、場合によってはllListen()を二度使って複数のチャンネルに聞き耳を立てるような使い方をすることがあります。
その際、この引数の値を見て、どこのチャンネルの発言なのかを判断します。

string name

発言者の名前です。
llListen()で発言者を""にしたような場合はあらゆる人・物の発言が聴こえてきます。
例えば私の発言だった場合は、"Miz Cremorne"という名前がこのname変数に入ってきます。

key id

発言者のUUIDです。
llListen()で発言者のUUIDにNULL_KEYを指定するとあらゆる人・物の発言が聴こえてきます。
そのような場合にはこの引数の値を見て、誰の発言なのかを判定するのに使います。

string message

発言内容です。
"red"や"green"や"blue"や"off"など、発言された内容が入ってきます。
今回はこの引数の内容を判定し、照明の色を変化させることになります。

なお、llListen()でチャンネルを0以外に設定している場合、チャット欄からの入力は「/7 red」のようになりますが、このmessageに入ってくるのはあくまでも"red"だけです。
チャンネル指定の「/7」は含まれません。

ここで一度、今回作ろうとしている照明スクリプトのlistenイベントの部分を書いてみましょう。
照明の色を変化させる処理は、前回作ったユーザー関数をそのまま利用します。


  listen(integer ch, string name, key id, string message){
    if (message == "red"){
      light(<1.0, 0.0, 0.0>);
    }else if(message == "green"){
      light(<0.0, 1.0, 0.0>);
    }else if(message == "blue"){
      light(<0.0, 0.0, 1.0>);
    }else if(message == "off"){
      light(ZERO_VECTOR);
    }else{
      llSay(0, "You can use red, green, blue, and off only.");
    }
  }


messageの内容をif文で判定しています。
そしてred,green,blue,offそれぞれに応じてユーザー関数light()を呼び出します。
それ以外の発言だった場合は、red,green,blue,offしか使えない旨のメッセージを表示するようになっています。

このようにlistenイベントを組み込めば、発言に応じて色が変わる照明を作ることができるはずです。

listen使用上の注意

実際にスクリプトにlistenイベントを組み込む前に、注意しなければならないことがもう一点あります。
それはllListen()によってONにした「聞き耳」をOFFにする処理です。

SLでは、常時多くのチャットが飛び交っています。
listenをONにすると、チャットで何か発言があるたびに、スクリプトが動くことになります。
発言者の名前やUUIDを指定していても、発言を処理すべきかどうかの判断が内部的になされますので、どうしてもサーバーの負荷になってしまいます。
サーバー上では複数のSIMが動いていますので、周囲に誰も居ないから・・・と思っても、実は他のSIMで多くの人が遊んでいて、全体的にラグが発生するようなことにもなりかねません。

ですので、listenの使用は最小限に留めるべきでしょう。
使い終わったらきちんとOFFにするのがスクリプトのマナーになります。

OFFにするにはllListenRemove(integer number)という命令を使います。

引数numberには、ハンドルを指定します。
ハンドルとはllListen()を実行したときに返ってくる番号です。

具体的に書いておきます。

  handle = llListen(7, "", llGetOwner(), ""); // listen ON
  llListenRemove(handle); // listen OFF

今回のスクリプトでもきちんとlistenのOFFを書くべきですが、どこに書くのが良いでしょうか?
使い終わったあとにOFFしますので、listenイベントで発言を受け取ったときにOFFにするのが良さそうです。
listenイベントの中で、
  llListenRemove(handle);
の一行を追加しておくと良いでしょう。

しかし・・・。
発言が無かったらどうでしょうか。
listenをONにしたものの、ユーザーが色名を発言せず、無言でどこかに行ってしまったら、listenは健気にいつまでも待ち続けることになってしまいます。
あまりにも哀れで涙がこぼれそうです。

いつまでも待ち続けることがないよう、timerイベントを使いましょう。
例えば、30秒間発言を待ち、発言が無ければlistenをOFFにするような仕組みです。

そこまでやっておけばlistenの負荷対策は十分でしょう。

照明スクリプト改

では、前回のスクリプトにlistenを組み込んでみましょう。
太字が追加部分になります。


integer handle;

light(vector color){
  if (color == ZERO_VECTOR) {
    llSetPrimitiveParams(
      [PRIM_POINT_LIGHT, FALSE, ZERO_VECTOR, 0.5, 3.0, 0.75]
    );
  }else{  
    llSetPrimitiveParams(
      [PRIM_POINT_LIGHT, TRUE, color, 0.5, 3.0, 0.75]
    );
  }
}

default {
  state_entry(){
    light(ZERO_VECTOR);
  }

  touch_start(integer detected){
    if (handle) {
      llListenRemove(handle);
    }

    handle = llListen(7, "", llDetectedKey(0), "");
    llSetTimerEvent(30.0);
  }

  listen(integer ch, string name, key id, string message){
    if (message == "red"){
      light(<1.0, 0.0, 0.0>);
    }else if(message == "green"){
      light(<0.0, 1.0, 0.0>);
    }else if(message == "blue"){
      light(<0.0, 0.0, 1.0>);
    }else if(message == "off"){
      light(ZERO_VECTOR);
    }else{
      llSay(0, "You can use red, green, blue, and off only.");
    }
    llListenRemove(handle);
    llSetTimerEvent(0.0);
    handle = 0;
  }

  timer(){
    llListenRemove(handle);
    llSetTimerEvent(0.0);
    handle = 0;
  }

}


タッチイベントを大幅に改造しています。
誰かがタッチすると、listenをONにしますが、llListen()にUUIDとしてllDetectedKey(0)を指定しています。
llDetectedKey(0)はタッチした人のUUIDを取得する関数です。
つまり、タッチした人の発言に耳を傾けるようになります。
そしてllSetTImerEvent()で30秒間のタイマーをセットしています。

llListen関数を実行する前にif文がありますが、これはlistenの複数起動を防ぐためのものです。
llListenは異なる引数の組み合わせを使って実行すると、新たに「聞き耳」を立てます。
もしも二人の人が立て続けにタッチした場合、二つの「耳」が動き出すことになってしまいます。
ですので、もしもhandleが0でなかったら(=すでにllListenが実行されていたら)llListenRemove()でlistenを閉じています。

タッチした人が発言するとlistenイベントが発生します。
ここで発言に応じてlight()ユーザー関数を呼び出します。
発言を処理したあとは、llListenRemove()でlistenの後片付けをしています。
また、llSetTimerEvent(0.0)でタイマーもOFFにします。

発言のないまま30秒が過ぎるとtimerイベントが起こります。
llListenRemove()でlistenをOFFにし、llSetTimerEvent(0.0)タイマー自体もOFFにします。

今回のポイント

listenのON/OFF:
  handle = llListen(7, "", llGetOwner(), ""); // listen ON
  llListenRemove(handle); // listen OFF

listenイベント:
  listen(integer channel, string name, key id, string message){
    //処理
  }

UUID:
SL内の全アバター・全オブジェクトに割り振られているユニークなID。
テクスチャーやサウンドなどのデータにもUUIDが付けられている。
「存在しないもの」はNULL_KEY(="00000000-0000-0000-0000-000000000000")。

チャットチャンネル:
通常チャットは0チャンネル。
0以外のチャンネルで発言する際には「/7 red」のように発言する。

ぜひマスターしていただきたいのはlistenの使い方です。
タッチイベントと並んで最も使用頻度の高いイベントですので、使いこなせるようにしておきましょう。

さて、これで何度もタッチすることなく色を変えられる照明ができました。
ですが今度は、いちいちチャットで発言しなければならないのが面倒に思えてきますw
もっと簡単なオペレーションで、照明の色を自由に変えられるようにはできないでしょうか?

・・・それはまた次回の課題にしたいと思います。
初級スクリプト