ソラマメブログ
プロフィール
mina Cookie
mina Cookie
IMに関してはこちら必読ください!
※asa Soyerは幽霊スタッフですw
アクセスカウンタ
< 2024年05月 >
S M T W T F S
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

2008年02月25日

【asa】おーねがいattach!attach!

そこにattach! あーなーたーかーらーーーーーーattach!(なに
 
と、このブログの「もう一人の」幽霊ブロガーですみなさんこんちちわごきげんいかがでございましょうか。
個人的に「おおー!」と納得した話だったのでちょーひさしぶりに書いてみます。
「相変わらず字だけかよ!」というツッコミが入ることウケアイですが字だけです。
 
●●あたっち!あたっち!そーしーてーエラー!●●
オブジェクトをwearした時になにか(アニメとサウンド)し、外したら止める、というようなスクリプトを書いたのです。
もともとそういうスクリプトは書いてたのでそれを流用していました。
が、それを押しつけた^H^H^H^H^Hお渡しした何人かの方から「スクリプトエラーが出る!」と御指摘を受けました。
 
一度直したつもりだったのですが、実はなおっておらず、また再現性もない状況でどうしたものかと悩んでいました。
そんな中、颯爽とIMをくださった、私が勝手に師匠と崇拝するしげさんに色々アドバイスを頂き、原因が判明しました。
 
●●スクリプトをリセットするタイミング●●
結果的には「オブジェクトに仕込まれたスクリプトは、インベントリに納まる瞬間の状態を保持したままになる」という現象によるものでした。
ここから得られた教訓は、スクリプト入りのオブジェクトを誰かに渡す(売る時も同じ)場合はスクリプトの内部状態を初期の状態に近付けるのが良いということです。
a)スクリプトのReset直後に渡す。
 ただし、state_entry()等、かならず処理されてしまうイベントハンドラもありますので、そこは見越しておくこと。
b)スクリプト内のどこかいい感じのところでllResetScript()が実行されるように作りこんでおく。
 これなら確実に渡した相手先で初期化できますが、使う場所を間違えると予期せぬ挙動を招くこともありますので十分に確認を。
 
これ、実は今回だけでなく、以前にも似たような案件(物理オブジェクトのエネルギー量)でしげさんに指摘受けてたことを思い出しました。しげさん、学習してなくてすみませんorz
 
●●ハマった箇所は?●●
さて、今回問題となったのは、全く同じ内容ではないですが、だいたい以下のような感じのスクリプトです。
このスクリプト自身は精査されていません。このまま流用しても動かないかもしれません。
あと、見掛け上のインデントをつけるために2バイト文字の空白を使ってたり、説明の都合上日本語のコメントを入れてますので、コピペしてもうまくないです;)
-----------------------------------------------------------------------------
string gSound_uffun = "uffun";
string gAnim_ahhan = "ahhan";
 
attach(key av_id){
  // 現在の許可情報を取得。
  // これで得る許可情報は「誰の」ということはお構い無しにとれてしまうという落し穴がある。
  integer perm = llGetPermissions();

  // アニメと同期させるためにサウンドの事前読み込み。
  // が、そもそもattachでこんなことするのが間違いといえば間違い。
  llPreloadSound(gSound_uffun); // ★1

  if(av_id != NULL_KEY){
    // このオブジェクトがアタッチ(wear)された時。
    // パーミッションの設定許可を実施。
    llRequestPermissions(av_id, PERMISSION_TRIGGER_ANIMATION);
  }
  else{
    // このオブジェクトがデタッチ(Detach)された時。
    // アニメとサウンドを停止する。
    if(perm & PERMISSION_TRIGGER_ANIMATION){
      // 許可設定がある場合にだけ、アニメ停止とサウンド停止を実施。
      llStopAnimation(gAnim_ahhan); // ★2
      llStopSound();
    }
  }
}

run_time_permissions(integer perm){
  if(perm & PERMISSION_TRIGGER_ANIMATION){
    // 許可されていればアニメ発動とサウンド再生を実施。
    llStartAnimation(gAnim_ahhan);
    llPlaySound(gSound_uffun,1.0);
  }
}
-----------------------------------------------------------------------------
 
以下、動作の流れを追って。
 
まず、初期状態(スクリプトのSave/Reset後インベントリに納めて)からwearした時にどうなるか?
1)wearされると、attachイベントハンドラが呼ばれます。この時、引数のav_idにはwearした人のIDが入ります。
2)llGetPermissions()の結果が納められているpermには何も設定されません。
3)llPreloadSound()で、サウンドファイルの事前読み込みが行われます。この処理は少し時間がかかります。
4)av_idにはwearした人のIDが入っていますので、
  if(av_id != NULL_KEY){
により、llRequestPermissions()が実行されます。
通常、llRequestPermissions()は水色のダイアログを出して許可/不許可の選択ができるのですが、attachイベントハンドラ内だと、どういうわけか無条件に許可されます。
5)llRequestPermissions()により、run_time_permissionsイベントハンドラが呼ばれ、llStartAnimation()とllPlaySound()が実行されます。
 
