衝突判定するスクリプト
オブジェクトの衝突についてです。
物理オブジェクトを扱いだすと、いろいろな場面で衝突判定をしたりするのですが、通常のオブジェクトにおける衝突判定も実は結構有用です。
衝突・・・なんていうと猛スピードで激突するイメージかもしれませんが、lslで言う衝突はもっと軽いものも含みます。
例えばアバターが歩いてきてオブジェクトを踏んだとき、それだけで衝突のイベントが起きるのです。
自動ドアの前にマットを用意しておいて、それを踏んだらドアが開く、という古典的な仕掛けが実現できます。
実際に衝突を使った自動ドアを作ってみましょう。
衝突イベントには3種類あります。
いや、正確には6種類ですが、まずは基本の3つからで良いでしょう。
collision_start(integer num_detected){
// 衝突が始まったときの処理
}
collision(integer num_detected){
// 衝突し続けているときの処理
}
collision_end(integer num_detected){
// 衝突が終わったときの処理
}
タッチイベントと似ています。
実際、それぞれのイベントの意味は3つのタッチイベントと同じです。
通常使うのはcollision_startイベントです。
ぶつかった瞬間に発生するイベントですので、単発的に「ぶつかったとき」の処理をするにはこれを使います。
引数num_detectedは「衝突している数」を意味します。
このあたりもタッチイベントと同様です。
具体的に「誰/何が衝突したのか」「衝突した位置はどこか」「衝突したもののUUIDは何か」などは、先日センサーのときに出てきたDetected系の関数を使って取得します。
例えば衝突したものの名前を得るにはllDetectedName()関数、UUIDを得るにはllDetectedKey()です。
それほど難しくないと思いますので少々余談をします。
これらの衝突イベントは、ファントム・オブジェクトでは通常発生しません。
ファントム・オブジェクトとは、buildツールの「オブジェクト」タグで「ファントム」にチェックが入っているオブジェクトのことです。
そこにあるように見えますが、ぶつかろうとするとスルリと通り抜けてしまうオブジェクトですね。
要するに「幻」であるため、衝突イベントも起こらないようになっているのですが、時にはファントム・オブジェクトでも衝突判定を行いたい場合があります。
例えば目に見えないファントム・オブジェクトを特定位置に配置しておいて、そこを誰かが通ったときに何か仕掛けを動かしたい、なんて時ですね。
その場合は、以下の関数を使います。
llVolumeDetect(integer detect);
引数にはTRUEかFALSEを指定します。
TRUEを指定すると、ファントム・オブジェクトで衝突イベントが起こるようになります。
FALSEにすると衝突イベントが起こらなくなります。
この関数はルートプリム内のスクリプトで実行しなければなりません。
ただし、この方法で発生するのはcollision_startイベントとcollision_endイベントのみです。
ファントムオブジェクトでcollisionイベントを発生させることはできませんので注意して下さい。
また、ファントムではないオブジェクトでllVolumeDetect関数を使うと、オブジェクトがファントムになってしまいます(^^;
余談はこのくらいで・・・。
では自動ドアを作ってみましょう。
前に作ったドアを再利用してもいいですが、衝突判定するためのマット等を追加して下さい。
ルートプリムはマットに設定します。
今回はドアの部分だけをスライドさせますが、複数の子プリムをまとめて動かすことはできません。
それぞれをバラバラに動かしますので、あまりプリムの数が多いと動きが怪しくなります。
こんな感じのドアがベストではないでしょうか。

ではスクリプトです。
ルートプリムに仕込むメインのスクリプトと、子プリムに仕込むスクリプトの二種類があります。
メインスクリプトは衝突判定を行い、子プリムのスクリプトは開く/閉じる動作を行います。
スクリプト間の通信は、以前使ったllMessageLinked関数を使います。
まずはメインのスクリプトから。
default {
collision_start(integer num_detected){
llMessageLinked(LINK_SET, 1, "", NULL_KEY);
}
}
いいのかこれだけで・・・って感じですが(^^;
ルートプリム(マット部分)に仕込むメインスクリプトは、あくまでも衝突の判定だけを行い、誰かがぶつかったらリンクメッセージを飛ばすだけです。
これだけですとアバターだけでなく、オブジェクトがぶつかっただけでも反応しますので、アバター限定にするのであれば、
このようにします。
llDetectedType()関数は衝突したのが何なのか(アバター?オブジェクト?)を取得するための関数です。
これに「& AGENT」と付けてやることで、アバターかどうかの判定ができます。
続いて子プリム(ドア部分)のスクリプトです。
vector close_pos;
vector open_pos = <0.0, 1.0, 0.0>;
integer opened = FALSE;default{
state_entry(){
close_pos = llGetLocalPos();
}link_message(integer sender, integer num, string str, key id) {
if (!opened){
llSetPos(close_pos + open_pos);
opened = TRUE;
}
llSetTimerEvent(5.0);
}timer() {
if (opened){
llSetPos(close_pos);
opened = FALSE;
}
llSetTimerEvent(0.0);
}
}
こちらも複雑ではないですね。
前に作ったセンサーのドアからの改造です。
センサーのときは、誰かを検知したらオープン、誰も検知しなかったらクローズにしていましたが、今回はリンクメッセージを受け取ったらオープンし、最後にメッセージを受け取ってから5秒たつと自動的にクローズします。
また、ステートエントリー内で「閉まったときの位置」を取得している部分に注意して下さい。
センサーのときはここでllGetPos()を使っていましたが、今回はllGetLocalPos()を使っています。
llGetLocalPos()関数は、子プリムの相対位置を取得するための関数です。
子プリムでllSetPos()を使うときには、相対位置での指定になりますので、close_posに絶対位置を保持してしまうとおかしなことになります。

最終回にして説明する基本的なことですが(^^;
相対座標と絶対座標とは、上記のような関係です。
llSetPos()はルートプリムで使うときには絶対座標を使いますが、子プリムで使うときには相対座標です。
今回は子プリムをllSetPos()で動かしますので、相対座標を使わなければなりません。
ですので、絶対座標50,25,21ではなく、相対座標0,0,1のほうを取得しているのです。
・衝突イベント:
collision_start(integer num_detected){
// 衝突が始まったときの処理
}
collision(integer num_detected){
// 衝突し続けているときの処理
}
collision_end(integer num_detected){
// 衝突が終わったときの処理
}
| 関数 | ルート | 子プリム |
|---|---|---|
| llSetPos | 絶対座標 | 相対座標 |
| llGetPos | 絶対座標 | 絶対座標 |
| llGetLocalPos | 絶対座標 | 相対座標 |
これだけでもややこしいかもしれませんが、アタッチメントになるとさらに複雑ですw
| 関数 | ルート | 子プリム |
|---|---|---|
| llSetPos | アバターとの相対座標 | ルートとの相対座標 |
| llGetPos | アバターの絶対座標 | ルートとの相対座標 |
| llGetLocalPos | アバターとの相対座標 | ルートとの相対座標 |
2007/08/9

