ソラマメブログ
QRコード
QRCODE
※カテゴリー別のRSSです。
アクセスカウンタ
読者登録
メールアドレスを入力して登録する事で、このブログの新着エントリーをメールでお届けいたします。 解除は→こちら
現在の読者数 2人
オーナーへメッセージ

2008年08月02日

ペンコレ用のダンスボール

スクリプト系の記事は久しぶりですが、今ペンコレ用のダンスボールを作成しています。
この手のスクリプトは得意では無いので、イマイチ不安だったのですが、どうやら思うとおりに出来ました。

ここでちょっとLSLの癖がある事が分かりましたので、書いておきます。



その前に御礼を兼ねて、今回アニメーションを提供いただいたお二人を紹介させていただきます。
今回用意したダンスアニメーションは、タイニー宇宙展でタイニー用のジェスチャー集を配布してくれていた、Zig Marchさんから特別にジャスチャーの元になったダンスアニメーションを提供いただき使っています。

またもう一人、前回映像撮影時にもお世話になった、タイニー職人さんのロンちゃんも、ペンちゃんをイメージしたアニメーションを製作してくれ、わざわざ送ってくれました。
そちらも使いたいと思ってます。
ロンちゃんのBlogはコチラです。↓

http://ron.slmame.com/

お二人には本当に感謝しています。この場をかりて御礼申し上げます。m(._.)m

さて、スクリプトのお話に戻ります。
まず仕様ですが、今回は所定の位置で、それぞれが踊って欲しいので、ボールに座った時にダンスアニメーションを開始するようにしたいと思いました。
そして、使ったスクリプトは以下の内容です。

********************************************************************
string animation_name="zig-dance2";
key siton = NULL_KEY;

vector sit_pos = <0.0, 0.0, 0.4>;
vector sit_rot = <0.0, 0.0, 0.0>;

default {
state_entry(){
llSitTarget(sit_pos, llEuler2Rot(sit_rot * DEG_TO_RAD));
}

changed(integer change){
if (change & CHANGED_LINK) {
key av = llAvatarOnSitTarget();
if (siton != NULL_KEY) {
if (av == NULL_KEY) {
llStopAnimation(animation_name);
llSetAlpha(1.0, ALL_SIDES);
siton = NULL_KEY;
}
} else {
if (av != NULL_KEY) {
siton = av;
llSetAlpha(0.0, ALL_SIDES);
llRequestPermissions(siton, PERMISSION_TRIGGER_ANIMATION);
}
}
}
}

run_time_permissions(integer perm) {
key perm_key = llGetPermissionsKey();
if (perm_key == siton) {
if (perm & PERMISSION_TRIGGER_ANIMATION){
list anms = llGetAnimationList(siton);
integer i;
for (i = 0; i < llGetListLength(anms); i++){
llStopAnimation(llList2Key(anms, i));
}
llStartAnimation(animation_name);
}
}
}
}
********************************************************************
これは単純にボールに座ると、アニメーションの実行許可イベントへ行き、現在稼働中のアニメーションを全て停止して、指定したアニメーションを実行すると言うものです。

単純なスクリプトなのですが、このままだと「動いたり動かなかったり」と動作が一定しません。><
そこで色々と原因を調べていたのですが、困った時のMakapu@BlackSheep-LSL頼み。
何となくポイントはllAvatarOnSitTarget()でのキー取得にあるような気がしたので、そのページを参照してみました。

http://miz.slmame.com/e11372.html#llAvatarOnSitTarget

すると!
サンプルスクリプトを見ると、そこには魔法のお呪いが書いてあるでは無いですか。!@@

そうです。
例によってスクリプトのタイムラグを埋めるお呪い。llSleep(0.5)です。

で、このサンプルのように、 if (change & CHANGED_LINK) と key av = llAvatarOnSitTarget();の間に、llSleep(0.5);を記述してみたところ、メデタク問題解消しました。^ ^

どうもLSLの場合、処理によって取得のタイムラグが発生する場合が多く有ります。
そんな場合は、適時お呪いを埋め込んであげなくてはなりません。

とりあえずこれでペンコレ本番も安心して迎えられそうです。
メデタシ・メデタシ。


  

2008年05月19日

整数計算と浮動小数点計算のミス

カクレクマノミの不具合報告です。



実は本店の水槽に設置時点で気が付いていたのですが、クマノミが泳がずに上を向いてアワアワしているのです。><
その原因が分かりました。

原因は遊泳するための目標地点座標計算部分にありました。

x_axi = pos_rand((integer)(x_max*10.0),(integer)(x_min*10.0))/10.0;

この計算式です。コレは修正後です。修正前は↓

x_axi = pos_rand((integer)x_max*10,(integer)x_min*10)/10;

クマノミは小さいですから、遊泳範囲の設定も0.5m刻みになっています。
そして最小値も0.5mなので、最小範囲は1m四方と言う事になっています。

今までの海洋生物は大きいので、最低が2m四方で、1m刻みの設定になっていました。
その点がカクレクマノミの場合は小さくなっているのです。

で、先の式ですが、変更前の場合、x_max=1.5,x_min=0.5とした場合次のようになります。

(integer)x_max*10=1(1.5が整数へ変換されるので1になる)×10=10
(integer)x_min*10=0(0.5が整数に変換されるので0になる)×10=0

結果として、0~10の間の乱数を求めると言う事になり、値は0~9.999999・・・と言う事になります。
でその値を10(整数計算と認識される)で割ると・・・自動的に整数扱いになりますから、答え=0となります。
つまり、何度計算しようとも、答えは常に0なんです。
だから、クマノミは天を仰いでアワアワしているだけで泳がなかったんです。><

修正後の式ですと、
(integer)(x_max*10.0)=15(1.5×10.0=15.0を整数へ変換するから15)
(integer)(x_min*10.0)=5(0.5×10.0=5.0を整数へ変換するから5)

つまり5~15の乱数結果なので、5.0~14.99999・・・となります。
それを10.0(浮動小数点計算と認識される)で割ると、0.5~1.49999・・・と言う結果になります。

このように数式には値の扱い順と「暗黙の型変換」と言う厄介な規則がある訳です。
10と言う数字は整数なので、「暗黙の型変換」が適用されて、計算結果は「整数」に自動的に変換されてしまいます。
10のような数字は「定数」と言いますが、扱いとしては強いんですね。
だから浮動小数点計算をしたい場合は、明示的に10.0としてやらないといけなかったのです。><

で、式を修正したら、クマノミちゃんがアワアワする事無く、メデタク狭い範囲でも嬉しそうに泳ぎ回ってくれました。^ ^

この「暗黙の型変換」に関しては、一般的なプログラミング言語を知っている人にとっては、「当たり前」の事象ですが、IT業界以外の人には分かりづらい概念と思います。
IT業界でも、「定数を式等へ直接書かずに、型宣言して定数用変数へセットして使え」と良く指導したりしてました。
でも、自分で書くと安易に使うんですよね~。。。^ ^;

プログラミングの世界では、整数と浮動小数点と言う違った性格の数字があり、これらを明示的に認識して扱わないと、思わぬ結果になってしまいます。
気をつけましょう。  

2008年05月11日

足の形状は男と女で違うのか。

先日から製作中のギョサンですが、男性アバターで大きさや鼻緒の形状を確認して作ったところ、そのまま女性アバターに使用すると、大きさだけでは無くて、足の形状自体が違う事が分かりました。

まずは男性アバターの足。



かなりフィットしている状態まで大きさ等を調整しました。
そして、そのまま女性アバターに変身させると・・・



当然、このようにブカブカになってしまいます。
で、大きさの調整を行ってフィットさせようとしたのですが・・・



このように、どうにもフィットしません。><
これは男性アバターに比べて、女性アバターの足先が細くなっているからです。
男性の足は、普通に考えた足幅よりも広めの感じがします。靴で言うとEEEEサイズみたいな感じですね。

それに比べて女性の足は、先端が細くなっています。こちらは普通に考えたよりも狭い感じです。
で、鼻緒の形状をモデリングから調整しなおしました。
その結果がコレ↓



