BlackSheep-LSL@Wiki

センサーを使おう

最終更新:

mizcremorne

- view
メンバー限定 登録/ログイン

はじめに


今回はセンサーについて見てみましょう。
センサーというのは、スクリプトによって近くのアバターやオブジェクトを検知する仕組みのことを言います。
最も単純な使い方としては、自動ドアなどが考えられます。

また、翻訳機などでは近くにいる人をダイアログに出力し、誰の言葉を翻訳するか指定できるようにしているものもあります。
このとき近くにいる人を特定するのにセンサーが使われています。

センサーの仕組み


センサーは、探知を開始する関数と、探知結果を受け取るイベントで成り立っています。
探知を実行する関数は二種類あり、一度だけ探知を行うものと一定間隔で繰り返し探知し続けるものがあります。
それから繰り返し探知を行っているセンサーを止めるための関数もあります。

センサー関数 説明
llSensor 一度だけ探知を実行する
llSensorRepeat 繰り返し探知を実行する
llSensorRemove 探知を止める

一度のみの探知、繰り返しの探知のどちらの関数を使っても、結果を受け取るイベントには同じものを使います。
イベントにも二種類あり、一つは「何か見つかったとき」のイベント、もう一つは「何も見つからなかったとき」のイベントです。

センサーイベント 説明
sensor 何か見つかったときのイベント
no_sensor 何も見つからなかったときのイベント

#ref error :画像を取得できませんでした。しばらく時間を置いてから再度お試しください。

センサー関数


まずは探知を実行する関数から具体的に見てみましょう。

llSensor(string name, key id, integer type, float range, float arc)


この関数は指定した条件での探知を一度だけ実行します。
探知条件を指定する引数について順番に説明します。

  • string name

探知対象の名前です。
例えば「Miz」というアバターやオブジェクトを探したいときは、ここに"Miz"を指定します。
大抵の場合、探知対象の名前が分かっていることは少なく、名前を指定することはあまりありません。
空文字""を指定すると、どんな名前のアバターやオブジェクトであろうと探知対象になります。

  • key id

探知対象のUUIDです。
名前と同様、UUIDがわかってて探知する機会はそれほど多くはありません。
使うとしたら、机の中やカバンの中を探しても見つからなかったものを探すときくらいでしょう。
NULL_KEYを指定すると、全てのUUIDが探知対象になります。

  • integer type

探知対象のタイプです。
どんなタイプのものを探すのか、ここで指定します。
と言っても、髪の長い子、とか、性格の穏やかな子、などの詳細な指定は残念ながらできません。
指定できるのは以下の4タイプのみです。
タイプ 説明
AGENT アバター
ACTIVE 移動している物理オブジェクト/またはアクティブなスクリプト(*1)を含むオブジェクト
PASSIVE パッシブなスクリプト(*2)を含むオブジェクト/または移動していない物理オブジェクト/またはその他の全オブジェクト
SCRIPTED なんらかのスクリプトを含むオブジェクト

*1:アクティブなスクリプトとは、listenのように比較的高負荷な常に動作を続けているスクリプトのこと
*2:パッシブなスクリプトとは、listenなどを含まない比較的低負荷なスクリプトのこと

このあたりのタイプ分けがイマイチどういう意図に基づいているのかわからないのですが、lslWikiによれば上記のようになっております。
上記4つのタイプですが、組み合わせて使うことも可能です。
組み合わせるときには「AGENT | ACTIVE」のように、|でつなぎます。

余談ですが、タイプSCRIPTEDを絡めて使った場合、どうもセンサーの動きが不可解であることは識者の間では有名な話です。
例えば「AGENT | SCRIPTED」という組み合わせは、アバターとスクリプトを含むオブジェクトの双方を探知してくれそうなものですが、どういうわけかアバターが探知されないそうです。
組み合わせ時のセンサーの動きについては、学会でも諸説あり、明確な答えが出ていない状態です。
余裕のある方は自分で研究してみるのも良いでしょう。
余裕のない方は、物好きな人が検証結果を発表してくれるのを待ちましょう。

  • float range

探知の範囲です。
半径を指定します。
例えば5.0であれば半径5m以内が探知対象になります。

  • float arc

探知角度です。X軸に対するラジアン角度になります。
最大はπ(=180度)です。
わかりにくいのでlslWikiから引っ張ってきた以下の図を参考にして下さい。

arc=π/4(45度)の場合:
#ref error :画像を取得できませんでした。しばらく時間を置いてから再度お試しください。

arc=π/2(90度)の場合:
#ref error :画像を取得できませんでした。しばらく時間を置いてから再度お試しください。

arc=π(180度)の場合:
#ref error :画像を取得できませんでした。しばらく時間を置いてから再度お試しください。

センサーの具体的な使い方を示しておきます。
例えば全方位半径5m以内のアバターを探知する場合は、

 llSensor("", NULL_KEY, AGENT, 5.0, PI);

このようになります。

