ソラマメブログ

2007年04月10日

照明を作ろう(初級スクリプト第六回)

椅子、ドアと作ってきたことで、だいぶスクリプトの構成については理解できてきたと思います。
今回はさらに練習として、照明を作ってみましょう。
ただの照明では面白くありませんので、カラフルに色が変わるものにしたいと思います。

例によってbuildから

お好みで照明器具を作りましょう。
ランプ型、蛍光灯型、電球型、なんでも構いません。

私はこんなのを作りました。

照明を作ろう(初級スクリプト第六回)

手抜きなスタンドライトですw
ライト効果の設定

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

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

  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;
  }
}


もしかしたらステートoffというのを作った方もいらっしゃるかもしれません。
それでもOKです。

このスクリプトは、
「default」ステートが「開始されたとき」に「照明をOFF」
「default」ステートで「タッチされた」ら「ステートredに変更」
「red」ステートが「開始されたとき」に「照明を赤にする」
「red」ステートで「タッチされた」ら「ステートをgreenに変更」
「green」ステートが「開始されたとき」に「照明を緑にする」
「green」ステートで「タッチされた」ら「ステートをblueに変更」
「blue」ステートが「開始されたとき」に「照明を青にする」
「blue」ステートで「タッチされた」ら「ステートをdefault(消灯状態)に変更」
このような動きです。

イベントも今まで登場したものだけですので、それほど難しくはないかと思います。

同じ処理をまとめよう

ですが、このスクリプトを見ていて、
「なんだか同じようなコードの繰り返しだなぁ・・・」
と思いませんでしたか?

どのステートの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 ++;

今回ぜひマスターして欲しいのはユーザー関数の作り方です。
上手なユーザー関数が作れるようになると、スクリプトがよりわかりやすく、シンプルで、拡張性が高くなります。

カラフルな照明が作れるようになりましたが、このスクリプトはまだまだ改良の余地があります。
次回も続けて照明スクリプトの改良を進める予定です。


同じカテゴリー(初級スクリプト)の記事画像
衝突判定(スクリプト初級第二十三回)
カメラ制御(スクリプト初級第二十二回)
センサーを使おう(スクリプト初級第二十回)
HUDを作ろう(スクリプト初級第十六回)
prim間通信(スクリプト初級第十五回)
アニメさせよう(スクリプト初級第十三回)
同じカテゴリー(初級スクリプト)の記事
 衝突判定(スクリプト初級第二十三回) (2007-05-08 12:15)
 カメラ制御(スクリプト初級第二十二回) (2007-05-07 14:36)
 デモ商品を作ろう(スクリプト初級第二十一回) (2007-05-02 12:15)
 センサーを使おう(スクリプト初級第二十回) (2007-05-01 12:15)
 ステートのこと(スクリプト初級第十九回) (2007-04-27 12:15)
 rez!(スクリプト初級第十八回) (2007-04-26 12:15)
この記事へのコメント
追加情報:

llSetPrimitiveParams()ですが、実際の実行には0.2秒の時間がかかります。
連続して色を変えるような場合、ときとして照明の色の変更が追いつかない場合があるようです。

「オブジェクトが赤くなっている」>「照明OFF」>「しばらく赤いまま」

詳しい原因は追究していませんが、バグというよりは処理上の遅延と考えたほうがいいかもしれません。
Posted by Miz at 2007年04月16日 15:22
仕様が変わったんでしょうかね。
今はradiusは20m,falloffは2.0まで指定可能ですね
Posted by TOSHI at 2008年06月07日 07:07
すみません、スクリプトとは直接関係ない(と思われる)質問なのですが…

私はここの記事を見て照明を自分で作ってみようと思い、チャレンジしました。
スクリプト自体はタッチすると色が変わり、ON/OFFもきちんと動作しました。
しかし、こちらのスクリーンショットにあるような、球体自体の色が変わる(球体が発光しているような)というものではなく、球体の色はそのままで、球体の周りが光るというものになってしまいます。
(スクリプトは球体に仕込んであります)

発光させたい球体自体のテクスチャやその他の設定とかに原因があるのかな?と思っていろいろ弄ってみたのですがどうもうまくいきません。

宜しかったらアドバイスお願いいたします。
Posted by まお at 2009年01月26日 09:32
非常に楽しい経験
Posted by BOSS Sunglasses at 2011年08月17日 10:51
 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。