やっと綺麗にフィットするようになりました。^ ^

たかがギョサンと侮っていたのですが、意外と靴と言うものも面倒な物ですね。
やっぱ地道に確認しながら製作しないと駄目なようです。
また一つ勉強になった。@@  

2008年04月17日

ちょっと仕組みをバラしてしまおうかな?

「チンドン赤ちゃんペンギン」や「お喋り抱っこペンちゃん」の場合、「編集可、コピー可」の設定で販売していますよね。
実はこのモードだと、かなり自由に「自分だけのオリジナルペンちゃん」も作成できるのです。

とは言っても、簡単な訳では無いです。
例えばチンドンの場合は歩く度に、パーツの相対位置も少し移動させています。
もし、今のと違うパーツを組込もうと思ったら、そのパーツも動きに合わせて相対位置を移動させてあげないといけません。
それと、どのタイミングで移動させれば良いか?と言う問題も発生します。

そこで今回は、この各パーツの移動タイミングや相対位置指定の方法を書いてしまいます。^ ^;(良いのか?)

まず一番重要な、各パーツの同期方法に関してですが、これはルートプリム内のメインスクリプトでコントロールしています。
方式は一般的なリンクメッセージを使用しています。

<リンクメッセージの仕様>
リンクメッセージは以下のような設定で使っています。

 llMessageLinked( LINK_SET, status, msg, NULL_KEY);

この中で実際にコントロールに使っている部分は、statusとmsgの部分です。
このstatusは、各種状態変化のたびに、その状態を表しており、それがリンクメッセージとして各パーツに伝達されています。
と言っても、状態自体は少ないので、実は簡単なものです。

1.設定状態(status = 9)
 Rez又は装着時には、必ず設定状態になります。又タッチした場合も設定状態に遷移します。
 設定状態とはダイアログが表示されている状態です。
 この状態は、動作が停止した状態になります。
 従って、各パーツともデフォルトの状態が設定されるようになっています。

2.行動状態(status=1)
 チンドンなら歩行状態になりますし、抱っこペンちゃんなら、起きて羽根をパタパタ動かしながらお喋りしている状態になります。
 チンドンの場合は、この状態の時に更に歩行と看板の上下をコントロールするリンクメッセージがタイマーで送信されます。
 抱っこタイプなら、羽根の上下をコントロールしています。

3.お眠り状態(status=99)
 抱っこペンちゃんのお眠りモードに入る場合のリンクメッセージです。
 このメッセージが来ると、お眠りポーズに各パーツが設定され、Z・Z・Zのハートのパーティクルが出始めます。
 帽子付の抱っこペンちゃんの場合は、帽子の位置も移動しますので、その辺の位置移動も、このメッセージを受信して実行しています。
 お眠りモードから自動でお目覚めモードになった場合は、status=1で再度行動状態へと戻る事になります。

4.タッチした時の扱い
 各状態共に、タッチした時はタッチイベント内で

touch_start( integer total_number )
{
if(llDetectedKey(0)==key_id){
status = 9;
llMessageLinked( LINK_SET, status, msg_end, NULL_KEY);
}
}

 が実行されますので、設定状態へと遷移する事になります。

5.パーツ位置設定(status=51,msg=1 or 2)
 他の状態と異なり、パーツの位置設定にはmsg部分も使っています。
 具体的には以下のようなメッセージを飛ばしています。

 llMessageLinked( LINK_SET, 51, (string)k, NULL_KEY);

 (string)kとは、内部でinteger kの値を、タイマーイベント内で、1と2を交互に入れ替えています。
 各パーツは1の場合と2の場合で、それぞれ自分の相対位置や相対回転値を保有しており、それをセットして位置を移動させています。
 チンドンの羽根のスクリプトは以下のようになっています。

list wing_rot = [<-0.55059, -0.77752, -0.15117, 0.26357>,<-0.49991, -0.81747, -0.27601, 0.07512>,<-0.55059, -0.77752, -0.15117, 0.26357>];

default
{
link_message(integer sender_num, integer st, string msg, key id){
if(st == 51){
llSetLocalRot(llList2Rot(wing_rot,(integer)msg)); // msgの値をintegerに変換して、リストから該当値を取得してセット
}else if(st==1||st==9){
llSetLocalRot(llList2Rot(wing_rot,1)); // 本当はデフォルトの0にする予定だったが、値の配列を間違えて、1になってしまった。0番目の値は要らないジャン。><
}
}
}

6.サイズ変更時(status=20,msg=サイズ変更比率)
 ダイアログからSmall.Middle,Bigのサイズ変更ボタンが押された場合ですが、この時は各パーツへもサイズ変更による相対位置の変更指示が飛びます。

 llMessageLinked( LINK_SET, 20, (string)f, NULL_KEY);

 ここで、問題になるのは(string)fです。
 
 f = 変更後のサイズ÷変更前のサイズ
 
 と言う計算結果が入っています。
 この比率をメッセージとして送信して、受信した各パーツがfloatの値に再変換して、自分の相対位置を比率に従って調整し直しています。
 例として、帽子のスクリプトを以下に掲載しておきます。

list hat_rot = [<-0.15323, -0.15257, 0.68834, 0.69241>,<0.08608, 0.08651, 0.70010, 0.70354>];
vector pos_0=<-0.12301, -0.00040, 0.01611>;
vector pos_1=<-0.12600, 0.00050, 0.04190>;
float f=1.0;

default
{
link_message(integer sender_num, integer st, string msg, key id){
if(st==1||st==9){ // 設定状態、行動状態の場合の帽子の位置
llSetLocalRot(llList2Rot(hat_rot,0));
llSetPrimitiveParams([PRIM_POSITION, pos_0]);
}else if(st==99){ // お眠り状態の帽子の位置
llSetLocalRot(llList2Rot(hat_rot,1));
llSetPrimitiveParams([PRIM_POSITION, pos_1]);
}else if(st == 20){
f = (float)msg;
llSetScale(llGetScale() * f); // 自分自身の大きさを変更
llSetPrimitiveParams([PRIM_POSITION, llGetLocalPos() * f]); // ルートプリムからの相対位置を調整
pos_0=pos_0*f; // 起きている状態の相対位置の変動を記録
pos_1=pos_1*f; // 寝ている状態の相対位置の変動を記録
}
}
}

 少々格好は悪いのですが、予めデフォルトの相対回転値と相対位置を定義してあります。
 この値がMiddleサイズ時の基準値になっています。
 で、サイズ変更が起こると、リンクメッセージに従い、変更比率から自分自身の大きさを変更し、更にルートプリムからの相対位置も変更比率に沿って変わるので、位置も調整してます。
 そして最後に、変更後の起きている状態と寝ている状態の相対位置も記録し直しておきます。

 こんな感じで、大きさが変わるたびに、各パーツも位置調整をしたりしている訳です。

と言うような感じで、各パーツの位置を本体の移動や大きさに合わせて調整しながら動いている訳です。
けっこう位置合わせは面倒です。><

勿論、羽根の上下動とかであれば、回転角度で指定して上下に動くようにも出来ます。
ただ、今回に関しては、単純なパターンでしか無かったので、制作上は粘土アニメーションのような感じで、各ポジションの座標を求めて、固定値として埋め込んでしまいました。
その辺は汎用性を求めるなら、回転角度だけをパラメータにする方式の方が良いでしょう。

ただどちらにしろ、動きが複雑になればなるほど、これらのバリエーションは増えていく訳で、スクリプト的にはどんどん面倒な内容になっていくと思います。
できれば、簡潔な動きだけで表現した方が、私は優れた製作物だと思っています。

これを見て、オリジナルのペンちゃんを作れる人は、そうザラには居ないでしょうが、今後は「編集可」のタイプはまず販売しないと思いますので、もし今のバージョンを入手していて、スクリプトの知識も中級レベル以上有るならば、オリジナルペンちゃんの作成にもチャレンジしてみて欲しいと思います。

ちなみに、編集可の「ちんどん赤ちゃんペンギン」は今月一杯で販売終了となります。
また、現在販売している「お喋り抱っこペンちゃん」に関しては、今週末には「編集不可」のバージョンに入れ替えると思います。
従って、これを書いても今後はオリジナル品は作れないと言う事です。^ ^;(だから書いた?)

