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はもう一箇所使ってます。
それは、リンク生成失敗時の処理です。リンク生成に失敗したと回転軸プリムが認識したら、「死ね!」とメッセージを叫びます。それをマンタは受け取り、「ご主人様の命じるままに。。。」と自ら消滅するのです。
なんとなく哀れな感じもしますけど、まあしょうがないですね。そのままでは動けないのですから。
と言う事で、これで新たに使ってみたスクリプト関係のお話は終わりです。
最初は簡単そうだから・・・と思って始めた回転型マンタでしたが、実際には非常に面倒でした。しかし、どうしてもサイズ調整やリンク距離の調整、回転方向等を設定で入るようにしたかったので、ここまで複雑になってしまったのです。
そして、何より厄介だったのは、マンタをバラさずに調整したいと言う事でした。
マンタを分解した状態で、先の調整を実現する方が遥かに厄介に感じていたのです。でも終わってみると、個々の位置関係を詳細に記録して、最初からリンクした状態で調整した方が簡単だったかな?などと思えてしまいます。
現時点でもこのスクリプト仕様で販売しようか?どうしようか?まだ迷っています。
それはまだ販売上は問題になりそうな部分が残っているからです。その辺は次回「番外編」で書きたいと思います。
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まで声が届きますので。
この部分では、まだ未確認の問題も残っています。
それは、進入禁止とかプリム設置制限一杯の土地にマンタが移動しようとして、侵入できずにエラーが出た場合です。
その場で止まるだけなら問題は無いですが、一応確認は必要だと思っています。
それと、この場合はリンクできたとしても、設定距離に達していないので、実際の遊泳半径を求めて、設定値を変えておく方が本当は良いのでしょうね。そこまではまだ対応していないです。><
うー。。。なんやかやと面倒じゃのー。
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まで声が届きますので。
この部分では、まだ未確認の問題も残っています。
それは、進入禁止とかプリム設置制限一杯の土地にマンタが移動しようとして、侵入できずにエラーが出た場合です。
その場で止まるだけなら問題は無いですが、一応確認は必要だと思っています。
それと、この場合はリンクできたとしても、設定距離に達していないので、実際の遊泳半径を求めて、設定値を変えておく方が本当は良いのでしょうね。そこまではまだ対応していないです。><
うー。。。なんやかやと面倒じゃのー。