ソラマメブログ
< 2008年08>
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            
プロフィール
mina Cookie
mina Cookie
オーナーへメッセージ
QRコード
QRCODE
※カテゴリー別のRSSです。

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)の対策を講じるのが一番良いようです。
 
うー長いなー・・・
だがまとまらぬ。許せ。
  
Posted by mina Cookie at 14:16Comments(0)TrackBack(0)Second Life 学習編

2007年11月09日

【asa】 ランラン乱数で おみくじ!

「おみくじ」を作ろう!

今の世の中、やっぱりおみくじですよね!
ん?なんか言った?いきなりなんだ?って??
おみくじですよ、おみくじ!!

とまあ、強引なツカミとともに不定期気まぐれ記事がやってまいりましたよ。
LSL?スクリプト?もうふつうに使ってるけど?
とか、
なにそれー喰えるんかー?やるきなんてないぜ!
とかいう方は適宜スルーの方向でお願いします。
LSLわっかんなーいしーでもぉーちょっとつかってみったーいかんじー、というくらいの方には参考にならなくもない感じでまとめてみました。

どうもLSLそのものの設計思想って、プリミティブな機能の充実よりマクロ的な機能提供が重点的な気がします。
今回のテーマ「おみくじ」は、なんてったって乱数私は乱数(you're a 乱数)乱数は~やめら~れな~いHey!!てくらい、乱数命です。
乱数についても、マクロ的な機能が意識されているなあ、、、という関数がありました。
それが今回の主役、llListRandomize()です。
LSLで提供されてる乱数っぽい機能にはllFrand()という関数とllListRandomize()という関数があります。
たいていのプログラミング言語ではllFrand()のように素直な乱数(通常は0~1未満の小数)の発生機能だけが提供されてます。
が、llListRandomize()という関数がちょっと不思議ちゃん。
てか、これ、使い方次第で便利モノです。

えーと前置き永井豪。
今回は、このllListRandomize()をつかった「おみくじ」作りをネタにしてみます。
llListRandomize()の機能を簡単に説明すると、list型変数(配列;文字列や数値の集合体)の中身をシャッフルするもの、となります。
ちょうどトランプをきるような感じですね。
たとえば、
list kikuzo = ["いやん", "ばかん", "うふふん", "そこは", "おみみなの", "っあ~ん"];
とかいう配列があったとします。
おい!LSL内に日本語使えないだろ!って言った奴誰だ!?
正しいです^^
これは説明をわかりやすくしたいという心意気だと思ってくださいませ。
本当に日本語を扱いたいのであれば、LSL内で日本語つかうような感じの時に便利っぽい感じ とか参考にしちゃってみちゃってくださいませとさりげなく宣伝化!

で、たとえばこのlist 型変数 kikuzo に対して、
list result_list = llListRandomize(kikuzo, 1);
とします。
すると、result_listの中身は、kikuzoの中身がバラバラになったものが入ります。
たとえば、
["うふふん", "そこは", "おみみなの", "いやん", "っあ~ん", "ばかん"];
とか。
バリエーション数は単純な順列で、リスト数の階乗個ですね。
因みに、llListRandomizeの第2引数(1)を2にすると2つ("いやん"と"ばかん", "うふふん"と"そこは", "おみみなの"と"っあ~ん"、のセット)毎にシャッフルします。
["うふふん", "そこは", "いやん", "ばかん", "おみみなの", "っあ~ん"]などのように。
llListRandomizeの第2引数を3にすると3つ毎にシャッフルです。

さてここであらためて、
list result_list = llListRandomize(kikuzo, 1);
について考えてみましょう。
この result_list というリストの、たとえば1番目には何がくるのかわかりません。
"いやん"かもしれないし"ばかん"かもしれないし"うふふん"かもしれないし"そこは"かもしれないし"おみみなの"かもしれないし"っあ~ん"かもしれない。
そして1番目にくる文字列の範囲は元の配列kikuzoの中に定義されたもの、ということになります。
つまり、
list result_list = llListRandomize(kikuzo, 1);
で得られた配列、result_listの1番目の値を見れば「指定された範囲の中からランダムでひとつ選択」ということが実現できます。
 
「配列の先頭をとってくる」にはllList2XXXX()という関数を使います。
XXXX部分は取ってくる型により事なります。llList2Integer(), llList2Float(), llList2Key(), llList2Vector(), llList2Rot()等。
ここで扱っている、"いやん"とか"ばかん"は文字列なのでString、すなわち、llList2String()を使います。
llList2XXXX()で、「リストの何番目をとってくるか?」の指示は第2引数です。
「配列のn番目を取ってくる」には、llList2XXXX()の第2引数にn-1の数を記します。
つまり「配列の1番目を取ってくる」には、1-1 = 0 を指定するわけです。
よって、
string result = llList2String(result_list,0);
これでresultには、 "いやん"または"ばかん"または"うふふん"または"そこは"または"おみみなの"が入ります。
 
llListRandomize()とllList2String()をつかうことで、たとえば以下のような簡単なおみくじが作れます。
このスクリプトを放り込んだプリムをtouchすると、おみくじの結果を叫びますw


list omikujibako = ["dai-kichi", "chu-kichi", "syo-kichi", "kichi", "kyo", "dai-kyo"];
default {
touch_start(integer num){
list result_list = llListRandomize(omikujibako,1);
string result = llList2String(result_list, 0);
llShout(0, result + "!!!!!!");
}
}



 
今日のポイント:
1) llListRandomize()でシャッフル!シャッフル!
2) llList2String()でリストからピンポイントで打つべし!打つべし!
3) llListRandomize()とllList2String()の組合せで無作為選択屋ケンちゃん(ぉぃ

いささか壊れ気味な感じですが、壊れてるのは最初からですからお気きになさりませぬよう。
ではまたいつか!

  
Posted by mina Cookie at 00:37Comments(5)TrackBack(0)Second Life 学習編

2007年08月25日

【asa】スクリプトに日本語しゃべらせるアレの支援ツール

なんか、すでにどなたかが作っていそうな気がするわけですが、、、
LSL(スクリプト)の中から、フローティングテキスト等で日本語を表示させる方法のアレです。

基本的にllSay()等でオブジェクトにしゃべらせたり、llSetText()でフローティングテキストを付けるのは英語にするのが良いと思っています。
しかし最近は事実上日本人だけが集まる場所も増えてますし、敢えて日本情緒を出すために日本語を使うという考え方もアリだと思うようになってきました。
といってもフォントはいってなければ全部「・・・・」になっちゃうわけですがまあきにすんな。

と、前置き長いけど、要するにLSLのエディターでは日本語書けないこともあり、オブジェクトに日本語しゃべらせたり、日本語のフローティングテキストを付けるにはちょっとしたワザが必要であるのが現状です。
そのワザというのが、日本語をある形式に暗号化したものをLSLに書いて、LSL内部でそれを復号化するというやり方です。

で、LSLが対応している暗号化として、url encoded形式(%hexの形式)とbase64形式があります。
要するに日本語を使いたい時は、日本語をこういった形に変換してやらんとならんのです。

で、その変換を行うcgiをさくっと書いて、こんなところに設置した次第です。
ま、すでに誰かが作ってあるのかもしれませんが、この程度のものは探すより作っちゃったほうが速かったのでということで。
基本自分用なんですが、使えそうーって感じだったらご自由にお使いくださいませー。
  
Posted by mina Cookie at 02:53Comments(2)TrackBack(0)Second Life 学習編

2007年06月04日

【asa】気まぐれLSL tips:ちゃんと座ってよ;;

えーと書くのいつぶりだろ、、、まあいいやw
生存証明っぽくちょっとしたtipsみたいなものを気まぐれで書いておくことにします。
たぶんあちこちで解説されてることなんだと思いますが、案外よく聞かれたり。
内容もカンタンで誰でもできます。サクっと覚えちゃいましょう。

[なんの話?]
乗物なり椅子なりソファーなり・・・Sitできるものを作った時、こんな経験ありませんか?
・自分の望む向きに座ってくれない><
・ケツが椅子に埋もれてるよorz
・ソコじゃないの、ソコじゃないのよ(謎)
困りますよね。アームレストに座っちゃったり、椅子の向きに微妙に合わなかったり。

[どうすればいいの?]
これは、ちょっとしたLSLを書くことでサクっと調整できちゃいます。
llSitTarget()という関数を使うことで、1つのプリムに対して1箇所1方向、Sitする位置を固定することができます。

[具体的には?]
llSitTarget()という関数に座標と回転を与えます。
座標には、そのプリムの中心点から、縦(X)、横(Y)、高さ(Z)方向にどれだけ移動した位置にSitさせるか?を指定します。vectorという型で指定します。
回転には、そのプリムの回転位置を基準に、どれだけ座る場所を回転させるか?を指定します。指定はrotationという4元数型で与えるのですが、これは非常にイメージしにくいものなので、それを補うためにllEuler2Rot()という関数を使います。llEuler2Rot()を使うと、Editモードの回転のように、X、Y、Z型での回転パラメータが与えられます。

まとめると、
llSitTarget(<縦(X)位置, 横(Y)位置, 高さ(Z)位置>, llEuler2Rot(<X方向角度, Y方向角度, Z方向角度>*DEG_TO_RAD));
こんな感じです。
DEG_TO_RADというのはおまじないみたいなものだと思ってください。ただの単位変換(DegreeからRadian)のための係数です。

たとえば、「座る位置を、プリムの中心から横(Y方向)に+20cm, Z方向に90度回転させた位置に固定したい」という場合は、

llSitTarget(<0, 0.2, 0>, llEuler2Rot(<0, 0, 90>*DEG_TO_RAD));

という具合ですし、

「プリム中心から、1m上、向きは変えずに座らせたい」という場合は、

llSitTarget(<0, 0, 1.0>, ZERO_ROTATION);

と書くことができます。
ZERO_ROTATIONTとは、回転する必要が無い場合に使います。

さて、椅子なんかの場合、座る位置は常に固定させることになると思います。
そうすると、llSitTarget()という関数を発動するのは、とにかくスクリプトが動いた時、で良いだろうと思われます。
ということで、スクリプト全体としては、以下のようなものになります。
「プリム中心から1m上」の例で書きます。

//--------------------------------------------------
default{
  state_entry(){
    llSitTarget(<0, 0, 1.0>, ZERO_ROTATION);
  }
}
//--------------------------------------------------
簡単デスネ!!

[おわりに]
たとえば横長ベンチを作り、その座面を1プリムで仕上げたい場合とか、複数人の座る位置を固定したい!なんて思ったりするかと思います。
llSitTarget()を使った場合、1つのプリムに対して指定できる位置は1箇所だけです。なので、ちょっと不便です。
で、これに対する対応策として最も安易な方法は、透明なダミープリムを設置することです。
この方法は最もお手軽に解決できるのですが、単に座る位置を調整するためだけにプリムを1つ消費することにもなり、もったいない感じもします。
プリムを消費しないでこの問題を解決する方法もあります。ただ、ちょっと複雑な作業が入ります。
そのカラクリと設定方法については、いつか気が向いたらまた説明しようと思います(笑)
  
Posted by mina Cookie at 16:09Comments(1)TrackBack(2)Second Life 学習編

2007年04月25日

【asa】ネタ製作とその実現3 ぱーみっしょん

そんなわけで第3段ですが、ハンカチ王子を目指すのはちょっとだけお休みにして「ぱーみっしょん」(permission)の話をしておこうかと。
まあ、この手の話もあちこちで語られてるとは思いますけどね。
 
SLで出てくる「ぱーみっしょん」という語は、以下の2つのいずれかを指します。
1.Object(モノ)の、複製(COPY)、編集(MOD)、譲渡(TRANS)の設定のこと(Asset Permissionと呼ぶようです)
2.スクリプトに仕込まれたアニメ、キー操作変更等の許可設定のこと(Script Permissionと呼ぶようです)

1.は、既にモノ作りをされている方でそれを誰かに渡したとか誰かから貰ったことがある方にはお馴染みだと思います。
COPY, MOD(MODIFY), TRANS(TRANSFER=Resale/Giveaway)のほか、Allow anyone to moveのチェックボックス設定もこの仲間のようです。
作ったモノの特性、使わせ方によって適宜設定する必要があります。
COPY, MOD, TRANSの3点セットを許可(フルパーミッション)すると、貰った人も色々な人に渡したい放題になります。つまり転売されたりってこともでてきますので、気をつけておきたいところですね。
 
2.は、たとえば何かにSitしたりWearした時に右上に青い確認用のダイアログが出ることがありますが、それのことです。
どんな時にこれが出るのか案外知らない方もいらっしゃるようなので、ちょっと解説なのです。
この確認作業は、オブジェクト等に仕込まれたスクリプトが以下のことを自動実行しようとする際に行われることがあります。
・アニメーションを発動させる(Animation)
・キー操作の動作内容を変更する(Take Controls)
・Payによる支払い(Debit)
・オブジェクトのアタッチ、デタッチ(Attach)
・オブジェクトのリンク操作(Change Links)
・カメラの移動/回転(Track Camera)
・カメラの制御(Control Camera)
でも、この確認ダイアログは常に出るわけじゃありません。出る時と出ない時とがある。何故か?
実はこれらの許可/不許可は、Agent毎に内部情報として持っています。
最初から許可された状態であればわざわざ確認する必要もないので確認しない、というような感じになっているものが多いのです。
毎回聞いてこられてもうっとーしー!ということもありますしね。
 
でもこの確認、意味はちゃんとあります。
それはもしもこの許可設定がなかったら、たとえば悪意のある人が自由に貴方をあんなポーズこんなポーズにしてしまうこともできちゃうということです。
他の操作も同様に、悪意をもってすれば迷惑行為に繋がるようなことなんです。
なので、右上に確認用の青いダイアログが出たとき、それに心辺りがなければ許可しないようにしましょう^^
 
さて、ハンカチ王子ネタにもこの話を絡めておきましょう。
ハンカチ王子になるためには「野球の投球フォームを実現するアニメーション」が必要でした。
すなわち、上で説明した通り、アニメーションを発動させるための許可が必要になります。
このアニメーションは、玉オブジェクトをwearした時に発動する、という仕様にすることにします。
wearした時に発生するイベントはattachイベントです。
attach(key avid){
  
}
wearされたら、まずは「今現在は、許可されてるの?」という確認をします。
許可されているかどうか?という状態値は、integer型の変数でbit imageとして持ちます。
この状態値を、permという変数で持つことにします。
attach(key avid){
  integer perm ;
  
}
次、状態値をとってきます。llGetPermissions()というfunctionを用います。
attach(key avid){
  integer perm = llGetPermissions();
  
}
そして許可があるかどうかの判定。
permという変数は、色々な許可の情報を含んでいます。
たとえば、Debit=2, Take Controls=4, Animation=16, ...で、許可された値のこれらの合算値が入ってます。
ちょっと電算機的な論理演算(ブール代数)ぽい話が入りますが、この合算値から必要な部分をとってくるには、AND演算を用います。
Animationは16ですので、
(perm & 16)
この値が0かどうかで、Animationの許可があるかどうかがわかります。0なら不許可状態。
あと、実際にLSLを書くときは、この部分には2とか4とか16とかは書きません。そのかわり、見た目にわかりやすい定数が用意されています。
たとえばAnimation(16)は、PERMISSION_TRIGGER_ANIMATIONという定数。つまり、
(perm & PERMISSION_TRIGGER_ANIMATION)
こう書きます。というか、こう書くのが流儀です。
さて、こいつが0かどうかを判断することになるので、以下のような分岐分が登場します。
attach(key avid){
  integer perm = llGetPermissions();
  if(!(perm & PERMISSION_TRIGGER_ANIMATION)){
    // if文の値が0。つまりアニメの許可はされていない!
  }
}
上の例は、ちょっと説明が面倒なので詳細説明は省きますが、LSLでは、これで「0の時」という表現ができます。
もっとわかりやすく書くならば、
  if((perm & PERMISSION_TRIGGER_ANIMATION) == 0){
とかです。
このif文は、アニメの許可がされていない時にほにゃららしなさい、という意味ですので、つまり、
アニメの許可がされていない→アニメの許可をするための操作
という手順になります。
Script Permissionの許可は、llRequestPermissions()というfunctionで実現します。
また、「許可されている」時の動作も合わせて書いておきます。
attach(key avid){
  integer perm = llGetPermissions();
  if(!(perm & PERMISSION_TRIGGER_ANIMATION)){
    // if文の値が0。つまりアニメの許可はされていない!
    llRequestPermissions(avid, PERMISSION_TRIGGER_ANIMATION);
  }
  else{
    // if文の値が0以外。アニメの許可はされているのでアニメ発動!
    llStartAnimation("pitching");
  }
}
ここで気付いた方も多いんじゃないかと思いますが、あの、右上に出てくる青い許可設定のダイアログは、
llRequestPermissions(avid, PERMISSION_TRIGGER_ANIMATION); ←こいつ
が出しているんです。
さて、続けます。
あれ?llRequestPermissions()で許可のダイアログ出したはいいけど、その後許可されたり許可されなかったりした場合はどうなるの?
という疑問が湧いてくると思います。いや、湧かなかったら湧かせてくださいw
llRequestPermissions()による許可が行われると、run_time_permissionsというイベントが発生します。
このイベントには、llRequestPermissions()で与えられた許可の状態値が渡されてきます。以下のpermがそれです。
run_time_permissions(integer perm){
  
}
さて、上記と似ていますね。llRequestPermissions()で許可されたら、と、許可されなかったら、の2つのケースがあります。
許可されなかったらアニメは発動できませんので、まあ、無視しておきましょうw
今度は上のものとは別です。
if(perm & PERMISSION_TRIGGER_ANIMATION){
で、この値が「0以外である」という判断ができます。
run_time_permissions(integer perm){
  if(perm & PERMISSION_TRIGGER_ANIMATION){
    // if文の値が0以外。アニメの許可はされているのでアニメ発動!
    llStartAnimation("pitching");
  }
}
さて、全て繋げてみましょう。
LSLとしてはstate内でイベントハンドラが定義されていなければなりません。ていうか、default stateでいいんですけど。
default{
  attach(key avid){
    integer perm = llGetPermissions();
    if(!(perm & PERMISSION_TRIGGER_ANIMATION)){
      // if文の値が0。つまりアニメの許可はされていない!
      llRequestPermissions(avid, PERMISSION_TRIGGER_ANIMATION);
    }
    else{
      // if文の値が0以外。アニメの許可はされているのでアニメ発動!
      llStartAnimation("pitching");
    }
  }
  run_time_permissions(integer perm){
    if(perm & PERMISSION_TRIGGER_ANIMATION){
      // if文の値が0以外。アニメの許可はされているのでアニメ発動!
      llStartAnimation("pitching");
    }
  }
}
こんな感じで「Wearするとアニメーションが発動する」というスクリプトができあがります。
ま、実際はもっと細かなテクニック的なところだとかなんとかでいくつか手を加える必要はあるのですが、いちおうこれはこれで動きます。
 
あともうひとつ、余計なお節介を付け加えておきます。
1: default{
2:   attach(key avid){
3:     integer perm = llGetPermissions();
4:     if(!(perm & PERMISSION_TRIGGER_ANIMATION)){
5:       // if文の値が0。つまりアニメの許可はされていない!
6:       llRequestPermissions(avid, PERMISSION_TRIGGER_ANIMATION);
7:     }
8:     else{
9:       // if文の値が0以外。アニメの許可はされているのでアニメ発動!
10:       llStartAnimation("pitching");
11:     }
12:   }
13:   run_time_permissions(integer perm){
14:     if(perm & PERMISSION_TRIGGER_ANIMATION){
15:       // if文の値が0以外。アニメの許可はされているのでアニメ発動!
16:       llStartAnimation("pitching");
17:     }
18:   }
19: }
9行目10行目と、15行目16行目はまったく同じことをしています。
こんなふうに全く同じ処理をするのに別々のところに書いてあると、ここに手を加える時に両方ともやらないといけなくなります。
これは面倒ですね。
そんな時に、functionの定義です。
functionの存在意義とか使い方はこれに限らず他にもありますが、このように同じ処理を1つにまとめる(=サブル-チン的な使い方)も立派なfunction定義の理由です。
integer Animation()
{
  // if文の値が0以外。アニメの許可はされているのでアニメ発動!
  llStartAnimation("pitching");
  return TRUE;
}
default{
  attach(key avid){
    integer perm = llGetPermissions();
    if(!(perm & PERMISSION_TRIGGER_ANIMATION)){
      // if文の値が0。つまりアニメの許可はされていない!
      llRequestPermissions(avid, PERMISSION_TRIGGER_ANIMATION);
    }
    else{
      Animation();
    }
  }
  run_time_permissions(integer perm){
    if(perm & PERMISSION_TRIGGER_ANIMATION){
      Animation();
    }
  }
}
  
Posted by mina Cookie at 17:20Comments(4)TrackBack(0)Second Life 学習編

2007年04月23日

【asa】ネタ製作とその実現2 代替手段に学ぶ

えー、実は、このblogでは「目指せハンカチ王子」ってことで野球のピッチャーをモチーフにしました。
が、実際には違うものをネタとして仕込んでいる最中だったりします。
ま、一部の方々にはバレバレですが、ここではヒミツってことにしておきますw
 
で、そのネタの骨子は「野球のピッチャー」に似ています。
これ、結構色々試すもどうもうまくいかなかったりしていました。
が、wahiさんからの一言を受けて視点を変えたら、アッサリ解決しました。
どこで悩んでいたかというと、野球の例で言えばボールを飛ばすところです。
wahiさんから言われたひとことは「銃に似てる」でした。
この助言には2つの大きな意味がありました。
1.LSLのWikiにスクリプト例が存在し、流用可能なこと
2.モノがまっすぐに飛んで行くスクリプト例として適していること
実際、LSLを使ってモノを飛ばす方法には何通りか考えられるのですが、今回の件では銃弾を動作させるスクリプトが良い題材になった、ということです。
 
前回、
llStartAnimation("pitching");
llSleep(2.5);
こんなことを書きました。
ここから、LSL Wikiに掲載されている銃弾のスクリプト例を参考にして追加すると以下のような感じ。
 
llStartAnimation("pitching");
llSleep(2.5);
// 自分の身長(idはattachイベントハンドラの引数等で得られるAvatarのid)
vector size = llGetAgentSize(id);
// rezする高さ調整用の係数
vector offset = <0, 0, 0.84>;
// 身長の半分に係数(0.84)を掛け合わせた高さ。
offset.z *= size.z / 2.0;
// 自分の位置と身体の向きの取得。
rotation rot = llGetRot();
// 自分の向き(方角)を基準にした場合のX方向(前方)の方角を、絶対的な方角に変換
vector dir = llRot2Fwd(rot);
// rezする位置を、現在の位置に身長に応じたオフセット分とオブジェクト1基準分ずらした所に設定
vector pos = llGetPos() + offset + (dir * 1.0);
// "ball"という名のobjectを、位置pos, 動作はdir(基準)の7倍の値でX方向に飛ばす。向きは元のオブジェクトと同じ(rot)。
// というような意味。
llRezObject("ball", pos, dir * 7.0, rot, 0);
// ...

おっと。上の例は「飛ばす玉」ではなく「持つ玉」に実装する内容です。attach()イベントあたりで実装することになるでしょう。
ここでは「飛ばす玉」をどこかに出現(rez)させそれを前方に飛ばす、というところを実現しています。
それと、前回もそうですが、今回も、コピペでそのままうごく例を載せていないのはわざとですw
スクリプト例をblogに公開するならコピペで動くものを載せますが、説明の一部なのであえてこのような形にしていますので、あしからず。
 
さて「飛ばす玉」。
SLでは、どこかにふっ飛ばした玉は、なにも処置していないとロストボールになります。
自動でロストボールを返却してくる設定(auto return)を施している土地もありますが、基本放置されます。
放置されてると、その土地の持てるプリム数を喰い尽くすことにもなるので迷惑をかけますし、土地の所有者にreturnさせる手間をかけさせることになります。
場合によっては放置された土地の所有者に通報(Report abuse)されることも考えられなくもありません。
なので、飛ばしモノについては自滅処理の作り込みをしておくのが良いです。
基本的に飛ばすものについては「このオブジェクトは●秒後に自動消滅する」という作り込みをしておくのが吉。
またSLのオブジェクトにはTemp-on-rezという属性があります。
詳細は後述しますが、このてのものにはこの属性を付けておくのが吉です。
 
まず、オブジェクトの自己消滅は、llDie()というFunctionで実現します。
玉がrezされて30秒後にllDie()でオブジェクトを自己消滅させる、というような実装をしておきます。
方法は、llSleep()で待たせるとか、llSetTimerEvent()とtimer()イベントを使う形ですね。
飛んでる間にもなにか動作させたい(変化球とか消える魔球とか)する場合、llSleep()では都合が悪い(飛ばしながらの処理ができない)ので、llSetTimerEvent()/timer()イベントを使うほうが良いかもしれません。
 
次にTemp-on-rez属性。一時Rezとでもいいましょうか。
この属性をプリミティブ、オブジェクトに与えておくと、一定期間後に勝手に消えます(何秒後だったかな?)。
更にこの属性が付いている場合には、Rezされた土地のプリミティブを消費しません(これが大きなメリットです)。
属性は、EditメニューからObjectタブのTemporaryというところにチェックすればOKですが、これを定常的に付けてると、地面にdropしてEditしている時、油断すると勝手に消えてしまいます(笑)。
なので、いじる時はまずTemporaryのチェックを外すこと、保存してインベントリに戻す時にはチェックすること、が必要です。
また、この属性は、LSLからも与えることができるので、動的に行う方法もあります。
ただこれを実施するためのFunction(llSetPrimitiveParams())は、ちょっと処理が重く、やや悩みの種でもあります。
llSetPrimitiveParams([PRIM_TEMP_ON_REZ, TRUE]);
 
えーと、またもや疲れてしまったので今日はここまで(笑)
実に中途半端なんだけど(爆)
  
Posted by mina Cookie at 18:39Comments(3)TrackBack(0)Second Life 学習編

2007年04月16日

【asa】ネタ製作とその実現1 目指せハンカチ王子

いきなりですが、LSLでネタを考える時の思考プロセスについてです。
気が向いたら実際に実装したものを、そのプロセスを含めて紹介しようと思っています。
そんな理由でタイトルに「1」を与えてみましたw
 
さて、たとえばということで「野球の投手がボールを投げるシーン」なんてものを考えてみます。
懐かしいあのアニメのパロをしてみるなんてのも面白いかもしれないですね。
で、これってどうやって作るの?っちゅう話です。
 
皆さん料理をしたことがあるでしょうか?
料理をするにはまず材料が必要ですよね。
こういうのを考えるプロセスもまったく同様で、まず何が必要か?を考えてみるわけです。
1.野球のボール
2.投げる動作を行うアニメーション
3.一連の動作を制御をする流れ(スクリプト)
4.ユニフォーム(衣装)とかグロ-ブとかスパイクとか
まあ、やりたいことの本質は1~3で、4は味付けのための調味料て感じですかね。
 
ここで挙げたものを順番に手掛けていくことになります。
各々を実現するために必要なものを更に細分化して考えます。
 
1.野球のボール
球型のプリムと、縫目のテクスチャがあればいけますね。
テクスチャの作り方と貼り方にコツがあると思いますけど。
 
2.アニメーション
単純にPoserなりAvimatorなりで作るだけですね。
 
3.制御する流れ
ここが、プログラミングの経験の無い方だとシンドい所だと思います。
LSLのことも全く知らないと厳しいです。これは構文とか書き方とかそういうことではなく、概念的なこととか、どうしたらどうなる、というような因果関係を多少なりともわかってないと厳しいという意味です。
んで、そのへんは秀逸な解説が各所にあるので探して参照して戴くとして・・・
基本的な「動き」をベースに追ってみます。
a.ピッチャーがボールを持つ
b.投球動作を行う
c.ボールを投げる
となるでしょうか。
これをまた、それぞれ細分化して考えてみましょう。
 
3.a.ピッチャーがボールを持つ
右利きのピッチャーなら、右手にAttachするだけですね。
ここは、単発ネタ的には、自分でWearしてもらうとかで良いでしょう。
もっと凝ったことをするならば自動でAttachするなんてことを考えてみても良いでしょう。
 
3.b.投球動作を行う
アニメーションの発動です。
流れは時系列で考えるものなので、どういうタイミングでこれを発動するか?を考える必要があります。
たとえば、Wearした瞬間に発動するとか、ボールにTouchされたら発動するとか、命令(chat)で"Throw"という言葉を聞いたら発動するとか。
ここは、LSLでできること、できないこと(無理なこと)があります。
たとえば「アバターを凝視して念じたら発動する」とか、絶対無理なわけですしね。
 
3.c.ボールを投げる
ボールが手から離れ、前方に飛んで行く感じですね。
これはSL的に2つの事象が含まれていることがわかるでしょうか。
●ボールが手から離れる=Detachする(Wearしてるものを外す)
◯前方に飛んで行く=ボールをRezし、移動(llSetPos()など)
LSLでこういったことを実現するためには、このように事象を細分化して考えることが重要です。
というか、作っていれば必然的にぶちあたる話だったりします。
また、時には都合上、代替案を考えなければならないこともあります。
たとえば、●部分は、ボールをDetachしなくても「見えなくなれば良い」という観点から、DetachするかわりにAlphaを使って透明化するなんてことも考えられます。
 
えー疲れたので今回はここまでですw  
続きを読む
Posted by mina Cookie at 11:07Comments(4)TrackBack(0)Second Life 学習編

2007年04月10日

【asa】意外と知られていない!? プリム透明化

たとえばガラス窓なんかを作る際、プリム・オブジェクトの「編集」メニューでTransparent設定をいじりします。
ここ、値の設定が0~90までできるようになってます。
しかし最大値の90を設定しても完全に透明化しません。
ガラスのようなものを作るなら存在感を残すため90とかで良いと思います。
しかし何らかの事情で完全に見えなくしたい場合に、これでは都合が悪いですね。
 
スクリプトを使わずにこれを実現する場合、アルファチャンネルを使えるソフトでテクスチャを作り、これを実現することもできます。
が、スクリプトを使う場合は、たった↓これだけでできます。
---------------------- プリム1つの場合
default {
state_entry(){
llSetAlpha(0.0,ALL_SIDES);
}
}
----------------------
---------------------- linkされた複数プリムをまとめて全部
default {
state_entry(){
llSetLinkAlpha(LINK_SET,0.0,ALL_SIDES);
}
}
----------------------

ついでに、こんなんすると、touchする度に表示/非表示が入れ替わるプリムになります。
touchした時(touch_startイベント発生)、
もしも現在それが表示されてたら(llGetAlpha()が 0.0より大きければ)、非表示(llSetAlpha(0.0,ALL_SIDES))。
そうでなければ(else)、表示(llSetAlpha(1.0,ALL_SIDES))。
という意味になります。
----------------------
default {
touch_start(integer detected){
if(llGetAlpha(ALL_SIDES) > 0.0){
llSetAlpha(0.0,ALL_SIDES);
}
else{
llSetAlpha(1.0,ALL_SIDES);
}
}
}
----------------------  
Posted by mina Cookie at 10:26Comments(5)TrackBack(0)Second Life 学習編

2007年04月07日

【mina】所持金0

昨日の出来事。


ろっきオーナーがなぜか所持金0表示になっていたらしい。
これ、私は未だに未経験だけどSLが不安定なときってよく聞きますね。

この状態で買い物してもL$買いなさい的なメッセージがでるらしいの
ですが、昨日たまたまCocololo Islandでどなたかが買い物をして
くれた途端、金額の表示が正常な状態に戻ったらしいのですw

それで、どうやら所持金0になってるときは誰かに1Lでも入れてもらえば
なおるみたいだね、って話しになりました。


わたしもろっきもSL歴は一年以上でそこそこ長い方だし、特に彼は
私よりもずっとずっとSLの知識あるのですがそれでも知らなかった。
意外と知られてない事実なのかな?と思ったのでちょっとblogネタに
してみました。


おまけ
wikiに投稿してもらおうと思ったらパス忘れた、というボケをしてくれた
ろっきオーナーw
  
Posted by mina Cookie at 10:04Comments(9)TrackBack(0)Second Life 学習編