照明効果スクリプト
今回のスクリプトは、タッチするたびに照明の色が変わるものにしましょう。
最初は明かりがついていなくて、タッチすると赤く光り、次にタッチすると緑、さらにタッチすると青、もう一度タッチすると照明が消える(最初の状態に戻る)ような動きにします。
この段階で、ステートとイベントの構成は思い浮かぶようなら、着実にスクリプトのスキルは進歩しています。
肝心の「照明の色を変える」方法については初めてですので、実現するための命令を紹介します。
llSetPrimitiveParams(
[PRIM_POINT_LIGHT, integer on, vector color, float intensity , float radius, float falloff]
);
これが照明効果を設定するための命令です。
簡単に説明すると、
integer on
この部分は「TRUE」か「FALSE」を指定します。
「TRUE」でしたら照明効果ON、「FALSE」はOFFです。
vector color
照明の色の指定です。
vector型は前に椅子のところで出てきましたが、
<1.0, 0.0, 0.0>
こんなふうに書きます。
この三つの数字が何を意味しているかというと、先頭から順番に、「赤色の強さ」「緑色の強さ」「青色の強さ」です。
red,green,blueの順なのでRGB値と言われます。
画像を描いたことのある人にはお馴染みですね。
各数字は0.0~1.0で指定します。赤・緑・青の色の強さを組み合わせて好みの色を指定するわけですが、全部が0.0の場合は黒、全部が1.0の場合は白になります。
代表的な色を紹介しておきましょう。
| 色 | RGB値 |
| 赤 | <1.0, 0.0, 0.0> |
| 緑 | <0.0, 1.0, 0.0> |
| 青 | <0.0, 0.0, 1.0> |
| 黄色 | <1.0, 1.0, 0.0> |
| 水色 | <0.0, 1.0, 1.0> |
| 紫 | <1.0, 0.0, 1.0> |
| 白 | <1.0, 1.0, 1.0> |
| 灰色 | <0.5, 0.5, 0.5> |
| 黒 | <0.0, 0.0, 0.0> |
default {
state_entry(){
llSetPrimitiveParams(
[PRIM_POINT_LIGHT, FALSE, <0.0,0.0,0.0>, 0.0, 0.0, 0.0]
);
}
touch_start(integer detected){
state red;
}
}
state red {
state_entry(){
llSetPrimitiveParams(
[PRIM_POINT_LIGHT, TRUE, <1.0,0.0,0.0>, 0.5, 3.0, 0.75]
);
}
touch_start(integer detected){
state green;
}
}
state green {
state_entry(){
llSetPrimitiveParams(
[PRIM_POINT_LIGHT, TRUE, <0.0,1.0,0.0>, 0.5, 3.0, 0.75]
);
}
touch_start(integer detected){
state blue;
}
}
state blue {
state_entry(){
llSetPrimitiveParams(
[PRIM_POINT_LIGHT, TRUE, <0.0,0.0,1.0>, 0.5, 3.0, 0.75]
);
}
touch_start(integer detected){
state default;
}
}
見慣れない表現が出てきました。
1行目に書いてある「rotation rot;」です。
これはドアの初期角度を保持しておくための「変数」です。
変数とは、数字や文字、位置や角度などのデータを扱うためのものです。
しばしば初心者さん向けのプログラミング解説書では「データを入れておく箱のようなもの」と説明されます。
変数を使うときには、
変数の型 変数名;
のように書きます。
ここでは「回転角度」のデータを扱いますので、データの型はrotation型です。
変数名は自由につけられますが、何のための変数かわかりやすい名前にすべきでしょう。今回はrotという名前にしました。
7行目に、
rot = llGetRot();
とあります。
llGetRot()という関数はオブジェクトの回転角度を教えてくれる関数です。
これが
・「初期状態の角度」を設定
の処理になります。
つまり「変数rotにオブジェクトの現在の角度を設定する」ということです。
15行目は「開いたとき」すなわち「ドアを初期状態の角度+90度にする」処理です。
llSetRot(rot * llEuler2Rot(<0.0, 0.0, 90.0> * DEG_TO_RAD));
ごちゃごちゃ書いてありますが、分割して見て行けば全て今までに出てきた内容です。
まずllSetRot()はオブジェクトの角度を設定する命令ですね。
この命令に「rot * llEuler2Rot(<0.0, 0.0, 90.0> * DEG_TO_RAD)」のように角度を指定しています。
この部分は、「rot」と「llEuler2Rot(<0.0, 0.0, 90.0> * DEG_TO_RAD)」に分けれます。
「rot」は先ほど設定したドアの初期角度、「llEuler2Rot・・・」は例のオマジナイでZ軸回りに90度回転することを意味しています。
注意して見て頂きたいのは、「rot」と「llEuler2Rot・・・」が「*」でつながっているところです。
「*」は掛け算、つまり「×」のことです。
「あれ?初期角度+90度だから、×じゃなくて+じゃないの?」
素直に考えるとそのような疑問が出てきますが、四元数を使った3D座標系の回転は、掛け算をすることによって二つの回転を合計するのです。
あまり数学的なことを書くのもアレですので、「角度+角度」をしたいときには「角度×角度」と書くのだということを覚えてしまいましょう。
要するに15行目の
llSetRot(rot * llEuler2Rot(<0.0, 0.0, 90.0> * DEG_TO_RAD));
これで「ドアを初期状態の角度+90度にする」ことになります。
27行目は簡単ですね。
閉じたときにドアを「初期状態の角度」にする処理です。
rotには初期角度を設定してありますから、そのまま、
llSetRot(rot);
これでドアは初期角度になるわけです。
ここまででドアの動きとしては必要なところを実装できました。
ですが、このスクリプトを見ていて、
「なんだか同じようなコードの繰り返しだなぁ・・・」
と思いませんでしたか?
どのステートのstate_entry()イベントもllSetPrimitiveParams()を呼び出していて、red、green、blueについては違うのは色の指定だけです。
ステートが4つもあるのでコードは長くなってますし、もしもさらに色を増やしたいと思ったら、また新しいステートを増やして、同じようなコードを記述しなければなりません。
もう少しすっきりとしたコードにならないでしょうか。
今回ぜひマスターして欲しいのは、このような場合に同じ処理をまとめて記述する方法です。
lslでは、繰り返し使うような処理をまとめて、「ユーザー関数」にすることができます。
単純なユーザー関数はこんなふうに書きます。
関数名(引数){
処理;
}
引数というのは、この関数に渡すパラメータです。
今回の例では照明の色が変化しますので、色データを引数として渡すのがベストです。
処理の部分では渡された色データに応じて照明効果を設定します。
例えば、このようになります。
light(vector color){
if (color == ZERO_VECTOR) {
llSetPrimitiveParams(
[PRIM_POINT_LIGHT, FALSE, ZERO_VECTOR, 0.0, 0.0, 0.0]
);
}else{
llSetPrimitiveParams(
[PRIM_POINT_LIGHT, TRUE, color, 0.5, 3.0, 0.75]
);
}
}
見慣れないものがいくつかありますので説明しておきます。
まずZERO_VECTORですが、これは<0.0,0.0,0.0>のことです。
いちいち<0.0,0.0,0.0>と書いてもいいのですが、この値はしばしば使うので、簡単に使えるようZERO_VECTORという特別な変数が用意されているのです。
それからifというのも初めて登場しました。
これは条件分岐をするための構文で、
if (条件その1) {
条件その1が正しいときの処理;
} else if (条件その2) {
条件その2が正しいときの処理;
} else {
それ以外のときの処理;
}
このように使います。
今回は引数colorがZERO_VECTORかどうかを判定し(color == ZERO_VECTOR)、ZERO_VECTORだった場合は照明OFFにしています。
ZERO_VECTOR以外の場合は引数colorで指定された色で照明をONにしています。
この関数を使って照明スクリプトを書き換えてみます。
integer counter=0;
light(vector color){
if (color == ZERO_VECTOR) {
llSetPrimitiveParams(
[PRIM_POINT_LIGHT, FALSE, ZERO_VECTOR, 0.5, 3.0, 0.75]
);
}else{
llSetPrimitiveParams(
[PRIM_POINT_LIGHT, TRUE, color, 0.5, 3.0, 0.75]
);
}
}
default {
state_entry(){
counter = 0;
light(ZERO_VECTOR);
}
touch_start(integer detected){
counter ++;
if (counter == 0){
light(ZERO_VECTOR);
}else if(counter == 1){
light(<1.0,0.0,0.0>);
}else if(counter == 2){
light(<0.0,1.0,0.0>);
}else if(counter == 3){
light(<0.0,0.0,1.0>);
}else{
counter = 0;
light(ZERO_VECTOR);
}
}
}
タッチした回数を数えるために、変数counterを用意しました。
integer型というのは整数です。
タッチのイベントの中で、
counter ++;
と書いてあるのは、
「counterの値を1増やす」
という意味です。
タッチするたびにcounterは1ずつ増えていきます。
そしてcounterの値をif文で判定しています。
0だった場合はユーザー関数light()をZERO_VECTORで呼び出しています。
1のときはlight()を<1.0,0.0,0.0>(赤色)で呼び出しています。
以下同様に、2のときは緑、3のときは青で呼び出します。
4以上になったときは、counterの値を0に戻し、light()をZERO_VECTORで呼び出して消灯しています。
このスクリプトですと、もっと色を増やそうと思ったときにはtouch_start()イベントの中に条件を追加してlight()関数を好みの色で呼び出すだけで済みます。
最初に書いたステートがたくさんあるスクリプトに比べて、非常に拡張性が高くなりました。
いろいろな色に光る照明をぜひ作ってみてください。
今回のポイント
照明効果を設定する命令:
llSetPrimitiveParams(
[PRIM_POINT_LIGHT, integer on, vector color, float intensity , float radius, float falloff]
);
ユーザー関数の作り方:
??
関数名(引数){
処理;
}
(なお、ユーザー関数は全てのステートを書く前に書きます)
条件分岐の構文:
if (条件その1) {
条件その1が正しいときの処理;
} else if (条件その2) {
条件その2が正しいときの処理;
} else {
それ以外のときの処理;
}
変数の値を1増やす:
counter ++;
今回ぜひマスターして欲しいのはユーザー関数の作り方です。
上手なユーザー関数が作れるようになると、スクリプトがよりわかりやすく、シンプルで、拡張性が高くなります。
2007/08/2

