回転(非物理)
LSLの中でもかなり悩むのが、回転の制御です。
回転には、建造モードでおなじみのオイラー角の他に、ラジアン角と四元数があり、どれも同じ角度でも表記が違います。
残念ながら、私は数学の専門家では絶対ありませんので、それぞれの表現についての詳しいことは各自でお調べください…。
ここではLSLによる回転制御をを見ていきましょう。
イベント
at_rot_target(integer tnum, rotation targetrot, rotation ourrot) { 内容; }
オブジェクトが到達目標角度に達した時に発動します。
引数:
integer tnum
llRotTargetの管理番号が入ってきます。
複数のllRotTargetが設定されている時、どの関数の条件が一致したのかを調べることができます。
rotation targetrots
llRotTargetで指定した目標角度と同じ角度が入ってきます。
rotation ourrot
オブジェクトの現在の角度が入ってきます。
integer targetID; default { state_entry() { targetID = llRotTarget(<90, 0, 0, 1>, 1 * DEG_TO_RAD); } at_rot_target(integer tnum, rotation targetrot, rotation ourrot) { llSay(0,"目標角度です"); } }
オブジェクトを目的の角度に動かした時にイベントが発生します。
必ずllRotTargetと組みで使います。
not_at__rot_target()
{
内容;
}
オブジェクトが到達目標角度に達していない時に発動します。
引数:
なし
integer targetID; default { state_entry() { targetID = llRotTarget(llGetRot(), 1 * DEG_TO_RAD); } not_at_rot_target() { llOwnerSay("設置角度が変わりました"); llRotTargetRemove(targetID); } }
llRotTargetで指定した目標角度にオブジェクトがない時に、連続でこのイベントが発生します。
そのため、うっかり使うとサーバーに負荷なだけでなく、Say系の発言を行うと画面中に発言が連続で流れてしまいます。
用途としては、動かしてはならないものが動かされた場合に警告を出す、くらいでしょうか。
関数
integer llRotTarget(rotation rot, float errot);
オブジェクトの到達目標角度を設定します。
戻り値:
integer 変数
llRotTargetの管理番号を変数で指定します。
引数:
rotation rot
目標角度をローテーション型で指定します。
float errot
目標角度の誤差を小数型で指定します。
単位はラジアンです。
integer targetID; default { state_entry() { targetID = llRotTarget(<90, 0, 0, 1>, 1 * DEG_TO_RAD); } at_rot_target(integer tnum, rotation targetrot, rotation ourrot) { llSay(0,"目標角度です"); } }
オブジェクトに目標角度を決めたい時に使います。
例えば、あるキーアイテムを決まった角度に回すと仕掛けが動く、などのゲーム的要素でしょう。
llRotTargetRemove(integer number);
オブジェクトの到達目標角度を解除します。
戻り値:
なし
引数:
integer number
llRotTargetの管理番号を変数で指定します。
integer targetID; default { state_entry() { targetID = llRotTarget(<90, 0, 0, 1>, 1 * DEG_TO_RAD); } at_rot_target(integer tnum, rotation targetrot, rotation ourrot) { llSay(0,"目標角度です"); llRotTargetRemove(targetID); } }
必要が無くなったllRotTargetを解除します。
特に、not_rot_at_targetを設定した場合は必ず使用しましょう。
rotation llGetRot();
現在のプリムの角度を調べます。
戻り値:
rotation 変数
角度をローテーション型で返します。
引数:
なし
default { touch_start(integer total_number) { rotation rot; rot = llGetRot(); llSay(0,(string)rot); } }
非常に基本的かつ重要な関数です。
ここで調べられるのは、それぞれのプリム角度です。
ただし装備品ではアバターの角度になります。
rotation llGetLocalRot();
現在の子プリムの角度を調べます。
戻り値:
rotation 変数
角度をローテーション型で返します。
引数:
なし
default { touch_start(integer total_number) { rotation rot; rot = llGetLocalRot(); llSay(0,(string)rot); } }
非常に基本的かつ重要な関数です。
ここで調べられるのは、リンクオブジェクトのルートプリムからの相対角度です。
また、ルートプリムや単体プリムで使った場合は、例外的にllGetRotと同じです。
ただし装備品ではアバターからの相対角度になるので注意してください。
アバターには装着位置がたくさんありますが、なぜか、それぞれの装着位置の角度を調べることができません。
また、あくまで相対角度のため、例えばルートプリムからXが90度回転した子プリムでは、装着位置がどこだろうと、常に角度はXが90度です。
rotation llGetRootRotation();
現在のオブジェクトのルートプリムの角度を調べます。
戻り値:
rotationr 変数
角度をローテーション型で返します。
引数:
なし
default { touch_start(integer total_number) { rotation rot; rot = llGetRootRotation(); llSay(0,(string)rot); } }
ここで調べられるのは、リンクオブジェクトのルートプリムの角度です。
ただし装備品ではアバターの角度になります。
llGetRotに比べると正確にアバターの角度を調べるようですが、その誤差は微々たるものです。
llSetRot(rotation rot);
プリムの角度を設定します。
戻り値:
なし
引数:
rotation rot
角度をローテーション型で指定します。
default { touch_start(integer total_number) { rotation rot; rot = llGetRot(); llSetRot(rot * <45, 0, 0, 1>); } }
非常に基本的かつ重要な関数です。
オブジェクトに新たな角度を絶対角度で指定します。
位置を表すベクター型と角度を表すローテーション形では、計算方法が違います。
ベクター型では、普通にベクター同士、またはそれぞれの成分で、足し算や引き算で増減を行います。
しかし、ローテーション型では、ローテーション同士、またはそれぞれの成分において、掛け算または割り算で増減を行います。
なぜそうなるのかは、四元数の専門で調べるとして、とりあえず掛け算と割り算で増減と覚えてください。
また、ローテーション型では左辺と右辺を入れ替えると結果が変わります。
具体的には、「元の角度を左辺、増減したい角度を右辺、で計算する」と絶対角度、「増減したい角度を左辺、元の角度を右辺、で計算する」と、ローカル角度で回転します。
これは覚えておかないと、いつまで経っても思ったように回転しないことになります(私も丸一日悩んだことがあります)。
llSetLocalRot(rotation rot);
子プリムの角度を設定します。
戻り値:
なし
引数:
rotation rot
角度をローテーション型で指定します。
default { touch_start(integer total_number) { rotation rot; rot = llGetLocalRot(); llSetLocalRot(rot * <45, 0, 0, 1>); } }
非常に基本的かつ重要な関数です。
子プリムに新たな角度を相対角度で指定します。
ベクターとは違い、ローテーションにはローカル指定の角度設定があります。
ローカルなので、元の角度をllGetLocalRotで取得する必要があります。
計算方法については、llSetRotと同じなので、注意が必要です。
llTargetOmega(vector axis, float spinrate, float gain);
オブジェクトをスムーズに回転させ続けます。
戻り値:
なし
引数:
vector axis
回転の軸をベクター型で指定します。
単位ベクトルなので、各成分は1を指定すれば十分です。
float spinrate
回転速度を小数型で指定します。
速さはラジアン/秒になります。
float gain
物理オブジェクトのみ作用する回転の強さを指定します。
非物理では1のままです。
default { state_entry() { llTargetOmega(<0, 0, 1>, TWO_PI, 1); } }
回転看板などでお馴染みのオメガ回転です。
物理で使うことはほとんどなく、その場でクルクルと回転させる看板の用途がメインになります。
回転処理は完全にクライアント処理なので、サーバー上ではオブジェクトは静止したままとなります。
そのため、最も負荷が低い回転命令となります。
また、個々のクライアント依存なので、その見え方は人によって違う事を覚えておく必要があります。
回転速度はラジアン単位のため、2π(TOW_PI)を指定すると1秒間に1回の回転になります。
リンクオブジェクトの場合、ルートプリムにこれを入れるとオブジェクト全体が、子プリムに入れるとその子プリムのみが回転します。
この関数はllSetTextと同じく、スクリプトを消去しても効果は消えません。
そのため、止めるにはllTargetOmega(<0, 0, 0>, 0, 1);と書き直す必要があります。
rotation llEuler2Rot(vector vec);
オイラー角を四元数に変換します。
戻り値:
rotation 変数
指定したベクター型角度がローテーション型に変換されて入ってきます。
引数:
vector vec
変換したいベクター型角度を指定します。
default { touch_start(integer total_number) { rotation rot = llGetRot(); rot = rot * llEuler2Rot(<90, 0, 0> * DEG_TO_RAD); llSetRot(rot); } }
非常に基本的かつ重要な関数です。
馴染みのあるベクター型オイラー角を、直感でわかりにくい四元数に変換します。
これにより、建造モードと同じ感覚で角度指定ができるようになります。
ただし、指定するベクター型がラジアン角でなければなりません。
ラジアン角は180度を1πとする表現方法で、1度=約0.017453ラジアンです。
もちろんこれではお手上げなので、ラジアンに変換する定数DEG_TO_RADを掛けてから指定します。
llEuler2Rot(<○, ○, ○> * DEG_TO_RAD);という書き方は、角度指定をする時の決まり文句として覚えましょう。
vector llRot2Euler(rotation q);
四元数をベクター型に変換します。
戻り値:
rotation 変数
指定したローテーション型角度がベクター型に変換されて入ってきます。
引数:
vector vec
変換したいローテーション型角度を指定します。
default { touch_start(integer total_number) { rotation rot = llGetRot(); vector vec; vec = llRot2Euler(rot)*RAD_TO_DEG; llSay(0,(string)vec); } }
非常に基本的かつ重要な関数です。
直感でわかりにくい四元数を、馴染みのあるベクター型に変換します。
これにより、現在のオブジェクトの角度を、建造モードと同じ感覚で確認できるようになります。
ただし、変換後のベクター型がラジアン角になっています。
ラジアン角は180度を1πとする表現方法で、1度=約0.017453ラジアンです。
もちろんこれではお手上げなので、ラジアンをオイラー角に変換する定数RAD_TO_DEGを掛けてやります。
llRot2Euler(<○, ○, ○, ○>) * RAD_TO_DEG;という書き方は、角度を確認する時の決まり文句として覚えましょう。
回転スクリプト サンプル
rotation close_rot; vector open_rot = <0, 0, 90>; default { state_entry() { close_rot = llGetRot(); } moving_end() { close_rot = llGetRot(); llOwnerSay((string)close_rot); } touch_start(integer total_number) { rotation set_rot = llEuler2Rot(open_rot * DEG_TO_RAD); llSetRot(close_rot * set_rot); llSetTimerEvent(10.0); } timer() { llSetTimerEvent(0.0); llSetRot(close_rot); } }
タッチをすると90度回転するスクリプトです。
moving_endを使って、設置場所が変わってもタッチ後の角度は設置角度+90度です。
ただし、角度だけ変えた場合ではmoving_endが反応しないことにも注意してください。


