BlackSheep-LSL@Wiki

鍵をかける

最終更新:

匿名ユーザー

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

はじめに


やもさんより質問いただいたスクリプトについて解説です。

[やもさんからの質問]
最近自分で服を作るようになり試着の為の更衣室を用意したのですが
ドアに鍵をかけるか、自分にしか開けられないドアするにはどのようにしたら良いのでしょうか?

これって結構ニーズのある話だと思います。
着替えの最中は裸になることも多く、やはり人に見られたくない場合がありますので・・・。

また、Kirさんからも関連のある質問をいただきました。

[Kirさんからの質問]
なんとか、オーナー限定にて動作するように改良したいのですが、

 if (llDetectedKey(0) == llGetOwner()){
   handle=llListen(channel, "", llGetOwner(), "");

これでしょうか?

あわせて見ていきましょう。

鍵をかけることの意味


実際には「人に見られることのない場所」を作るのは結構大変だったりします。
というのも、SLではかなり自由なカメラのコントロールが利きますので、建物をALT押しながら左クリックしてドラッグすると、簡単に内部を覗けてしまうんですね(^^;
たとえドアに鍵をかけたとしても、これを防ぐことはできません。

覗き見だけでなく、内部に侵入することも可能です。
カメラを動かして建物内部を見て、中の家具などにsitすると、強引に室内に押し入ることができたりします。
これを防ぐにはsitしたときの強制排除の仕組みを建物や家具に組み込んでおくしかありません。
llSitTaeget?でsit位置を屋外に設定してしまうとかですね(^^;

完璧なセキュリティを考え始めると、かなり頭を使うことになります。
どこかのSIMに、セキュリティを売りにしているラブホテルがあるとか聞きましたが、そこも相当工夫をしているはずです。
現物を見てないのでどこまでセキュリティを実現しているのかはわかりませんが・・・。

個人的には「絶対に誰にも覗かれない場所」というのは不可能に近いと思っています。
コンピュータの世界に「完璧なセキュリティ」が存在しないのと一緒です。
あくまでも「悪意を持って故意に覗こうとしない限りは、覗かれずに済む」程度のプライバシーであれば確保できます。

以上のようなことを前提とした上で、他の人には開けられないドア及び鍵のかかるドアについて見てみましょう。

操作できる人を限定する方法


自分にしか開けられないドアを作るのは簡単です。
前にタッチして開くドアを作りましたが、そのスクリプトのタッチイベントにコードを追加し、オーナーがタッチしたときだけ反応するようにすればOKです。

rotation rot;
vector open_rot = <0.0, 0.0, 90.0>;

default {
  state_entry()
  {
    rot = llGetRot();
    state close;
  }
}

state open {
  state_entry()
  {
    llSetRot(rot * llEuler2Rot(open_rot * DEG_TO_RAD));
    llSetTimerEvent(30.0);
  }
  
  touch_start(integer total_number){
    if (llDetectedKey(0) == llGetOwner()) { // タッチしたのがオーナーの場合のみ
      state close;
    }
  }

  timer(){
    llSetTimerEvent(0);
    state close;
  }
}

state close {
  state_entry(){
    llSetRot(rot);
  }

  touch_start(integer total_number){
    if (llDetectedKey(0) == llGetOwner()) { // タッチしたのがオーナーの場合のみ
      state open;
    }
  }
}

llDetectedKey(0)は「タッチした人のUUID」を得る関数です。
llGetOwner()のほうは「オーナーのUUID」を得る関数ですので、これが等しい場合だけ反応するようにしておけば、オーナー以外には操作ができません。

ドアに限らず、「オーナーがタッチしたときにだけ動作するスクリプト」は、

touch_start(integer total_number){
  if (llDetectedKey(0) == llGetOwner()) {
    // オーナーがタッチしたときの処理
  } else {
    // オーナー以外がタッチしたときの処理
  }
}

このような書き方になります。
しばしば使う表現ですので、これはもう丸暗記でもいいかと思います。

この手法をもうちょい応用してみましょう。
オーナーだけでなく、同じグループの人が操作できるようにするには?

touch_start(integer total_number){
  if (llDetectedGroup(0) ) {
    // オブジェクトと同じグループの人がタッチした場合の処理
  } else {
    // オブジェクトと同じグループ以外の人がタッチした場合の処理
  }
}

llDetectedGroup関数は、オブジェクトのグループと同じアクティブグループかどうかを調べる関数です。

さらに応用して、スクリプトに操作を許可する人の名前を定義しておいて、その人以外には使えないようにするには、

list agents=["Miz Cremorne", "Hoge Hogera", "Honya Honyara"];
rotation rot;
vector open_rot = <0.0, 0.0, 90.0>;

(中略)

state open {
(中略)
  touch_start(integer total_number){
    if (llListFindList(agents, [llDetectedName(0)]) != -1) { // リストagentsに名前のある人のみ
      state close;
    }
  }
(以下略)

list型変数agentsに、タッチを許可する人の名前を列挙しておきます。
llListFindList関数はリストの中から指定した要素を探してそのインデックスを返す関数ですが、リストに無かった場合は-1を返します。
つまりリストに名前があるなら、-1以外の数字が返ってきます。
ですので-1以外が返ってきた場合だけ、動作させればOKですね。

ここのところを逆に-1のときだけ動作するようにすると、

if (llListFindList(agents, [llDetectedName(0)]) == -1) { // リストagentsに名前のない人のみ
  // 処理
}

BANのように、リストに名前のある人には動かすことのできないスクリプトになります。

鍵をかける


鍵をかける仕組みは「操作できる人を限定する方法」の応用に過ぎません。
上で紹介したものはどれも「操作できる人」をあらかじめ限定していましたが、ロックしていないときは誰でも操作できて、ロックしたときだけ「操作できる人」が限定されれば良いですね。

rotation rot;
vector open_rot = <0.0, 0.0, 90.0>;
key lock=NULL_KEY;
integer counter=0;

default {
  state_entry()
  {
    rot = llGetRot();
    state close;
  }
}

state open {
  state_entry()
  {
    llSetRot(rot * llEuler2Rot(open_rot * DEG_TO_RAD));
    llSetTimerEvent(30.0);
    lock = NULL_KEY; // ロック解除
  }
  
  touch_start(integer total_number){
    state close;
  }

  timer(){
    llSetTimerEvent(0);
    state close;
  }
}

state close {
  state_entry(){
    llSetRot(rot);
  }

  touch_start(integer detected){
    counter=0;
  }

  touch(integer detected){
    if (llDetectedKey(0) == llGetOwner()){ // ロックできるのはオーナーだけ
      if (counter < 50){
        counter ++;
      }else if (counter == 50){
        counter ++;
        if (lock == NULL_KEY){
          lock = llDetectedKey(0); // ロック
        } else {
          lock = NULL_KEY; // アンロック
        }
      }
    }
  }

  touch_end(integer total_number){
    if (lock == NULL_KEY){ // ロックがかかっていない場合
      state open;
    } else if (llDetectedKey(0) == lock) { // ロックをかけた人がタッチした場合
      if (counter < 50){
        state open;
      }
    }
  }
}

ステートopenのときには誰もが操作可能です。開いてるのですから、閉じるのはいくらでもできるわけです。
ステートcloseのときにロックの仕組みを追加しました。

しばらくドアをクリックしっ放しにするとロックがかかります。
touch?イベントを使って「しばらくマウスボタンを押し続けたとき」の処理の仕方は色の変わるカツラを作ったときに使いました。
今回は「しばらくマウスボタンを押し続けたとき」に、ロックがかかっていなければロックし、ロックがかかっていればアンロックしています。

ロックがかかっているかどうかはkey型変数lockの中身で判断します。
lockがNULL_KEYの場合はロックがかかっていない状態です。
ロックがかかったときには、ロックした人のUUIDが変数lockに入ります。

ドアを開けることができるのは、
(1)ロックがかかっていない場合=lockがNULL_KEY
(2)ロックした人がタッチした場合=lockがllDetectedKey(0)と同じ
の2通りですので、touch_endイベントの中でそのように判断しています。

このスクリプトではロックできるのはオーナーだけにしていますが、touch?イベントの中の最初のif文に手を加えてやれば、グループメンバーが自由にロック/アンロックできるドアなども出来ます。

強制排除スクリプト


ついでに、sitによる侵入を防ぐコードも考えてみます。
せっかくドアには鍵かかかっているのに、sitで侵入されては無念ですので(^^;

vector exit_pos = <128.0, 128.0, 128.0>;

default {
  state_entry(){
    vector p = exit_pos - llGetPos();
    p /= llGetRot();
    llSitTarget(p, ZERO_ROTATION);
  }
  
  on_rez(integer parm){
    llResetScript();
  }
  
  moving_end(){
    llResetScript();
  }
  
  changed(integer change){
    if (change & CHANGED_LINK){
      key avatar = llAvatarOnSitTarget();
      if (avatar != NULL_KEY){
        llUnSit(avatar);
      }
    }
  }  
}  

変数exit_posには強制退去される先の座標を設定します。
上記のコードでしたら、sitした人は全員<128.0, 128.0, 128.0>へ飛ばされます。
ただし飛ばせる距離は300m程度ですので、あまり離れたところに退去させるのは無理です。

オブジェクトがrezされたとき、あるいは動かされたときにはスクリプトをリセットし、退去位置を再設定します。

誰かがこのスクリプトの入ったオブジェクトに座ると、退去位置に移動しますので、即座にllUnSit?で排除します。

このコードは全primに入れておく必要はありません。
例えば建物がリンクされているのであれば、ルートにさえ入れておくだけで済みます。

  • 親切丁寧な解説で、初心者の私にも解りやすく、ありがたく思っています。しかし、強制排除スクリプトi -- Zephyr Whitfield (2011-05-15 21:33:04)
  • 投稿ミスをしました。済みません。もう一度書き直します。親切丁寧な解説で、初心者の私にも解りやすく、ありがたく思っています。しかし、強制排除スクリプトにllDetectedKey()を使用して10m以内に近付いたAvatarを排除するスクリプトを作ろうとしましたが、その組み合わせ方が分かりません。サンプルを掲載して頂ければ嬉しいのですが、お願いできませんでしょうか? m(_ _)m -- Zephyr Whitfield (2011-05-15 21:39:05)
名前:
コメント: