ひなたの雑記帳

最近はセカンドライフで遊んでいます

まるくならべる(1)

Computer, SecondLife


セカンドライフ技術系 Advent Calendar 2019 のための記事です。

 仮想世界セカンドライフの中で「ある目的」のために、たくさんのオブジェクトをきれいに丸く並べたいと思ったので、それを実現するためのわたしの試行錯誤を記事にしてみました。他の皆さんに比べてレベル低いのはお許しください。。

オブジェクトを置く(REZする)ための関数は llRezObject です。その書式は、

llRezObject( string inventory, vector pos, vector vel, rotation rot, integer param );

オブジェクト inventory を位置 pos に、速度 vel、回転 rot、開始パラメタ param で出現させる。
• string inventory – プリムの インベントリ の中にあるオブジェクト
• vector pos – 位置 (リージョン座標 で指定)
• vector vel – 速度
• rotation rot – 回転
• integer param – on_rez イベントの引数であり、 rez されたオブジェクトでの llGetStartParameter の戻り値でもある。

置く場所をまるく

ここで、まず重要なのが「どこに」オブジェクトを置くのか、という部分、 POS です。
ここでいきなりですが、高校の数学のことを思い出してみましょう。

OPの長さ(半径)が r で、なす角が θ のとき、点Pの x 座標は rcosθ 、y座標は rsinθ になります。

r を一定にして、θを動かしていけば円が描けるはず。そう考えて書いた最初のスクリプトがこれです。


float r=3.0;
integer b = 8;
float t=0.0;

default
{
     touch_start(integer param)
     {      
      integer a = 0;
      for(; a < b; ++a)
         {
             t=(360/b * a)* DEG_TO_RAD; 
            llRezObject("Object", llGetPos() + <r*llCos(t),r*llSin(t),0.0>, <0.0,0.0,0.0>, <0.0,0.0,0.0,1.0>, 0);
         }
     }
}

ここで、
r はオブジェクトを並べる円の半径
b は並べるオブジェクトの個数

360/b は一周360°を b 等分するということです。このスクリプトの例では8等分(つまり45°)ですね。


      integer a = 0;
      for(; a < b; ++a)
         {
             t=(360/b * a)* DEG_TO_RAD; 

の部分は、角θ(このスクリプトの中では t という文字を使っています)を、0倍から7倍まで変化させる(つまり0°からはじめて、45°きざみで 315°まで)ということです。

* DEG_TO_RAD

はなにかというと、リンデンスクリプトの角の大きさの単位がおなじみの「度」ではなくて、「ラジアン」なので、「度からラジアンに」換算するためにかけ算されているものです。

心臓部の llRezObjectを見てみましょう。
"Object" は並べたいオブジェクトの名前です。その次の

llGetPos() + <r*llCos(t),r*llSin(t),0.0>

が一番のポイントです。このスクリプトとObjectが入っているプリムを中心にObjectをまるく並べていきます。、

llGetPos()

で、もとのプリムの座標(つまり円の中心)を調べて、そのx座標に rcosθ を、y座標に rsinθ を付け加えて、置くObject の位置を決めていくのがこの部分になります。

1つのプリムにこのスクリプトと、置きたい Object を入れてタッチしてみると・・・

こんなふうにきれいにまるく並びます。でも、置いてある場所がきれいにまるくても、Objectの向きがいまひとつですよね。

置く向きもまるく

次に「どんな向きで」オブジェクトを置くのか、という部分 rot に注目して書いてみたのがこれ。


float r=3.0;
integer b = 8;
float t=0.0;

default
{
     touch_start(integer param)
     {      
      integer a = 0;
      for(; a < b; ++a)
         {
             t=(360/b * a)* DEG_TO_RAD; 
            llRezObject("Object", llGetPos() + <r*llCos(t),r*llSin(t),0.0>, <0.0,0.0,0.0>, llEuler2Rot(<0.0,0.0,t>), 0);
         }
     }
}

最初のスクリプトで

<0.0,0.0,0.0,1.0>

と書いてあったところを

llEuler2Rot(<0.0,0.0,t>)

と直しました。
ここが、Object を置く「向き」を指定する部分になります。リンデンスクリプトの回転の表現は「クォータニオン」(四元数) と呼ばれる形式で、直感的にはとてもわかりにくいのです。
<0.0,0.0,0.0,1.0>
はクォータニオンで「まったく回転しない」ことを意味しています。
回転を表す形式で直感的にわかりやすいのは「オイラー角」と呼ばれるもので、x軸 y軸 z軸を中心にそれぞれどれだけ回転するのかで表現します。
<0.0,0.0,t>
は、x軸 y軸のまわりには回転しない、z軸のまわりにθ回転する、ということです。
しかし、リンデンスクリプトはオイラー角ではなくクォータニオンでないと受け付けてくれないので、オイラー角をクォータニオンに変換してくれる関数llEuler2Rotを使っています。

実行結果はこのようになりました。

きれいに丸く並びましたね。これが求めている結果だったらOKなんですけど、そうでない場合は、追加でさらに±90°とか180°とか回転してあげないといけないかもしれません。その場合は、回転のところを
<0.0,0.0,t+90*DEG_TO_RAD> ← 90°回転
<0.0,0.0,t-90*DEG_TO_RAD> ← -90°回転
<0.0,0.0,t+180*DEG_TO_RAD> ← 180°回転
などに変えて試してみれば、どれかが思った結果になると思います。

半径 r=3.5 ,並べる個数 b=24 で、+90° 回転させた結果です^^

第2回に続きます・・・

まるくならべる(2)