次に、繰り返し探知を行う関数llSensorRepeat()ですが、llSensor()関数に引数が一つ増えているだけです。

llSensorRepeat(string name, key id, integer type, float range, float arc, float rate)


増えているfloat型の引数rateは、探知を繰り返す周期を秒数で指定するものです。
例えば1.0を指定すると、1秒ごとに探知を繰り返します。

 llSensorRepeat("", NULL_KEY, AGENT, 5.0, PI, 1.0);

上記は全方位半径5m以内のアバターを1秒ごとに探知します。

llSensorRepeat()による探知を止めるには、llSensorRemove()を使います。

llSensorRemove();


引数はありません。

センサーイベント


センサーが何かを探知すると、sensor?イベントが起きます。

 sensor(integer num_detected){
   // 処理
 }

引数num_detectedには、探知したものの数が入ります。
例えば3人のアバターが探知された場合は3です。

「それだけじゃ何が何やらわからんじゃないか!」と思った方、ごもっともです。

探知した結果、名前や位置、UUIDなどが知りたいのは素直な気持ちでしょう。
探知された対象の情報を取得するためには、Detected系と呼ばれる関数を使います。
実は今までにもコレ使ってるんですよね。
タッチイベントの中でよく使っているllDetectedKey()関数などがそうです。

いくつか挙げてみましょう。
これらの関数はいずれも引数にはinteger型の数値を取ります。
「何番目の対象か」を指定する引数です。

Detected系関数 説明
llDetectedKey UUIDを取得
llDetectedName 名前を取得
llDetectedPos 位置を取得
llDetectedRot 回転角度を取得
llDetectedGroup グループが同じかどうか確認
llDetectedType タイプ(AGENT/ACTIVE/PASSIVE/SCRIPTED)を取得

他にもありますが、使用頻度の高いものは名前とUUIDでしょう。
例えば以下のようにすると、センサーで探知したもの全ての名前を連呼します。

 sensor(integer num_detected){
   integer i;
   for (i = 0; i < num_detected; i++){
     llSay(0, "I found " + llDetectedName(i) + "!");
   }
 }

一方、何も見つからなかった場合のno_sensor?イベントは非常にシンプルです。

 no_sensor(){
   // 処理
 }

引数はありません。

自動ドアスクリプト


さて、センサーを使ってスクリプトを作ってみましょう。
安直に自動ドアです(^^;

前に作ったドアと異なり、横にスライドして開くタイプのドアにします。
どんなものでもいいのですが、テストの際にはドアオブジェクトを用意して下さい。

vector close_pos;
vector open_pos = <0.0, 1.0, 0.0>;
integer opened = FALSE;

default{
  state_entry(){
    close_pos = llGetPos();
    llSensorRepeat("", NULL_KEY, AGENT, 5.0, PI, 3.0);
  }
  
  sensor(integer total_number) {
    if (!opened){
      llSetPos(close_pos + open_pos);
      opened = TRUE;
    }
  }

  no_sensor() {
    if (opened){
      llSetPos(close_pos);
      opened = FALSE;
    }
  }
}

グローバル変数として、ドアの閉じたときの位置をclose_posに保持しています。
開いたときの位置は、close_posにopen_posを加算したものです。
この例ではY方向に1mスライドするようになっています。
opendはboolian型として使う「開いてるか/閉じてるか」を判定する変数です。

スクリプトが開始されると同時に、llSensorRepeat()関数を使い、全方位5m以内のアバターを3秒ごとに探知しています。
誰か見つかった場合は、llSetPos?()関数を使い、ドアをスライドさせます。
誰も見つからなかった場合は、llSetPos?()関数を使って、ドアを元の位置に戻します。

なお、スクリプトを組み込んだあとにドアの位置を手動で調整した場合は、一度スクリプトをリセットしないとまた元の位置に戻ってしまいますので注意して下さい。

今回のポイント


  • センサー関数:

 llSensor(string name, key id,
  integer type, float range, float arc); // 一度だけ探知

 llSensorRepeat(string name, key id,
  integer type, float range, float arc,
  float rate); // 繰り返し探知

  • センサーの停止:

 llSensorRemove();

  • 何か見つかったときのイベント:

 sensor(integer num_detected){
   // 処理
 }

  • 何も見つからなかったときのイベント:

 no_sensor(){
   // 処理
 }

センサーは面白い機能ですが、サーバーには負荷がかかります。
例に出した自動ドアスクリプトでは、センサーの繰り返し間隔を3秒と少々長めに設定していますが、なるべく負荷を減らすための処置です。
とは言っても、常にセンサーが動き続けることになりますので、多用するとラグを引き起こす原因になります。

正しいセンサーの使い方としては、常に探知をするのではなく、必要なときにのみセンサーON、使わなくなったらすぐにOFFするべきです。

・・・・・・そうなると自動ドアとしては役に立たなくなってしまいますが(^^;
記事メニュー
目安箱バナー