お問合せ

アートクリエイター Hijiri-Mishima 三島聖

アートクリエイター Hijiri-Mishima 三島聖サイトのTOPページへ移動する

> リンデンスクリプト-Linden Script- >  戻る

Second Lifeのプログラミング Linden Scripting Language(リンデンスクリプト)の倉庫

照明効果スクリプト

今回のスクリプトは、タッチするたびに照明の色が変わるものにしましょう。
最初は明かりがついていなくて、タッチすると赤く光り、次にタッチすると緑、さらにタッチすると青、もう一度タッチすると照明が消える(最初の状態に戻る)ような動きにします。
この段階で、ステートとイベントの構成は思い浮かぶようなら、着実にスクリプトのスキルは進歩しています。

肝心の「照明の色を変える」方法については初めてですので、実現するための命令を紹介します。

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>
float intensity

光の強さです。
float型というのは小数で、ここでは0.0~1.0を指定します。
1.0が一番強い光になります。

float radius

光のとどく範囲です。
オブジェクトの中心からの半径で指定します。
単位はメートルで、10.0メートルまで指定可能です。

float falloff

光の減衰率です。
大きな値なほど、遠くまで弱まらずに光が届きます。
指定可能な範囲は0.0~1.0です。

具体的にどのような数値を指定するのか示しておきます。
例えば、赤色の照明を最高の明るさで半径5メートルの範囲に光らせるには、以下のようになります。

llSetPrimitiveParams(
[PRIM_POINT_LIGHT, TRUE, <1.0, 0.0, 0.0>, 1.0, 5.0, 1.0]
);

青色の照明を、半分くらいの強さで半径3.5メートルの範囲に、減衰率50%で光らせるには、以下の通りです。

llSetPrimitiveParams(
[PRIM_POINT_LIGHT, TRUE, <0.0, 0.0, 1.0>, 0.5, 3.5, 0.5]
);

照明を消すときには、

llSetPrimitiveParams(
[PRIM_POINT_LIGHT, FALSE, <0.0, 0.0, 0.0>, 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

Copyright @2006-2007 Getting Better, Hijiri-Mishima & Co. All Rights Reserved.