そだ!面白いアイデアのオリジナルペンちゃんが作成できたら、ペンギン・ビレッジのブランドで販売し、売れれば共同製作者として、一定の金額を即時配布することも出来ますので、「これは!」と言うものでも製作したなら、ご一報いただければと思います。

ではではこれで。  

2008年03月10日

自動オブジェクト配布機

HAVOK4対応版を実際に対象顧客へ渡す機械です。
今回の対象者は100人ちょっと居ました。そして渡すオブジェクト総数は180ちょっと・・・アワワ。
けっこう対象を抽出して慌てました。^ ^;
まあ、これだけ多くの人が購入してくれていたと言う事ですよね。だから、しっかりサポートもしなくては。

で、色々考えたのですが、以前サメシリーズの不具合の時みたいに、いきなり送信しても、このご時勢では「受け取り拒否」される危険性も高いと思いました。
かと言って、一人ひとりにIMで連絡して、事情を説明して配布と言うのでは、とても時間が足りません。
また、自分のIN時間帯は日本時間の午後9時~11時程度の2時間が精一杯なので、時間帯の合わない人も多いと思います。
実際に購入記録を見ると、大部分が私が寝ている間に発生しています。^ ^;

そこで、今回は本店内に自動配布機を置いて、来店して受け取ってもらう方式にしました。
要求事項は次のようになります。

1.購入者に対してのみ配布する。
2.コピー不可の商品が多いから、配布は1回だけに制限したい。
3.配布した記録を残したい。
4.できるだけ簡単な操作で受け渡しをしたい。

と言うところです。
1,2を満足させるには、次のようなデータが必要になります。
1)顧客名
2)購入商品名
3)受渡しフラグ
1)と2)はTransaction Historyのデータから抽出して取り出せます。これが180件ちょっとある訳です。
そして3)の受渡しフラグは、スクリプトで商品を渡した事を、内部的に記録する部分です。
未渡し時=0、 渡し済=1と言う感じで記録して管理します。

次に3ですが、これも少々難問でした。
一番簡単で確かな方法は、SLのTransaction Historyに記録される方式で渡す事です。
その為には、「0L$の支払い」と言う方式を取らなければなりません。要は支払いスクリプトを入れて制御する方式です。
でも、これは4の要件には少々反します。「支払い」をクリックして、「0L$」をクリックしてもらう必要があります。
そして更には渡す際にダイアログが出てきて、それもクリックしないと駄目です。

できればパネルにタッチするだけで、GiveInventoryで渡してしまいたいと思っていました。
GiveInventoryですが、相手のプロフィール画面やアバター直接の場合は、Transaction Historyに記録が残るのですが、スクリプト内で行ってしまうと残らないのです。
そこで、今回はE-mail機能を使って、引渡しが完了したら、自分宛にE-mailを送信して、メール上で記録が残るようにしました。

これらに基づいて作成した物が次のような仕様のものです。



アバターがタッチすると、タッチイベント内でllDetectedKeyとllDetectedNameでタッチした人の名前とUUIDを取得します。
最初に名前から、コンテンツ内の顧客リスト(ノートカード)を1件づつ読み込んでマッチングします。
該当する名前が存在したら、その人が購入した商品も同時に読み込まれていますので、対応する商品をコンテンツから相手にGiveInventoryします。その際、スクリプト内のリストに「受渡しフラグ」を持たせておき、そこに済みマークを記録します。
そして「サンキュー・メッセージ」を出すと同時に、自分宛にE-mailを送信すると言う仕組みです。

当初は、1台で全ての顧客、全ての商品を対応させる予定でした。
その方がお客様には分かりやすいですからね。
ところが、まず180件もの購買記録は、スクリプト内部には保持できない。
また、受渡しフラグだけでも・・・と思ったのですが、それだけでも100件も入らないと言う事が分かりました。
リストに保持できるデータ量って、思っていた以上に小さいのですね。正直言って「困った」状態でした。><
まあ工夫すれば、180件程度のフラグは内部で管理できるかもしれませんが、今回は正攻法で取り組んでいたので、そこまでは考えるのを止めました。

そこで止む無く、商品別に顧客リストを分割しました。
それでも「顧客名、商品名、受渡しフラグ」にすると入りきりません。結局内部的には「受渡しフラグ」だけ保持して、後は外出しでノートカードを都度読み込んで処理する事にしました。

利用する側としては面倒なんですけどね・・・止むを得ません。
とりあえず、こんな感じで対象6商品分ですから、6台の配布機を作成して設置しました。
単純な構造にしたので、それぞれの機械は全て同じスクリプトになっています。違いは顧客リストの中身とコンテンツの中に入れた配布する商品だけです。そん点では汎用的で、今後も色々と使えるでしょう。
まあ、こう言う用途では2度と使いたくは無いのですが・・・^ ^;

なかなか皆さん来てはくれないでしょうが、出来れば早く渡しきってしまいたいと思います。今月中には終わらせたいな~。。。

  

2008年03月09日

お知らせ自動配信機

HAVOK4移行に伴う不具合に関して、本日IM送信とノートカード配布を行いましたが、対象者が100人以上居たので、機械的に送信する仕組みを作りました。
その内容に関してメモしておきます。

まずは要求事項。
1.顧客リストを元にIMとノートカードを自動配信する。
2.今回は起動はオーナータッチで起動すれば良い。(将来的にはタイマー起動も欲しいかも?)

と言う事で、結論から言うと、以下のような仕様のスクリプトを作成しました。



オーナーがタッチすると起動し、顧客リスト(ノートカード)を1件づつ読込、顧客名を取得してLinkメッセージを使って子スクリプトを起動します。
子スクリプトは受け取った顧客名から、HTTPリクエストを行い、WEB経由でアバター名からUUIDを取得します。
取得したUUIDを元に、顧客のアバターへIMを自動送信し、更にコンテンツの中のノートカードを送信します。

送信したら「送信完了メッセージ」を親スクリプトへLinkメッセージで返します。
親は完了メッセージを受け取ると、5秒待ってから次のデータを顧客リストから読み込みます。
これを顧客リストの最後まで繰り返すと言う仕組みです。

ここでポイントになった事は、スクリプトからアバターへ、IM送信(llInstantMessage)、ノートカードの提供(llGiveInventory)共に、アバター名では無くアバターのUUIDを指定しなければならない事です。

ちなみに対象顧客リストは、SLのTransaction HistoryデータをEXCELで取得し、ACCESSに登録してあるので、そこから抽出しています。
しかしTransaction Historyにはアバター名は記録されてますが、当然アバターのUUIDは分かりません。
ここではUUIDが必要になります。

スクリプトの関数としては、UUIDからアバター名を取得する関数(llKey2Name)が存在しますが、その逆は存在しません。
しかし、痒い所に手が届くMakapuでは、ちゃんと逆パターンの取得方法が紹介されていました。
それがWEB経由でアバターのUUIDを検索できるサイトから取得する方法です。
今回は、そちらを利用させてもらってUUIDをスクリプト内に取り込んで処理しています。

ただ、HTTPリクエストで取得した場合、全員のUUIDは取得できませんでした。20件ちょっとがエラーになって取得できませんでした。
後から、直接HPへ行って、個別にアバター名を入力して検索したら、何故かちゃんと取得できました。
この辺は英語のサイトなので、どう言う風になっているのかまでは分かりません。

これでIMとノートカードの配布は自動的に、次々と処理されていきました。
ここで、もう一つ注意が必要な事は、今回の仕様では、IMとノートカードを送信した結果を受けて、次のデータを顧客リストから読み込んでいるのですが、スクリプト内部で送信完了として処理していても、実態はタイムラグもあるようで、間をおかずに進めると、途中で「スピードが速すぎる」と言ってスクリプトが落ちてしまいました。
どうもWEBとのやり取りが早過ぎて駄目だったようです。

