2009年01月06日
キーコントロール
パーミッションをやったところで、ついでにキーコントロールをやってみようか。
あんまり使う機会はないかなぁ。
と言うか、作る機会はあまりないかな、使う機会はそこそこあるか。
だって乗り物のアレだもんね。
ちょうど1年前に装備型の飛行機作ったっけ。
あの時はまだLSL始めたばかりで、何が何やらさっぱり解らなかったっけな。
装備型なのになんでキーコントロールか。
だってアニメーション連動型だったから。
まぁつまり、前回と今回の内容をやってたわけだ。
キーコントロールを行うなら、もちろんパーミッションが必要になる。
だからまず基本はパーミッション取得だ。
今回は装備じゃなくて、キーでプリムを動かすことを目指してみよう。
--------------------------------------
default
{
touch_start(integer total_number)
{
if(llDetectedKey(0) == llGetOwner())
{
llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
}
}
run_time_permissions(integer perm)
{
if(perm & PERMISSION_TAKE_CONTROLS)
{
llOwnerSay("パーミッションを取得");
}
}
}
--------------------------------------------
はい、ここまでは前回とほぼ同じ。
今回はタッチしたらコントロールを開始することにする。
タッチをした人が持ち主かどうかを調べ、持ち主ならパーミッションを取得しているわけだ。
装備や椅子じゃないんで、今回はパーミッションの取得のためにダイアログが出る。
さて、じゃあ続けてキーコントロールに行こう。
キーコントロールを設定するには、
llTakeControls(integer controls, integer accept, integer pass_on);
という関数がまず必要だ。
integer controlsは、乗っ取るキーをビットフィールドで書く。
前進キー(↑)と後退キー(↓)を使いたいならCONTROL_FWD|CONTROL_BACKと、|を使って続けて書く。
integer acceptは、キーを使うか使わないかを設定する。
使うならTRUE、使わないならFALSEだ。
どうして使うつもりなのに、もう一回設定がいるんだ?と思うだろうが、後から設定を変える時に楽だからだ。
integer pass_onは、キーを押した時にアバターも動くかどうかを決める。
動くならTRUE、動かないならFALSEだ。
まぁ本当はinteger acceptとinteger pass_onは、組み合わせで動作が変わるんだけど、ここではこんな感じで簡単に覚えておくといい。
大抵は、乗り物やラジコンだとTRUE, FALSE、キーに対応したアニメーションとか行うならTRUE, TRUEになる。
じゃあ追加しよう。
--------------------------------------
default
{
touch_start(integer total_number)
{
if(llDetectedKey(0) == llGetOwner())
{
llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
}
}
run_time_permissions(integer perm)
{
if(perm & PERMISSION_TAKE_CONTROLS)
{
llOwnerSay("パーミッションを取得");
llTakeControls(CONTROL_FWD
|CONTROL_BACK
|CONTROL_RIGHT
|CONTROL_ROT_RIGHT
|CONTROL_LEFT
|CONTROL_ROT_LEFT
|CONTROL_UP
|CONTROL_DOWN
, TRUE, FALSE);
}
}
}
--------------------------------------------
うむ、文字に色付けるだけでも大変。
見やすいようになるべく実際のエディッターのような色分けをしているが、まぁ定数とかはそれぞれ色が違ったりしているのでテキトーだ。
細かい色はその時の気分だ。
いっぺんに使いたいキーを設定できるのだが、ズラッと横につなげるとかなり見づらいので、このように改行して書くといい。
今回はいわばラジコンのようなものを作るので、操作中に自分まで動くとかなり不便だ。
だからinteger pass_onはFALSEにする。
ちょっと間違えやすいのだが、CONTROL_RIGHTとCONTROL_ROT_RIGHTの違いが解るかな。
私たちが何気なく右キーを押すと、アバターは右に向きを変える。
これはCONTROL_ROT_RIGHTの方。
CONTROL_RIGHTはSHIFT+右キーを押した時の横移動のことだ。
ROTの有り無しで変わるからね。
ちなみにUPは上昇キーでDOWNは下降キーだ、飛行時のことを思い出してごらん。
さて、この関数があると、設定したキーが押された時にイベントが発生する。
それが、control(key id, integer level, integer edge)だ。
key idには、キーを押した人のUUIDが入ってくる。
その次の二つはちょっと厄介だ。
integer levelは、押されたキーの定数が「押されている間」入ってくる。
integer edgeは、押されたキーの定数が「押された瞬間と離した瞬間」に入ってくる。
例えば、前進キーを押した瞬間、integer levelとinteger edgeにはCONTROL_FWDの定数が入っている。
前進キーをそのまま押し続けるとinteger levelにはCONTROL_FWDが、integer edgeには0が入っている。
前進キーを離した瞬間、integer levelには0が、integer edgeにはCONTROL_FWDが入っている。
そして完全にどちらも0になって前進キーの入力が終わったことになる。
しかも、キーは同時にいくつも押される可能性があるので、ビットフィールドで&演算を使わないといけない。
ちょっとここだけ別に書いてみよう。
------------------------------------------
control(key id, integer level, integer edge)
{
if(level & CONTROL_FWD)
{
-------------------------------------------
この書き方だと、前進キーが押され続けている間、中身が実行され続ける。
------------------------------------------
control(key id, integer level, integer edge)
{
if(level & CONTROL_FWD && edge & CONTROL_FWD)
{
-------------------------------------------
この書き方だと、前進キーを押した瞬間だけ、中身が実行される。
違いが解るかな?
levelとedgeの片方だけ見るか、両方見るかで、連続入力なのか、一回ずつなのかに変わるんだ。
この使い分けはすごく大事なので、きちんと覚えよう。
そうそう、なぜかこの二つの引数は、スクリプトを書く人によって色んな名前に変わっていることが多いから、フリーのスクリプトを見た時に混乱しないように。
じゃあ試しに突っ込んでみようか。
--------------------------------------
default
{
touch_start(integer total_number)
{
if(llDetectedKey(0) == llGetOwner())
{
llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
}
}
run_time_permissions(integer perm)
{
if(perm & PERMISSION_TAKE_CONTROLS)
{
llOwnerSay("パーミッションを取得");
llTakeControls(CONTROL_FWD
|CONTROL_BACK
|CONTROL_RIGHT
|CONTROL_ROT_RIGHT
|CONTROL_LEFT
|CONTROL_ROT_LEFT
|CONTROL_UP
|CONTROL_DOWN
, TRUE, EFALS);
}
}
control(key id, integer level, integer edge)
{
if(level & CONTROL_FWD)
{
llOwnerSay("前進キー");
}
}
}
--------------------------------------------
前進キーを押している間、「前進キー」と出るはずだ。
試しに、
if(level & CONTROL_FWD)を、
if(level & CONTROL_FWD && edge & CONTROL_FWD)に書き換えてごらん。
今度はキーを押し続けても一回しか出ないよ。
さあさあ、ようやくここまで来てコントロールが何となくわかったから、プリムを実際にキーで動かしてみようか。
必要なのはプリムの現在位置、移動方向、現在角度、変更角度だけど、ついでに他にも色々追加してしまおう。
--------------------------------------
integer flag;
default
{
touch_start(integer total_number)
{
if(flag == 0)
{
if(llDetectedKey(0) == llGetOwner())
{
llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
}
}
else if(flag == 1)
{
if(llDetectedKey(0) == llGetOwner())
{
llResetScript();
}
}
}
run_time_permissions(integer perm)
{
if(perm & PERMISSION_TAKE_CONTROLS)
{
flag = 1;
llOwnerSay("パーミッションを取得");
llTakeControls(CONTROL_FWD
|CONTROL_BACK
|CONTROL_RIGHT
|CONTROL_ROT_RIGHT
|CONTROL_LEFT
|CONTROL_ROT_LEFT
|CONTROL_UP
|CONTROL_DOWN
, TRUE, FALSE);
}
}
control(key id, integer level, integer edge)
{
vector pos = llGetPos();
rotation rot = llGetRot();
vector fwd = llRot2Fwd(rot) * 0.5;
vector back = llRot2Fwd(rot) * -0.5;
vector right = llRot2Left(rot) * -0.5;
vector left = llRot2Left(rot) * 0.5;
vector up = llRot2Up(rot) * 0.5;
vector down = llRot2Up(rot) * -0.5;
rotation right_rot = llEuler2Rot(<0,0,-20> * DEG_TO_RAD);
rotation left_rot = llEuler2Rot(<0,0,20> * DEG_TO_RAD);
if(level & CONTROL_FWD)
{
llSetPos(pos + fwd);
}
if(level & CONTROL_BACK)
{
llSetPos(pos + back);
}
if(level & CONTROL_RIGHT)
{
llSetPos(pos + right);
}
if(level & CONTROL_LEFT)
{
llSetPos(pos + left);
}
if(level & CONTROL_UP)
{
llSetPos(pos + up);
}
if(level & CONTROL_DOWN)
{
llSetPos(pos + down);
}
if(level & CONTROL_ROT_RIGHT)
{
llSetRot(right_rot * rot);
}
if(level & CONTROL_ROT_LEFT)
{
llSetRot(left_rot * rot);
}
}
}
--------------------------------------------
いきなり長くなってビックリしてはいけない。
一つ一つ見ていけばちゃんと解るよ。
最初のinteger flag;は、1回目と2回目のタッチの中身を切り替えるためのフラグ変数だ。
1回目のタッチでパーミッションを取得してフラグ変数が1になると、2回目のタッチではスクリプトをリセットできる。
これで必要がない時はパーミッションごとコントロールを終えることができる。
controlイベントの中身が大幅にパワーアップだ。
まず、キーが押されるとオブジェクトの位置と角度を保存する。
次にやってるのは、キーが押された時の移動量と角度を決めているんだ。
llRot2Fwd、llRot2Left、llRot2Upは、それぞれオブジェクトの角度から正面、左、上の各方向を調べるやつだ。
前にここで説明したね、忘れちゃった人は読み返してごらん。
0.5、-0.5をかけることで、一回に50cm移動する。
llEuler2Rotは、<X, Y, Z>のオイラー角度を、<X, Y, Z, S>の四元数に変換するものだ。
角度を何度変えればいいか解りやすくなる。
最後にDEG_TO_RAD定数をかけるのを忘れたらダメだよ。
後は、押されたキーに対応して、現在の位置に移動量を足したり、現在の角度と変更角度をかけて増やしたりしてるわけ。
とりあえず上のをコピーして見てみると早い。
一つだけ注意が必要だ。
移動に設定しているllSetPosとllSetRotは、物理オブジェクトには通用しない。
これは非物理で動かす内容だからね。
キーコントロールは一緒だけど、物理の時は物理専用関数で動かす必要がある。
いやいや疲れた。
お疲れさま。
あんまり使う機会はないかなぁ。
と言うか、作る機会はあまりないかな、使う機会はそこそこあるか。
だって乗り物のアレだもんね。
ちょうど1年前に装備型の飛行機作ったっけ。
あの時はまだLSL始めたばかりで、何が何やらさっぱり解らなかったっけな。
装備型なのになんでキーコントロールか。
だってアニメーション連動型だったから。
まぁつまり、前回と今回の内容をやってたわけだ。
キーコントロールを行うなら、もちろんパーミッションが必要になる。
だからまず基本はパーミッション取得だ。
今回は装備じゃなくて、キーでプリムを動かすことを目指してみよう。
--------------------------------------
default
{
touch_start(integer total_number)
{
if(llDetectedKey(0) == llGetOwner())
{
llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
}
}
run_time_permissions(integer perm)
{
if(perm & PERMISSION_TAKE_CONTROLS)
{
llOwnerSay("パーミッションを取得");
}
}
}
--------------------------------------------
はい、ここまでは前回とほぼ同じ。
今回はタッチしたらコントロールを開始することにする。
タッチをした人が持ち主かどうかを調べ、持ち主ならパーミッションを取得しているわけだ。
装備や椅子じゃないんで、今回はパーミッションの取得のためにダイアログが出る。
さて、じゃあ続けてキーコントロールに行こう。
キーコントロールを設定するには、
llTakeControls(integer controls, integer accept, integer pass_on);
という関数がまず必要だ。
integer controlsは、乗っ取るキーをビットフィールドで書く。
前進キー(↑)と後退キー(↓)を使いたいならCONTROL_FWD|CONTROL_BACKと、|を使って続けて書く。
integer acceptは、キーを使うか使わないかを設定する。
使うならTRUE、使わないならFALSEだ。
どうして使うつもりなのに、もう一回設定がいるんだ?と思うだろうが、後から設定を変える時に楽だからだ。
integer pass_onは、キーを押した時にアバターも動くかどうかを決める。
動くならTRUE、動かないならFALSEだ。
まぁ本当はinteger acceptとinteger pass_onは、組み合わせで動作が変わるんだけど、ここではこんな感じで簡単に覚えておくといい。
大抵は、乗り物やラジコンだとTRUE, FALSE、キーに対応したアニメーションとか行うならTRUE, TRUEになる。
じゃあ追加しよう。
--------------------------------------
default
{
touch_start(integer total_number)
{
if(llDetectedKey(0) == llGetOwner())
{
llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
}
}
run_time_permissions(integer perm)
{
if(perm & PERMISSION_TAKE_CONTROLS)
{
llOwnerSay("パーミッションを取得");
llTakeControls(CONTROL_FWD
|CONTROL_BACK
|CONTROL_RIGHT
|CONTROL_ROT_RIGHT
|CONTROL_LEFT
|CONTROL_ROT_LEFT
|CONTROL_UP
|CONTROL_DOWN
, TRUE, FALSE);
}
}
}
--------------------------------------------
うむ、文字に色付けるだけでも大変。
見やすいようになるべく実際のエディッターのような色分けをしているが、まぁ定数とかはそれぞれ色が違ったりしているのでテキトーだ。
細かい色はその時の気分だ。
いっぺんに使いたいキーを設定できるのだが、ズラッと横につなげるとかなり見づらいので、このように改行して書くといい。
今回はいわばラジコンのようなものを作るので、操作中に自分まで動くとかなり不便だ。
だからinteger pass_onはFALSEにする。
ちょっと間違えやすいのだが、CONTROL_RIGHTとCONTROL_ROT_RIGHTの違いが解るかな。
私たちが何気なく右キーを押すと、アバターは右に向きを変える。
これはCONTROL_ROT_RIGHTの方。
CONTROL_RIGHTはSHIFT+右キーを押した時の横移動のことだ。
ROTの有り無しで変わるからね。
ちなみにUPは上昇キーでDOWNは下降キーだ、飛行時のことを思い出してごらん。
さて、この関数があると、設定したキーが押された時にイベントが発生する。
それが、control(key id, integer level, integer edge)だ。
key idには、キーを押した人のUUIDが入ってくる。
その次の二つはちょっと厄介だ。
integer levelは、押されたキーの定数が「押されている間」入ってくる。
integer edgeは、押されたキーの定数が「押された瞬間と離した瞬間」に入ってくる。
例えば、前進キーを押した瞬間、integer levelとinteger edgeにはCONTROL_FWDの定数が入っている。
前進キーをそのまま押し続けるとinteger levelにはCONTROL_FWDが、integer edgeには0が入っている。
前進キーを離した瞬間、integer levelには0が、integer edgeにはCONTROL_FWDが入っている。
そして完全にどちらも0になって前進キーの入力が終わったことになる。
しかも、キーは同時にいくつも押される可能性があるので、ビットフィールドで&演算を使わないといけない。
ちょっとここだけ別に書いてみよう。
------------------------------------------
control(key id, integer level, integer edge)
{
if(level & CONTROL_FWD)
{
-------------------------------------------
この書き方だと、前進キーが押され続けている間、中身が実行され続ける。
------------------------------------------
control(key id, integer level, integer edge)
{
if(level & CONTROL_FWD && edge & CONTROL_FWD)
{
-------------------------------------------
この書き方だと、前進キーを押した瞬間だけ、中身が実行される。
違いが解るかな?
levelとedgeの片方だけ見るか、両方見るかで、連続入力なのか、一回ずつなのかに変わるんだ。
この使い分けはすごく大事なので、きちんと覚えよう。
そうそう、なぜかこの二つの引数は、スクリプトを書く人によって色んな名前に変わっていることが多いから、フリーのスクリプトを見た時に混乱しないように。
じゃあ試しに突っ込んでみようか。
--------------------------------------
default
{
touch_start(integer total_number)
{
if(llDetectedKey(0) == llGetOwner())
{
llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
}
}
run_time_permissions(integer perm)
{
if(perm & PERMISSION_TAKE_CONTROLS)
{
llOwnerSay("パーミッションを取得");
llTakeControls(CONTROL_FWD
|CONTROL_BACK
|CONTROL_RIGHT
|CONTROL_ROT_RIGHT
|CONTROL_LEFT
|CONTROL_ROT_LEFT
|CONTROL_UP
|CONTROL_DOWN
, TRUE, EFALS);
}
}
control(key id, integer level, integer edge)
{
if(level & CONTROL_FWD)
{
llOwnerSay("前進キー");
}
}
}
--------------------------------------------
前進キーを押している間、「前進キー」と出るはずだ。
試しに、
if(level & CONTROL_FWD)を、
if(level & CONTROL_FWD && edge & CONTROL_FWD)に書き換えてごらん。
今度はキーを押し続けても一回しか出ないよ。
さあさあ、ようやくここまで来てコントロールが何となくわかったから、プリムを実際にキーで動かしてみようか。
必要なのはプリムの現在位置、移動方向、現在角度、変更角度だけど、ついでに他にも色々追加してしまおう。
--------------------------------------
integer flag;
default
{
touch_start(integer total_number)
{
if(flag == 0)
{
if(llDetectedKey(0) == llGetOwner())
{
llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
}
}
else if(flag == 1)
{
if(llDetectedKey(0) == llGetOwner())
{
llResetScript();
}
}
}
run_time_permissions(integer perm)
{
if(perm & PERMISSION_TAKE_CONTROLS)
{
flag = 1;
llOwnerSay("パーミッションを取得");
llTakeControls(CONTROL_FWD
|CONTROL_BACK
|CONTROL_RIGHT
|CONTROL_ROT_RIGHT
|CONTROL_LEFT
|CONTROL_ROT_LEFT
|CONTROL_UP
|CONTROL_DOWN
, TRUE, FALSE);
}
}
control(key id, integer level, integer edge)
{
vector pos = llGetPos();
rotation rot = llGetRot();
vector fwd = llRot2Fwd(rot) * 0.5;
vector back = llRot2Fwd(rot) * -0.5;
vector right = llRot2Left(rot) * -0.5;
vector left = llRot2Left(rot) * 0.5;
vector up = llRot2Up(rot) * 0.5;
vector down = llRot2Up(rot) * -0.5;
rotation right_rot = llEuler2Rot(<0,0,-20> * DEG_TO_RAD);
rotation left_rot = llEuler2Rot(<0,0,20> * DEG_TO_RAD);
if(level & CONTROL_FWD)
{
llSetPos(pos + fwd);
}
if(level & CONTROL_BACK)
{
llSetPos(pos + back);
}
if(level & CONTROL_RIGHT)
{
llSetPos(pos + right);
}
if(level & CONTROL_LEFT)
{
llSetPos(pos + left);
}
if(level & CONTROL_UP)
{
llSetPos(pos + up);
}
if(level & CONTROL_DOWN)
{
llSetPos(pos + down);
}
if(level & CONTROL_ROT_RIGHT)
{
llSetRot(right_rot * rot);
}
if(level & CONTROL_ROT_LEFT)
{
llSetRot(left_rot * rot);
}
}
}
--------------------------------------------
いきなり長くなってビックリしてはいけない。
一つ一つ見ていけばちゃんと解るよ。
最初のinteger flag;は、1回目と2回目のタッチの中身を切り替えるためのフラグ変数だ。
1回目のタッチでパーミッションを取得してフラグ変数が1になると、2回目のタッチではスクリプトをリセットできる。
これで必要がない時はパーミッションごとコントロールを終えることができる。
controlイベントの中身が大幅にパワーアップだ。
まず、キーが押されるとオブジェクトの位置と角度を保存する。
次にやってるのは、キーが押された時の移動量と角度を決めているんだ。
llRot2Fwd、llRot2Left、llRot2Upは、それぞれオブジェクトの角度から正面、左、上の各方向を調べるやつだ。
前にここで説明したね、忘れちゃった人は読み返してごらん。
0.5、-0.5をかけることで、一回に50cm移動する。
llEuler2Rotは、<X, Y, Z>のオイラー角度を、<X, Y, Z, S>の四元数に変換するものだ。
角度を何度変えればいいか解りやすくなる。
最後にDEG_TO_RAD定数をかけるのを忘れたらダメだよ。
後は、押されたキーに対応して、現在の位置に移動量を足したり、現在の角度と変更角度をかけて増やしたりしてるわけ。
とりあえず上のをコピーして見てみると早い。
一つだけ注意が必要だ。
移動に設定しているllSetPosとllSetRotは、物理オブジェクトには通用しない。
これは非物理で動かす内容だからね。
キーコントロールは一緒だけど、物理の時は物理専用関数で動かす必要がある。
いやいや疲れた。
お疲れさま。