次に、このオブジェクトをDetachの時の流れです。
6)Detachされた時もattachイベントハンドラが呼ばれます。この時、引数のav_idにはNULL_KEYが入ります。
7)llGetPermissions()の結果が納められているpermには、llRequestPermissions()の結果が納められているので、PERMISSION_TRIGGER_ANIMATIONフラグが設定されています。
8)llPreloadSound()で、サウンドファイルの事前読み込みが行われます。この処理は少し時間がかかります。
9)av_idにはNULL_KEYが入りますので、
  if(av_id != NULL_KEY){
の判定は偽となり、elseブロックの、
    if(perm & PERMISSION_TRIGGER_ANIMATION){
      // 許可設定がある場合にだけ、アニメ停止とサウンド停止を実施。
      llStopAnimation(gAnim_ahhan); // ★2
      llStopSound();
    }
が実行されます。perm にはPERMISSION_TRIGGER_ANIMATIONが設定されているので、このif()文は真となり、llStopAnimation()とllStopSound()が実行されます。
 
と、いきたかったのですが、実際はこの通りにならないことがありました!
 
●●ここがイカんかったのか●●
オブジェクトがインベントリにある場合、スクリプトは「状態を保持して停止」した状態になるようです。
わかりにくい説明かもしれませんが、オブジェクトのDetachをし、アバターから外されたその瞬間の状態でスクリプトが止まった状態になっているのです。
具体的には(推測入りますが)、Detachした瞬間、LSLは8)のllPreloadSound()の処理途中で止まったままになっていたようです。
6)以降の処理がいつ実行されるか?
これはこのオブジェクトが再びwearやrez等によりインベントリから出てきた時です。
 
たとえば、私がこれをwearし、Detachすると、そのオブジェクトのスクリプトは8)のところで止まったままになります。
このオブジェクトを他の人に渡します。
渡された人がwearすると、8)で止まった状態からスクリプトが再度動きだします。
9)の処理では私がwearしていた時の状態値が残っているため、av_idは私のもの、permは「私に対するアニメ許可フラグ」が入っていて、wearした人の許可(wearした人によるllRequestPermissions())がありません。
このため、
    if(perm & PERMISSION_TRIGGER_ANIMATION){
ここは真であるにもかかわらず、実際にはllRequestPermissions()実行が行われていないのに★2のllStopAnimation()が実行され、スクリプトエラーとなってしまうのです。
なんていうか、まあ、すげーわかりにくい話だと思いますが^^;
 
●●その他のよもやま話●●
あと、attachイベントハンドラでllPreloadSound()は、特に他のサウンドに切替える等の処理が無いし、相当に無駄な処理です。state_entry()に放りこんでおけばOK。
 
もうひとつ、
  integer perm = llGetPermissions();
これを、許可判定の本当に直前で実行していれば良かったのかもしれません。
  else{
    // このオブジェクトがデタッチ(Detach)された時。
    // アニメとサウンドを停止する。
    if(llGetPermissions() & PERMISSION_TRIGGER_ANIMATION){
とか。
ここが偽だった場合もllRequestPermission()を、オブジェクトの所有者に対して行えば良かったのかもしれません。
    if(llGetPermissions() & PERMISSION_TRIGGER_ANIMATION){
      // 許可設定がある場合にだけ、アニメ停止とサウンド停止を実施。
      llStopAnimation(gAnim_ahhan); // ★2
      llStopSound();
    }
    else{
      llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION);
    }
等のように。
もっとも、これは今(動作確認できない状況で)思い付いただけなので、これで正しい動作をするかどうかわかりません^^;

●●まとめ●●
くりかえしになりますが、要するに、他人にスクリプト入りのオブジェクトを渡す場合、渡す前に、以下のようなことをやっておくのが良いでしょう、ということです。
a)スクリプトのReset直後に渡す。
b)スクリプト内のどこかいい感じのところでllResetScript()が実行されるように作りこんでおく。
 
再配布可能で渡すことを考えると、再配布をする方がスクリプトのResetを実施できるとは限らない(スクリプトがmod OKじゃないとできない)ので、しげさんに教えて頂いた通り、b)の対策を講じるのが一番良いようです。
 
うー長いなー・・・
だがまとまらぬ。許せ。


同じカテゴリー(学習)の記事画像
【mina】所持金0
同じカテゴリー(学習)の記事
 【asa】 ランラン乱数で おみくじ! (2007-11-09 00:37)
 【asa】スクリプトに日本語しゃべらせるアレの支援ツール (2007-08-25 02:53)
 【asa】気まぐれLSL tips:ちゃんと座ってよ;; (2007-06-04 16:09)
 【asa】ネタ製作とその実現3 ぱーみっしょん (2007-04-25 17:20)
 【asa】ネタ製作とその実現2 代替手段に学ぶ (2007-04-23 18:39)
 【asa】ネタ製作とその実現1 目指せハンカチ王子 (2007-04-16 11:07)

Posted by mina Cookie at 14:16│Comments(0)学習
※このブログではブログの持ち主が承認した後、コメントが反映される設定です。
上の画像に書かれている文字を入力して下さい
 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。