そこで、例によって「間」を5秒ほど空けて、次のデータを読み込んで処理しました。
2秒でも十分だったのですが、途中でふと思ったのです。
「100件以上も連続で機械的にWEB接続かけたら、それってスパム行為と思われないかな~・・・」と言う事です。
まあ、5秒空けても一緒なんですけどね。気持ちです。^ ^;

と言う事で、めでたく処理は完了しました。
ちなみに、スクリプトを2つに分けたのは、1つのスクリプトだと連続的な処理構造が複雑になってしまうからです。
仮に顧客リストを内部のリストに格納してからであれば、一つのスクリプトで完結できたと思います。
しかし、100人以上の顧客名のリストは、残念ながら内部のリストに格納しきれんませんでした。
そこで、1件づつ読み込んで処理させたのです。

ノートカードを読み込む場合、読み込む命令を発行して、実際にデータを取得する部分は、dataserverイベントで行われます。
そして、HTTPリクエストをした場合、その結果を取得するのは、http_responseイベントになります。
一つのスクリプト内で書く場合は、双方のイベントでキックし合うような構造になってしまいます。

そこで、処理を単純化するために、「顧客カードを読み込む処理」と「HTTPリクエストする処理」を別スクリプトとして分離しました。双方のやり取りにはLinkメッセージを使ってます。
これだとループ的に処理させても、ループ部分はカードデータの読込部分だけになるので、比較的分かりやすく記述できると思います。

今回作ったスクリプトは汎用性も高いと思いますので、今後の顧客管理等にも上手く活用できるかも知れません。
ただ、一つだけ気になった事は、「アバターのUUIDって、勝手に調べて利用して良いの?」と言う事です。
う~ん。。。こう言うのって悪用する人が居なければ良いが・・・と思ってしまいました。

だって、例のテロオブジェクトを無差別に自動配布するなんて事もできてしまうのですよね。
勿論最低アバター名が分からなければ駄目ですけどね。
まあ、そんなこと言っても、SL内のアバター検索で、適当に検索して、プロフィールから送信もできてしまうのだから、気にしてもしょうがない話ですが。^ ^;

  

2008年01月29日

JIRAにコメントを出してみた。

コイのリンク全解除現象ですが、その後の検証で以下の事が分かりました。
しかし、対策はまだ見出せないので、一旦コピー版も販売を停止しました。T T
写真は条件等を変えたものを並べて検証中の風景。(泣ける><)



また、JIRAにも同様の現象が上がってましたので、そちらにもコメントを書いてみました。上手く伝わるかどうか?分かりませんが、個人的にはお手上げ状態ですから、藁にも縋る思いです。><
JIRAのページはこちらです。
http://jira.secondlife.com/browse/SVC-1184

<検証結果(中間報告)>
鯉は体長1mが基準サイズで、その場合の胸鰭パーツは厚さ1cm以下です。
体長に比例してパーツも大きさが拡大縮小されます。

体長=1.5m:発生しません。
体長=1m:4~5日程度で全リンク解除
体長=0.5m:2~3日程度で全リンク解除
体長=0.3m以下:1日~2日程度で全リンク解除
胸鰭を厚くすると、体長1mでも発生しなくなります。(まだ様子を見る必要は有る)
しかし、それ以上は厚くできないので、1m以下の場合は発生してしまいます。(魚では無くなってしまうから)
また、胸鰭のアニメーションを止めるとサイズが小さくても発生しないようです。(現在最終確認中)
そして、アタッチして使用する場合は、サイズが小さくても発生しません。

今の状態ですと、コイは調整しても最低1mサイズが限界と思います。
クマノミは1mサイズにはできないので、方式を変えるしか無いでしょう。
現在胸鰭のアニメーションを止めて、プリム数は増えますが、胸鰭を両側に1つづつにして、それを回転系の関数で処理する事も検討中です。
しかし、一番自然な動きはアニメーションさせることなので、この機能が使えないのは、実に残念です。
  

2008年01月15日

マンタより大きな鯉

鯉のパーツ消失事件ですが、その後はパッタリと発生しなくなりました。何故じゃー???
まあ、起こらないに越した事は無いのですが・・・

で、現在店の水槽と海底では、テスト用に10秒毎にリンク状態をチェックし、リンクが解除されてしまったら、その時刻が分かるようにスクリプトを入れて試していいます。
が、一昨日にその対応してからは、何故か一向に発生しません。不思議ですねー。。。

ここまでの現象を整理すると以下のようになります。
1.発生対象
 コイとカレクマノミ(単純回転タイプもフリームーブタイプも両方発生)のみに発生している。
 サメ、ナポレオンフィッシュ等には発生していない。
 また、コイも体長が1.5mのBigに設定してあるものには発生していない。
2.現象
 強制的に全リンクが解除されてしまっている。
 ヒレとヒゲが消失したのは、物理属性のため、リンクが解除されるとヒレとヒゲは浮力を失って落ちてしまうからで、ヒレの一部は回収できた。が、何故かヒゲは全く何処にも見当らず回収できていない。これも謎。
3.その他
 気がついたのは1月1日にSASUKEで回転カクレクマノミのリンクが解除されて止まっていた時が最初だが、コイに関しては12日に初めて気がついた。恐らく1日~12日までの間に発生している。
 更に最初に発生したのは、1固体を除き全て0.5m以下の小さいサイズであった。
 13日には体長1mのMiddleサイズの物にも発生している。

上記の事から、どうも大きさにも関係があるように思えます。
そこで、現在はテストとして、ナポレオンフィッシュも体長0.5mにして試し始めました。
また鯉も体長3mと10mの巨大な物を作成して泳がせ始めてみました。
これらで暫く様子を見てみたいとも思います。

で、コレが体長10mの鯉。マンタよりも大きいよー><



それと、同様の現象は既にJIRAにも上がっているようです。
知人から情報がありました。私はまだその情報を見つけられずに居ます。やはりSculptedアニメを使ったサルのペットのようです。知人にURLを聞いて、見てみたいと思います。

とにかく、今月中は調査をしてみて、もし発生しないようなら、コピー不可の普及版の販売も再開したいと思います。
あ~しかし、疲れるなー  

2007年12月29日

パーツズレの結論

ナポレオンフィッシュのリンクパーツがズレてしまう現象だが、その後の追加確認で以下のような結論に達した。

<現象>
llMoveToTargetで移動している状態で、同時に2つのパーツがSculptedアニメで動いている場合に、リンクされた子プリムの位置がズレてくる。
ズレるのは、Sculptedパーツだけでは無く、リンクされている通常プリムのパーツも同様にズレる。
ズレは非常に少しづつズレて行くようで、時間の経過と共にズレは大きくなる。
ズレる方向は一定しており、空中よりも水中の方が著しくズレる。水中では1時間程度でズレを認識できるが、空中だと1日以上経過しないと認識できない。

1週間泳がせ続けた結果がコレ↓


あれから検証用に状態を変えた物で色々と試した。検証風景はコレ↓


その結果は以下のようになった。
<検証>
1.サメやイッカク等llMoveToTarget移動時に本体が同時にSculptedアニメで動いていても発生していない。
2.ナポレオンフィッシュで胸鰭の動きを止めて泳がせた場合にもズレは発生しない。
3.同様に尾びれの動きを止め、胸鰭だけ動かしてもズレは発生しない。
4.胸鰭と尾びれを同時に動かしても、llMoveToTargetを止めて動かないようにした場合はズレは発生しない。(物理属性でも)
5.胸鰭と尾びれがSculptedアニメで動き、目玉とか他のパーツには動くスクリプトが存在しないカクレクマノミでも同様の現象が発生した。
6.非物理のまま胸鰭と尾びれを動かしながらllSetPosで移動させ続けてもズレ無い。
7.X軸方向のみ往復させた場合、ズレは発生するが程度は小さい。(空中と同程度かそれ以下)
8.Y軸方向のみ往復させた場合、ズレの程度は大きく、短時間でズレが確認できる。
9.各子プリム・パーツに10分間隔で相対位置をチェックし、規定位置からズレて居た場合、元に戻すスクリプトを入れるとズレは補正され、ズレた状態にはならなくなる。
<結論>
llMoveToTargetで移動している状態で、同時に2つのパーツがSculptedアニメで動いている場合に発生する。
ズレは水中でY軸方向への移動時に著しく発生し、X軸方向への移動及び空中での移動時にはズレは少ない。
ただし、何故このような状態の時に、リンクプリムの位置がズレるのか?と言う事は分からない。
この辺はリンデン社に問い合わせるべきなのだろうか?

と言う事で、非常に限られた条件で発生するようなので、一般的には認識されていない現象と思う。
で対処としては、ズレが一気にズレるのでは無く、徐々にズレて行くことから、適当な時間毎に、リンクプリムの相対位置をチェックして、ズレが認められたら、強制的に補正する事にで解決できる。

まあ、こんな所でしょう。
このようなオブジェクトを作る人は、他には居ないと思うのだが、仮に上記条件に該当するようなオブジェクトを作った場合は、ズレるので気をつけましょう。  

2007年12月26日

パーツ・ズレ現象に関して

ナポレオンフィッシュで起こった、遊泳中に各パーツがズレていく現象に関して記録しておきたいと思う。
まずは2日半遊泳させた場合の状況は、以下のように体からパーツが飛び出してしまう。



これは水中での使用時には著しく、2時間程度で目に見えてズレが生じる。胸鰭の方がズレの程度が大きい。目玉は胸鰭よりは小さいが、こちらも両目玉とも同じ方向へとズレて行く。
陸上(空中)での使用時には、これ程大きなズレは発生しない。が、5日間水槽内で遊泳させた物を見ると、やはりズレは発生している。コチラの場合は、胸鰭は向かって右に、目玉は前にズレているようだ。



サメ・シリーズとかでは、このようにパーツがズレる現象は発生していない。考えられるのは、ナポレオンのみで行っている仕組みに起因していると言う事になる。
試しに昨夜から泳がせて見た、ナポレオンと同様の作りをしているカクレクマノミも、程度は小さいが1日経過でやはりズレが見られる。
この場合は胸鰭と背ビレ部分も同じ方向にズレてきている。



具体的なナポレオンの構造を以下に示す。↓



各パーツでの稼動スクリプトは以下のようになる。
目玉:タイマーで2秒毎に左右別々に乱数で回転角度を発生させ、llSetLocalRotで目玉を少し回転させている。
胸鰭:2枚のSculptedテクスチャを2秒毎に切り替えて、胸鰭が動いているように見せている。
本体:llMoveToTargetで移動しつつ、同時に2枚のSculptedテクスチャを2秒毎に切り替えて、尾びれを動かしている。

従来品と異なる点は、本体以外のパーツでもSculptedテクスチャの張替えを同時に行っていると言う点だ。
同様に2箇所同時にSculptedテクスチャを張替えている、カクレクマノミも1日経過時点で同様の現象を起こしている。

更に今回の現象で不可解な事は、ナポレオンはフリー・ムーブだからランダムに方向転換して、確率的には前後左右同等にズレが発生する可能性を持っている。
にも関わらず、ズレる方向が常に一定しているのは何故であろう?(水中と空中では若干異なるようだが・・・)
これは設置場所に関係無く、本体の左側へとズレて行く。
カクレクマノミでも同様で、同じ方向へとパーツはズレる。

それと、空中と水中では顕著にズレの程度が違う。
これも、もしかしたら物理属性で動いているから、空気中より水中の方が抵抗が大きくて、その影響で差が出ているのかも知れない???
ってそんな所までSLの世界は再現しているのだろうか?だとすると凄いな~。。。

で、対処なのだが、非常に泥臭い方法ではあるが、リンク位置を一定時間毎にチェックして、もし最初の位置とズレていたら、元に戻すと言う処理を、各パーツに入れてみた。
具体的には次のような内容になる。

vector base_pos;
float t=600;
default
{
state_entry()
{
base_pos = llGetLocalPos();
llSetTimerEvent(t);
}
timer(){
if(base_pos != llGetLocalPos()){
llSetPrimitiveParams([PRIM_POSITION, base_pos]);
}
}
}

これで補正してやった場合、2日半経過時点でのズレは見られない。



もともと1時間に数cmレベルのズレなので、10分間隔でチェックしていれば、まず問題は無さそうだ。スクリプト負荷的にも影響は微々たるもののようなので、この状態で安定するなら、販売を開始したいと思う。
ただ、原因が明確には分からないので、それが気持ち悪い。

多分今後はSculptedテクスチャを張替えた、アニメーション効果を利用したオブジェクトが増えると思う。その場合の制約事項になるのであれば、ちょっと悩ましい気もする。
現時点で知っている限り、2パーツを同時にSculptedでアニメーションさせている物は無いと思う。

ちなみに、最近はSASUKE SIMでスクリプト負荷のチェックが具体的にできるので助かる。
ナポレオンに関して言えば、サメやマンタと同程度と言う事で、地域/不動産のデバッグ画面で調べても、0.03ms~0.05ms程度であった。  

2007年12月19日

回転マンタ・スクリプトの総括(番外編)

これで最後ですが、今回の製作時に気が付いた点、困った点等を番外編としてまとめておきます。

1.販売モードの問題
 回転マンタは当初次の2タイプでの販売を考えていました。
 ・「編集不可」「コピー不可」「再販可」
 ・「編集不可」「コピー可」「再販不可」
 ところが様々な制約の関係上、可能となったのは次の2タイプでした。
 ・「編集不可」「コピー不可」「再販不可」
 ・「編集不可」「コピー可」「再販不可」

 

 ところが、表示上は上記の通りなのですが、実態はまた違うのです。
 詳しく書くと、
 ・回転軸オブジェクト:「編集可」「コピー可」「再販不可」
 ・回転軸スクリプト :「編集不可」「コピー不可」「再販可」
 ・コンテンツ内マンタ:「編集可」「コピー可」「再販不可」
 ・マンタ内スクリプト:「編集不可」「コピー不可」「再販可」
 と設定されています。結果として総合では「全て不可」になる訳です。
 「編集不可」「コピー可」「再販不可」タイプはスクリプトも同じ設定にするから、総合でも同じタイプになります。

 この辺のモード設定は、けっこう悩ましいです。良くあるのは、オブジェクトの設定とコンテンツ内のテクスチャとかスクリプトの設定が違っていて、思うようなモードにならずに販売してしまったとか・・・気をつけましょう。

 で、結局回転マンタは再販可能バージョンが作れなくなってしまったのです。これはけっこう痛い結果です。プレゼント品としては使えないと言う事ですよね。基本的には再販可能モードが一番望ましいと思っていました。不要になったら、誰か欲しい人にでもプレゼントして、長く使ってもらいたかったからです。
 その希望は敢え無く消え去りました。。。

2.編集ができてしまう問題
 実態はオブジェクトの編集ができてしまいますから、次のような問題が発生し得ると言う事です。
 ・オブジェクトサイズの変更
  これはリンク距離にも関係してしまいますから、スクリプト内で強制的に最大サイズにしたりしてますが、それでも変えられる事にかわりは無いです。
 ・コンテンツ内の変更ができてしまう。
  スクリプトは「編集不可」ですから、スクリプトを書き換えられる心配はありません。しかし、スクリプトを削除する事は可能です。そして、別のスクリプトを入れる事も可能です。
  この辺に関して、どう考えるか?悩みどころです。

3.リンク解除やリンク追加なんてのもできちゃう。
  編集可能だから、リンクの追加や解除なんてのもできてしまうんですね。

 
 
 回転中のマンタに、更に別のオブジェクトをリンクさせるとか。。。まあ、別にいいじゃん!と言えばそれまでなんですが・・・
 ちなみにリンクの解除をすると、マンタは自動的に消滅してしまいます。
 その際、回転軸は透明化されたままなので、一見全て消えてしまったように見えて、けっこう慌てるかもしれません。
 あ~。。。そう言うのにも対処しておかないと駄目かな~。。。どんどん面倒な事が増えていく。

 そして、姑息な手段ですが、現在の仕様の場合、マンタの中の消滅スクリプトを削除してしまって、リンクを解除すると、マンタ単独のオブジェクトが手に入ってしまいます。
 もっとも「再販不可」だから、そうなっても別に構わないと言えば構わないのですが・・・でも、知らない所で、違う使い方されているのは、ちょいと気持ちが悪いかも?

 この辺はSculpted Dolphins Shopのイルカとオルカも同じです。実際に試してみたら、オルカのオブジェクトが単独で手に入りました。
 が、Sculptedのアニメーション部分は動作しなくなってしまいます。それが動けば、自分のフリー・ムーブ・スクリプト入れて泳がせようと思ったのに・・・一応先方も色々考えて対策しているみたいですね^ ^;

4.マンタのリンク距離に関して
 これがちょっと不思議だったのですが、βグリットと本番とでは少し違いました。
 マンタのサイズ別最大リンク距離は以下のようになります。
           β   本番
 Super Big 29m  27m
 Big       26m  29m
 Middle    24m  28m
 Small     21m  24m
 βグリットの方がマンタの大きさによって距離が伸びており、予想通りの数字だったのですが、そのまま本番で試すと、SuperBig以外は距離が伸びて、SuperBigだけは縮んでしまいました。結局は両方の値を参考に最大可能距離を決めましたが、この辺はSIM環境にもよるのかも知れないので、もう少し追加調査もしてみたい気がします。

と言う事で、簡単と思って始めた回転マンタは、このように厄介な状況を引き起こし、更には途中でリンデン社のサポートまで巻き込んで大騒ぎした事件も引き起こし(自業自得だっちゃ)、いまだに販売するかどうかも決めかねている状況です。
まあね、スクリプトに関しては色々と勉強できたことだし、この経験はきっと今後の物作りに役立つであろう。。。
そう思って、締めくくりたいと思います。

しかし、回転マンタは売るかな?って売れるかな?どうしよう?
まだまだ悩みは続く・・・  

2007年12月18日

回転マンタ・スクリプトの総括(3)

さてさて回転マンタ・スクリプトの総括の続きです。

4.llCreateLink
 リンク生成の関数です。先にマンタが最後に自分のUUIDをメッセージで飛ばしていたのは、このためでした。
 llCreateLink(key target, integer parent)
 key targetにリンクする相手のUUIDを指定する必要があります。
 integer parentにはTHRUEかFALSEを指定します。THRUEなら自分をルートにし、FALSEなら相手をルートにします。
 今回のケースでは、回転軸側で発行していますので、THRUEを指定しています。

 ここで幾つか問題になった事がありました。
 ・リンクするオブジェクトに対して、オーナーは編集権限を持っていないといけない事。
 ・リンクが成功したか、失敗したかの判断方法。
 ・アバターがオブジェクトに座る行為もリンクと見なされる事。

 まずはリンク結果の判断方法ですが、これも色々考えられます。
 (1)Changeイベントで判断する
  今回使っているのはこの方法です。リンクが成功すればChangeイベントが発生します。
  実際には以下のような処理をしてます。
  changed(integer change){
   if (change & CHANGED_LINK) {
    llSetTimerEvent(0);
    status = 1;
    llMessageLinked( LINK_SET, status, msg_sta, NULL_KEY);
    state moving;
   }
  }
  これはif文でリンクの状態が変わったかどうかを判断しています。今回の仕様では、リンク生成を発したオブジェクトは、その時点では単独オブジェクトですから、リンク状態が変わった=リンク成功と考えています。
  ただ、Changeイベントだけでは、リンクが失敗した場合は判定ができません。
  仮に先のif文でelseを指定した場合はどうなるか?それは、リンク以外での状態が変わった事を意味します。オーナーが変わったとか、大きさが変わったとか・・今回はリンクが変わった時のみを対象としてますから、elseの指定は無いです。
  また、リンク生成を発生させた後、タイマー設定して、タイマーイベントが発生したら、リンクが失敗したと判定させています。
  もしリンクが成功していれば、changeイベントが先に発生しますから、タイマーイベントは発生し無い事になりますので。

 (2)リンクNumberで判断(llGetLinkNumber)
  llGetLinkNumber()で自分のリンク番号を取得して判断する事もできます。
  単独プリムの場合はllGetLinkNumber()=0になります。自分をルートとしてリンクに成功するとllGetLinkNumber()=1に変わります。
  ただし、この場合もタイムラグ等もあるので、一定時間待って番号を取得した方が良いでしょう。1秒待って番号を取得して、番号が変わって居なければリンクは失敗したと判断するとかです。

 (3)リンク失敗のシステムメッセージを取得できないか?
  結論から言うと取得できないようです。この方式は断念しました。
  参考までに書いておきますと、リンク失敗した場合には次のようなダイアログとシステムメッセージが出力されます。
  
  
  このメッセージを取得できれば、確実にリンク失敗を検知できることになります。
  この点に関しては、MizさんのBBSに投稿し、Mizさん自身からもご意見をいただきました。結論はデバッグチャネルにも通常チャネルにも出力されていないようで、lissenイベントでは取得できませんでした。うーん。。。残念。

 今回(1)の方法を採用した理由は、単にchangeイベントを使ってみたかったからです。
 まあ、最終的には(1)と(2)の両方を使って、確実にリンク状態を把握して対応する事になりましたが。
 そして、もう一つの問題である、座られてもリンク状態が変わると言う点ですが、これは無視する事にしました。理由は「座れないから」です。
 フリー・ムーブ・マンタで以前書きましたが、完全ファントム化すると座れないのです。座れないオブジェクトで有る以上、座られた時の状態変化を考慮する必要は無くなります。

 さて、残る問題はオーナーに「編集権限」を与えなくてはならない事です。
 実はこれも厄介なのです。
 今回の仕様では、回転軸オブジェクトをSculptedで作って、見た目は小さく、実は10m四方の大きなオブジェクトにしています。これはマンタのリンク可能距離を最大化するためです。
 ところが、編集可能な状態にしてしまったら、購入者が勝手に回転軸プリムを小さくできてしまう事になります。これでは、せっかく最大距離までリンク可能にして。その範囲チェックまでしていても意味が無くなってしまいます。
 でも「編集権限」が無いとリンク生成ができません。。。と言う事で、姑息な手段をとりました。
 スクリプト内で、強制的にスケールを10m四方にしてしまう事です。勿論マニュアルで変更できてしまうのですが、リンク生成する直前には、必ず最大サイズにスケール変更かけてリンク命令を出すようにしました。
 これなら、確実にリンク距離が保障できますから。

 しかし、リンク生成に関しては、更なる問題も発見しました。
 それは、「対象オブジェクトは同じ権限(次のオーナーが実行できる操作)状態で無いとリンクできない!」と言う事でした。
 具体的には、回転軸プリムは「編集可」「コピー不可」「再販可」に設定していました。そしてコンテンツ内のマンタは、「編集可」「コピー可」「再販不可」にしてあったのですが、この状態で他の人へ渡して使わせるとリンクエラーが出てしまいます。
 エラーは「 Link failed -- cannot link no-copy with no-transfer」と言う物です。

 

 どうも、回転軸プリムとマンタとで権限が違うためにエラーになったようです。
 試しに両方とも「編集可」「コピー可」「再販不可」に揃えたらエラーは消えました。同様に、「編集可」「コピー不可」「再販可」に揃えてもエラーにはなりません。正常にリンクできました。
 でも、「編集可」「コピー不可」「再販可」の設定では、on_rezで書いたとおり、1回Rezしたらマンタがコンテンツ内から消えてしまう事になってしまいます。結局は「編集可」「コピー可」「再販不可」に揃える以外には無いと言う事です。

5.llBreakAllLinks
 前項がリンクの生成でしたが、今回はリンクの全解除です。
 何のために使っているか?それは、回転しているマンタを再設定する場合に必要だからです。
 今回の仕様では、ダイアログでサイズ、回転方向、回転半径の設定ができますが、これらの設定内容は、マンタをコンテンツ内からRezした時に反映するようになっています。
 ですから、動いているマンタを止めて、再度ダイアログで設定する場合には、一旦マンタを消滅させないといけません。
 そこで、一度リンクを全解除し、その後マンタの部分だけを消滅させると言う訳です。

 この仕組みを書くと次のようになります。
 (1)マンタ(又は透明ながら回転軸)にタッチ
 (2)停止命令がリンクメッセージで発令
 (3)停止命令にそって、回転軸スクリプトはリンク全解除を発令し、透明化を解除してダイアログを表示
 (4)マンタ側の各パーツはリンクが解除されると、自分自身を消滅させる。
 と言う具合です。

 llBreakAllLinksもllCreateLinkと同様に、オーナーには対象オブジェクトに対する編集権限が必要になります。
 又、今回は全解除してますが、リンク番号を指定した部分解除も可能です。その場合はllBreakLinkを使います。

6.llDie
 プリムを消滅させる命令です。
 先に書いたようにリンクを全解除して、それぞれのパーツ毎にllDieを記述して消滅させています。
 勿論回転軸には書かれていません。
 が、リンク解除命令発動後、即各パーツで実行すると、何故か回転軸も巻き込まれて消滅してしまいます。
 リンク生成やリンク解除時には、若干のタイムラグがあるそうです。リンク生成時にも1秒程度のタイムラグがあるとlslWikiにも書いてありました。 解除時も同様のようです。
 
 実際のスクリプトとしては、今回もchangeイベントを使用しています。
 default
 {
  changed(integer change){
   if (change & CHANGED_LINK) {
     if(llGetLinkNumber()==0){
      llSleep(0.5);
      llDie();
     }
   }
  }
 }
 てな感じですね。
 念のためllGetLinkNumber()==0で自分が単独プリムになっている事まで確認しています。
 そして、0.5秒待って消滅!と言う事です。

 llDieはもう一箇所使ってます。
 それは、リンク生成失敗時の処理です。リンク生成に失敗したと回転軸プリムが認識したら、「死ね!」とメッセージを叫びます。それをマンタは受け取り、「ご主人様の命じるままに。。。」と自ら消滅するのです。
 なんとなく哀れな感じもしますけど、まあしょうがないですね。そのままでは動けないのですから。

と言う事で、これで新たに使ってみたスクリプト関係のお話は終わりです。
最初は簡単そうだから・・・と思って始めた回転型マンタでしたが、実際には非常に面倒でした。しかし、どうしてもサイズ調整やリンク距離の調整、回転方向等を設定で入るようにしたかったので、ここまで複雑になってしまったのです。
そして、何より厄介だったのは、マンタをバラさずに調整したいと言う事でした。
マンタを分解した状態で、先の調整を実現する方が遥かに厄介に感じていたのです。でも終わってみると、個々の位置関係を詳細に記録して、最初からリンクした状態で調整した方が簡単だったかな?などと思えてしまいます。

現時点でもこのスクリプト仕様で販売しようか?どうしようか?まだ迷っています。
それはまだ販売上は問題になりそうな部分が残っているからです。その辺は次回「番外編」で書きたいと思います。  

2007年12月18日

回転マンタ・スクリプトの総括(2)

さて前回の続きです。

3.on_rezイベント
 on_rezイベントは目新しいイベントではありません。今までも必ず使っていたイベントです。
 これはRezした時に発生するイベントで、良く使う使い方としては、次のような物です。
on_rez(integer param)
{
llResetScript();

 これはRezした時にスクリプトをリセットさせるものです。以前も書いたのですが、スクリプトの一番最初に、オーナーのUUIDを取得したり、各種変数の初期化をしたりする事が多くあります。それらを最初からセットし直したい場合等は、このイベントを使って、必ず最初から実行するようにさせています。

 今回は、このような単純な使い方では無く、回転軸オブジェクトからRezされた時に行う処理として使っています。
 以下に実際のスクリプトを示します。
 float radius;
 float def_size = 3.0;
 integer size_no;
 list scale_p = [1.5,3.0,4.5,6.0];
 float f;
 integer chanel = -3;
 integer i;
 integer j;

 default
 {
  on_rez(integer param){
   if(param>0){
    size_no = (integer)llGetSubString((string)param, 0, 0)-1;
    radius = (float)llGetSubString((string)param, 1, 2);
    if(size_no!=1){
     f = llList2Float(scale_p,size_no)/def_size;
     llMessageLinked( LINK_SET, 90, (string)f, NULL_KEY);
    }
    llSleep(0.5);
    j=llFloor(radius/10.0);
    for(i=1;i<=j;i++){
    llSetPos(llGetPos()+<10.0,0.0,0.0>);
    }
    llSetPos(llGetPos()+<(float)radius-j*10,0.0,0.0>);
    llShout(chanel,llGetKey());
   }
  }
 }
 これはon_rezイベントだけのスクリプトで、マンタがRezされた時のみ実行されます。
 最初のif文はRez時に引き渡されたパラメータ内容のチェックです。parm=0と言う事は、オブジェクトからRezされたのでは無く、単独で持ち物からRezされた場合を意味します。
 もし持ち物からRezされた場合は何もしません。(これは制作時の為の条件です。販売時には持ち物からRezされる事は有り得ませんから)
 parm>0と言う事は、何らかの値が引き渡されていると言う事ですから、その値の1桁目をサイズNOとして切り出し、残りの2桁をリンク距離として切り出しています。
 そして、取得したサイズNOとリストで保持しているサイズ表から、標準(マンタ本体が3m)との比率を求めて、その結果をマンタの各パーツへとリンクメッセージで伝達しています。
 勿論各パーツには、このメッセージを受け取って、自分のサイズとマンタ本体からの相対位置調整を行うスクリプトが入っています。

 更にマンタは0.5秒待って、指定されたリンク距離へと移動をします。0.5秒待つ理由は、各パーツのサイズ調整が揃わないケースが発生したからです。
 ここが以前暴走して問題になった箇所で、llSetPosで一回に移動できる距離が10mだったので、指定が25mとかの場合は、10m単位で2回移動させ、その後端数の5m移動するような仕組みが必要でした。
 その部分がforループとループを抜けた後に出てくるllSetPosの部分です。

 そして最後に自分のUUIDを取得して、llShoutでメッセージを飛ばしています。勿論相手は自分をRezした回転の中心軸オブジェクトに対してです。
 llShoutを使っているのはllSayが20mまでとなっており、リンク距離が20m以上になっていた場合、声が届かないからです。
 llShoutなら100mまで声が届きますので。

 この部分では、まだ未確認の問題も残っています。
 それは、進入禁止とかプリム設置制限一杯の土地にマンタが移動しようとして、侵入できずにエラーが出た場合です。
 その場で止まるだけなら問題は無いですが、一応確認は必要だと思っています。
 それと、この場合はリンクできたとしても、設定距離に達していないので、実際の遊泳半径を求めて、設定値を変えておく方が本当は良いのでしょうね。そこまではまだ対応していないです。><

 うー。。。なんやかやと面倒じゃのー。  

2007年12月17日

回転マンタ・スクリプトの総括(1)

今回初めて回転タイプの商品開発に取り組んだ訳ですが、その結果今まで使ったことの無いイベントや関数を色々と駆使する事になりました。
今回新たに使ったイベント、関数類は以下のようになります。
<イベント>
・run_time_permissions
・change
・on_rez
<関数>
・llRequestPermissions
・llRezObject
・llCreateLink
・llBreakAllLinks
・llGetLinkNumber
・llDie
随分と色々使ってます。^ ^
正直言って、こんなに面倒だとは思っていませんでした。でも、スクリプトの勉強にもなったし、それなりに面白かったとも思います。
これらをここで総括しておきたいと思います。けっこうなボリュームになるので、何回かに分けて記録しておきたいと思います。

その前に通常の回転タイプのお魚と言うのは、これから紹介するような複雑怪奇な処理をしている訳ではありません。
多くの商品は、単純にお魚テクスチャを貼った板に回転軸プリムをリンクさせたり、円筒系プリムの表面にテクスチャを貼って、llTargetOmegaと言う回転関数で回しているだけです。
それらはテクスチャさえ作れれば、初心者でも簡単に作れるレベルの物です。
今回のお話は、中級レベルのスクリプト知識が無いと作れない内容です。これと同等の機能を実現した商品は、現在もまだ少ないでしょう。
と前置きは終わりにして本題に入りましょう。

1.パーミッションの取得関数:llRequestPermissions
 これは何か特別な事(お金を扱う場合、アバターのアニメーション、オブジェクトのリンクの変更、カメラ(視点)の制御、キー入力の判定など)を実行する時に、その許可を得る行為です。
 許可無く実行されると、利用者に損害を与えかねないような場合、必ず同意を得てから実行させるために存在します。
 今回の場合は、オブジェクトのリンク生成と解除の為に、このパーミッションの取得が必要でした。
 実際に使ったのは、次のようなリンク関連のパーミッションです。

 llRequestPermissions(key_id, PERMISSION_CHANGE_LINKS);

 ここでKey_idには事前に取得しておいた、オブジェクトの所有者のUUIDがセットされています。つまり、該当オブジェクトのリンク変更に関する許可を得ていると言う事です。
 この関数を実行すると次のようなダイアログが表示されます。



 このパーミッション取得関数は、run_time_permissionsイベントと連動します。run_time_permissionsイベントはダイアログの入力結果によって発生するイベントです。
run_time_permissions(integer perm) {
if (perm & PERMISSION_CHANGE_LINKS){
link_flg=ON;
}
}
 ここでは「はい」が押された場合に、パーミッションを取得していると言う意味で、取得済みフラグをONにして記憶させています。
 この辺の扱いに関しては、色々な仕様で考える事ができますが、今回はこの時点で許可が得られない場合でも、後続の処理を実行するようにしています。許可が得られないと後続処理をさせたく無い場合は、elseで「許可をして下さい」とメッセージを出して、再度パーミッション取得の状態へ戻して、許可ダイアログを出させる事もできるでしょう。
 また、パーミッションは1度取得すれば良く、取得済みの状態でTakeして、再度Rezしてもパーミッションの許可は有効でした。ただし、オーナーが変わった場合は、再度そのオーナーでのパーミッション取得が必要となります。
 今回はその辺も考慮して対応しています。

2.llRezObject
 これはコンテンツ内のオブジェクトを、スクリプトを使ってRezする関数です。回転マンタでは、マンタのサイズ、遊泳半径、回転方向をダイアログで設定できるようにしましたが、その都度設定内容に合わせてRezし直す方式を採用しています。

 llRezObject(llGetInventoryName(INVENTORY_OBJECT, 0), llGetPos(), ZERO_VECTOR, llEuler2Rot(<x_axi, 0.0, z_axi>* DEG_TO_RAD), (integer)parm);

 今回はこのような使い方をしています。パラメータとしては次のような内容です。
 (1)コンテンツ内のオブジェクトName
   llGetInventoryNameでコンテンツ内のオブジェクト0番の名前を取得してセットしてます。
 (2)Rezする位置
   llGetPos()で自分自身と同じ位置にRezするようにしてます。
 (3)Rezオブジェクトに加える力
   今回は必要無いのでZERO_VECTORを指定してます。
 (4)Rezしたオブジェクトの回転角度
   ここはダイアログで設定した回転方向に合わせて、事前に角度を求めてRezした時のオブジェクトの向きをセットしてます。
   時計回りに回す場合と、反時計回りとでは、マンタの向きは反対になりますので。
 (5)Rezオブジェクトへの引数
   ここで設定した内容は、Rezされたオブジェクト側で参照して使う事ができます。マンタの場合はサイズとリンク距離を合成してセットしています。マンタ側は、この値に基づいて自分のサイズを変更し、リンク距離まで移動する事になります。

 海洋生物でのllRezObjectの利用例としては、TempオブジェクトのRezzerBoxで使われています。クラゲとかで使われている例が多いようです。
 TempRezzerとして使う場合は、コンテンツ内のオブジェクト側でTemp(臨時)オブジェクトになるような設定が必要です。
 TROPICAL FISHで販売している「Free Swimming Tropical Fish」の場合は、単独でRezして使用した場合は、Tempでは無く、普通の状態で使えますが、RezzerBox経由で使う場合は、同じオブジェクトがTempオブジェクトでRezされます。
 この辺はスクリプトでRezzerBoxからRezされた場合にTempオブジェクトに設定するようにしていると思われます。

 実はこの部分で、少々問題がありました。
 それはコンテンツ内に入っているオブジェクトは、コピー可能状態で無いと困ると言う事です。
 理由はコピー不可だと、一回実行してRezするとコンテンツ内からオブジェクトが消えてしまうからです。まあ、当然と言えば当然ですが・・・
 そのため、コンテンツ内のオブジェクトはコピー可能に設定して入れなければなりません。販売上は「再販可能」で「コピー不可」が望ましいのですけどね。。。
 更にこの辺の扱いに関しては、他でも色々と問題が出てきて・・・とそれは先の項目で詳しく説明したいと思います。

と言う事で、続きは後日。。。  

2007年12月09日

回転タイプは面倒だった。

う~。。。思ったより、ずっと面倒だった。
何とかできたけど。。。止めれば良かったかも?



回転マンタの機能をおさらいすると、
1.サイズ変更機能(全長2.5m,5m,7.5m,10mの4段階)
2.回転半径変更機能(1m~25m)
3.回転方向変更機能
の3つになります。
本来であれば、ダイアログから此処の設定を指定時に変われば一番良いのですが、それが困難でした。
理由は、マンタが4パーツから構成されており、それぞれのパーツ位置調整が難しいからです。

例えばマンタだけで考えれば、マンタの胴体(ルートになる)を中心に、各パーツの位置を調整すれば良いのですが、回転タイプではルートが回転軸になってしまうため、回転軸との相対位置では、マンタとしての構成パーツの位置が単純には合わないのです。

例えば、フリームーブタイプの場合のサイズ調整機能は以下のような感じになっています。
float f = 変更後の大きさ/変更前の大きさ;  //体長の変更比率を求める。
llMessageLinked( LINK_SET, status, (string)f, NULL_KEY);

このリンクメッセージを受信して、変更比率からllSetScaleで大きさを変え、更にllSetPrimitiveParamsで位置を微調整しているのです。
link_message(integer sender_num, integer st, string msg, key id){
 f = (float)msg;
 llSetScale(llGetScale() * f);
 llSetPrimitiveParams([PRIM_POSITION, llGetLocalPos() * f]);
}

ここで位置の微調整に使っているのがllGetLocalPos() です。つまりルートからの相対位置に対して、大きさ変更による比率を元に調整している訳です。
これが回転タイプだと使えなくなってしまうのですね。ルートが回転軸ですから、そこからの相対位置を元に調整したのではあわなくなってしまいます。あくまで、マンタ本体の中心を基準に調整しないと駄目ですからね。

回転方向を変えるに至ってはお手上げです。なにせ、マンタの向きを逆にしないといけません。すると、今まで右側にあったヒレは左側へ移動する事になります。尾びれも反対側に来ます。
回転の位置が苦手の方向音痴ゴンベとしては、こうなっては何が何やら?訳が分からずお手上げ状態です。

で、結局Sculpted Dolphins Shop方式で、ダイアログの設定内容を元に、次の手順で反映させる方式に至った訳ですね。
1.ダイアログで設定内容を入力
2.マンタ側にダイアログで設定した内容をパラメータで渡して、標準のマンタオブジェクトをRez
3.マンタ側はパラメータ内容を元に、サイズ調整と半径の位置へと移動
4.マンタと回転軸をリンク
5.回転開始
となる訳です。
そして、タッチして再設定したい時は、一旦マンタを消滅させて、再度Rezして反映させると言う事です。

しかし、これもけっこう面倒なスクリプトでした。
リンク設定・解除のパーミッションを取得したり、メッセージ交換でUUIDを受け取ったり、更に厄介なのは自爆時に回転軸を巻き込んで自爆してしまい、全てが消えてしまうとか・・・(T_T)ウルウル
時には、マンタの片翼だけ大きさが違うとか・・・(T_T)
とにかく焦りまくりでした。(^_^;)  
続きを読む