ソラマメブログ

2007年04月23日

prim間通信(スクリプト初級第十五回)

しばしばカツラなどに組み込まれている色変更のスクリプトを作ってみましょう。
単純に色を変えるだけのスクリプトであれば、ここまで読んできた方には簡単に出来ると思います。
ですが、今回は特にprim間の通信を行う方法使って、拡張性の高いスクリプトを考えてみたいと思います。

prim間通信とは、llMessageLinked()という関数を使い、リンクされているprim同士でメッセージをやり取りする方法です。
これを使うと、一つのprimを操作したときに、別のprimを操作することができるようになります。
色を変える

primの色を変えるのは簡単です。
llSetColor()関数を使います。

  llSetColor(vector color, integer face)

vector color
今までも何度か出てきた色を指定するためのvector型引数です。
<R, G, B>の形式で、0.0~1.0の数字で赤・緑・青の色の強さを混ぜ合わせるものでしたね。
詳しくは「照明を作ろう」の回を参照して下さい。

integer face
primのどの面の色を変えるかの指定です。
面の番号については「看板を作ろう」の回で触れました。

例えば立方体primの上の面だけ赤色にしたいという場合は、
  llSetColor(<1.0, 0.0, 0.0>, 0);
このようになります。

リンクされている他のprimの色を変えるための関数もあります。
llSetLinkColor()関数です。

  llSetLinkColor(integer linknumber, vector color, integer face)

integer linknumber
色を変えたいprimのリンクナンバーを指定します。
リンクナンバーについては「ベンダーを作ろう」の回で説明しています。
この引数以外はllSetColor()関数と同じです。

例えばルートprimの全面を青に変えたいときは、
  llSetLinkColor(1, <0.0, 0.0, 1.0>, ALL_SIDES);
こんな感じですね。

すでに慣れてる皆さんであれば、これだけわかれば色を変えるスクリプトが出来るかと思います。

簡易版カラーチェンジャー

試しにllSetLinkColor()を使ったカラーチェンジャーを作ってみましょう。

list linknumbers=[ // 色を変えるリンクナンバーの一覧
  1,2,3
];

list colors = [ // 色名のリスト
  "red", "green","blue",
  "yellow", "cyan","magenta",
  "pink","orange", "purple",
  "grey","white","black"
];

list rgb = [ // 色名に対応したRGB値のリスト
  <1.0,0.0,0.0>, <0.0,1.0,0.0>, <0.0,0.0,1.0>,
  <1.0,1.0,0.0>, <0.0,1.0,1.0>, <1.0,0.0,1.0>,
  <0.965,0.668,0.648>, <0.936,0.504,0.0586>, <0.652,0.340,0.656>,
  <0.5,0.5,0.5>, <1.0,1.0,1.0>, ZERO_VECTOR
];

integer handle; // listenハンドル
integer channel=7; // listenチャンネル

default{
  touch_start(integer detected){
    if (llDetectedKey(0) == llGetOwner()){ // オーナーがタッチした場合のみ作動
      handle=llListen(channel, "", llGetOwner(), "");
      llDialog(llGetOwner(),"Select Color",colors,channel);
      llSetTimerEvent(60.0);
    }
  }
  
  timer(){ // ダイアログのタイムアウト
    llSetTimerEvent(0.0);
    llWhisper(0,"Time out! Touch again for change color.");
    llListenRemove(handle);
  }

  listen(integer ch, string name, key id, string msg){
    llSetTimerEvent(0.0);
    llListenRemove(handle);
    integer i = llListFindList(colors, [msg]); // 色名のインデックスを取得
    if (i != -1){ // インデックスが有効だった場合(リストにある色名だった場合)
      vector c = llList2Vector(rgb,i); // インデックスに対応するRGB値を取得
      integer j;
      for (j = 0; j < llGetListLength(linknumbers);j++){ // リンクナンバーの一覧を一つずつ処理
        llSetLinkColor(llList2Integer(linknumbers,j), c, ALL_SIDES); // 色変更
      }
    }
  }
}


照明のスクリプトに似ています。
違うのはリンクに対応したところと、照明効果ではなく色を変更しているという点だけですね。

このスクリプトを試す際には3つ以上のprimがリンクされているオブジェクトを使って下さい。
リンクナンバー1~3のprimのみ色が変わります。

拡張性の問題

簡易版カラーチェンジャーをカツラに組み込むことを考えてみます。
どのprimの色を変更するかはリスト型変数linknumbersで管理していますので、linknumbersの定義さえ変えればOKです。

・・・とは言っても・・・。

カツラ・オブジェクトは、物にもよりますが、百個以上のprimがリンクされているものも珍しくありません。
200、300個というものも中にはあります。
髪の毛のprimだけは色を変え、髪飾りやリボンなどはそのままにしておきたいという場合もあるでしょう。
となると、100や200のprimのうち、色を変えるべきprimのリンクナンバーを調べ、それらを全てlinknumbersに書いていかなくてはなりません。
さらに、前髪をちょっと追加したり、余分な髪の毛primを一つ取り除いたりすると、それだけでリンクナンバーが変わってきます。
そのたびにリンクナンバーの調べなおしとlinknumbersの修正を行うとなると、これはもう面倒どころか戦意喪失しかねない手間になります。

数個のprimで構成されるものであれば、簡易版カラーチェンジャーのスクリプトでも良いのですが、カツラに組み込むのは少々非現実的でしょう。
もっと良い方法は無いものでしょうか?

そこでprim間通信を利用する方法を考えます。

prim間通信の仕組み

リンクされているprim間の通信には、llMessageLinked()関数とlink_messageイベントを使います。
llMessageLinked()関数が送信元、link_messageイベントが受信側です。
今回の場合は、ルートプリムから「色を変えろ」というメッセージを送信し、子プリムでそれを受信して色を変えるような仕組みを考えます。

prim間通信(スクリプト初級第十五回)

子プリムには受信用のスクリプトを入れておかなければいけません。
今まではオブジェクトの中には一つのスクリプトだけ入れていましたが、実はスクリプトはオブジェクトの中に複数入れておくことができます。

buildツールでオブジェクトの編集を行う際、「Edit linked parts」(日本語版では「リンクされたパーツを編集」とかそんなん)にチェックを入れると、子プリムだけを選択することができます。
その状態でコンテンツを開き、スクリプトを作成すると、子プリム内にスクリプトを作ることが可能です。

子プリムのスクリプトにはメッセージの受信イベントと、受信したメッセージに応じて色を変える仕組みを書いておきます。
あとはこの子プリムをコピーして使うことで、色変更可能なパーツをどんどん増やすことができます。
色を変えたくないパーツに関しては、スクリプトの入っていないprimを使えば良いのです。

llMessageLinked()関数とlink_messageイベントの詳細を見てみましょう。

  llMessageLinked(integer linknum, integer num, string str, key id)

リンクされたprimにメッセージを送る関数です。

integer linknum
送信先のprimのリンクナンバーを指定します。
全てのprimに送信する場合はLINK_SETという値を使います。
全ての子プリムに送信する場合はLINK_ALL_CHILDREN、自分以外のprimに送る場合はLINK_ALL_OTHERS、ルートプリムに送る場合はLINK_ROOT、などの値が使えます。
今回はプリムの数が変わっても対応が効くよう、LINK_SET(全プリムに送信)を使います。

integer num
送信する数値です。
送りたい数値を好きなように指定できます。
今回は使いませんので、固定値0を指定します。

string str
送信する文字列です。
送りたい文字列を好きなように指定できます。
今回はこの引数に"<1.0,0.0,0.0>"などの色データを指定して送信します。

key id
送信するkey型データです。
送りたいkey型データを好きなように指定できます。
今回は使いませんので、NULL_KEYを指定します。

  link_message(integer sender_num, integer num, string str, key id)

llMessageLinked()関数で送信されたメッセージを受信したときに発生するイベントです。

integer sender_num
送信してきたプリムのリンクナンバーが入ってきます。
今回はルートプリムからメッセージを送信しますので、ここには1の値が入ってきます。

integer num, string str, key id
メッセージの内容です。
それぞれllMessageLinked()関数で指定された値が入ってきます。
今回は文字列型のメッセージのみ使いますので、着目すべきはstrの値です。

カラーチェンジャースクリプト

簡単なほうから行きます。
メッセージを受信して色を変える、子プリムのスクリプトです。

default {
  link_message(integer sender_num, integer num, string str, key id){
    vector c = (vector)str;
    llSetColor(c, ALL_SIDES);
  }
}


これだけです。
受信した文字列型メッセージ(str)をvector型に変換し、llSetColor()関数を使って色を変えます。
「何色に変えるか?」は送信側(ルートプリムのスクリプト)で指定しますので、子プリムのほうは何も考えずに受信した色に変えるだけです。

次にルートプリムのスクリプトですが、先ほどの簡易版カラーチェンジャーとほとんど同じです。
llSetLinkColor()関数で色を変えていた部分を、llMessageLinked()関数に置き換えるだけですね。

list colors = [
  "red", "green","blue",
  "yellow", "cyan","magenta",
  "pink","orange", "purple",
  "grey","white","black"
];

list rgb = [
  <1.0,0.0,0.0>, <0.0,1.0,0.0>, <0.0,0.0,1.0>,
  <1.0,1.0,0.0>, <0.0,1.0,1.0>, <1.0,0.0,1.0>,
  <0.965,0.668,0.648>, <0.936,0.504,0.0586>, <0.652,0.340,0.656>,
  <0.5,0.5,0.5>, <1.0,1.0,1.0>, ZERO_VECTOR
];

integer handle;
integer channel=7;

default{
  touch_start(integer detected){
    if (llDetectedKey(0) == llGetOwner()){
      handle=llListen(channel, "", llGetOwner(), "");
      llDialog(llGetOwner(),"Select Color",colors,channel);
      llSetTimerEvent(60.0);
    }
  }
  
  timer(){
    llSetTimerEvent(0.0);
    llWhisper(0,"Time out! Touch again for change color.");
    llListenRemove(handle);
  }

  listen(integer ch, string name, key id, string msg){
    llSetTimerEvent(0.0);
    llListenRemove(handle);
    integer i = llListFindList(colors, [msg]);
    if (i != -1){
      vector c = llList2Vector(rgb,i);
      llSetColor(c, ALL_SIDES);
      llMessageLinked(LINK_SET, 0, (string)c, NULL_KEY);

    }
  }
}


簡易版カラーチェンジャーでは、色を変えたいパーツ全てに対していちいちllSetLinkColor()関数を実行していましたが、今回はllMessageLinked()で全プリムに対して色データを送信するだけです。
これだけで、受信用スクリプトの入っているプリムは全て色が変わります。
リンクナンバーがどのように変わっても問題はありません。

もう一工夫

さて。
以上でカラーチェンジャーとしては機能しますが、もう一工夫しましょう。
今のままだと、クリックしたときにダイアログが開きます。
機能的には問題はありませんが、カツラなどにこのスクリプトを入れた場合、アバターの向きを変えようと思ってうっかりカツラをクリックすると、そのたびにダイアログが表示されてしまいます。
実際にそのようなカツラを身に着けてみるとわかりますが、なかなかにウットオシイものです。

そこで、一定時間マウスボタンを押しっぱなしにした場合にのみ、ダイアログが表示されるような仕組みに変えてみたいと思います。
以下のようになります。

list colors = [
  "red", "green","blue",
  "yellow", "cyan","magenta",
  "pink","orange", "purple",
  "grey","white","black"
];

list rgb = [
  <1.0,0.0,0.0>, <0.0,1.0,0.0>, <0.0,0.0,1.0>,
  <1.0,1.0,0.0>, <0.0,1.0,1.0>, <1.0,0.0,1.0>,
  <0.965,0.668,0.648>, <0.936,0.504,0.0586>, <0.652,0.340,0.656>,
  <0.5,0.5,0.5>, <1.0,1.0,1.0>, ZERO_VECTOR
];

integer handle;
integer channel=7;

integer counter=0;

default{
  touch_start(integer detected){
    if (llDetectedKey(0) == llGetOwner()){
      counter=0;
    }
  }

  touch(integer detected){
    if (llDetectedKey(0) == llGetOwner()){
      if (counter < 50){
        counter ++;
      }else if (counter == 50){
        counter ++;
        handle=llListen(channel, "", llGetOwner(), "");
        llDialog(llGetOwner(),"Select Color",colors,channel);
        llSetTimerEvent(60.0);
      }
    }
  }

  
  timer(){
    llSetTimerEvent(0.0);
    llWhisper(0,"Time out! Touch again for change color.");
    llListenRemove(handle);
  }

  listen(integer ch, string name, key id, string msg){
    llSetTimerEvent(0.0);
    llListenRemove(handle);
    integer i = llListFindList(colors, [msg]);
    if (i != -1){
      vector c = llList2Vector(rgb,i);
      llSetColor(c, ALL_SIDES);
      llMessageLinked(LINK_SET, 0, (string)c, NULL_KEY);
    }
  }
}


touchイベントは「マウスボタンを押している間、繰り返し発生する」イベントです。
前にちょろっと説明しました。

このスクリプトではcounterという変数を用意し、タッチを開始したとき(touch_startイベント)にcounterを0に初期化し、タッチし続けている間(touchイベント)counterを増加させます。
そしてcounterが50に達した時点で、ダイアログを表示しています。

私の環境では1秒間に10回ほどtouchイベントが発生していましたので、50だと約5秒です。
5秒間マウスボタンを押しっぱなしにしないとダイアログが出ませんので、うっかりカツラをクリックしたとしても、ダイアログに煩わされることはなくなるでしょう。

念のために補足しますが、touchイベントにはバグがあるという話があります。
マウスボタンを離しているにも関わらず、touchイベントが発生し続けるという現象があるようですので、使用の際には注意して下さい。

今回のポイント

・色の変更:
  llSetColor(vector color, integer face)

・リンクされているprimの色の変更:
  llSetLinkColor(integer linknumber, vector color, integer face)

・prim間メッセージの送信:
  llMessageLinked(integer linknum, integer num, string str, key id)

・prim間メッセージの受信:
  link_message(integer sender_num, integer num, string str, key id)

リンクされているprim同士であればllMessageLinked()関数が使えます。
リンクされていないprim同士の場合はlistenを使うことになります。
もちろん、listenはリンクされているprim間でも有効ですが、以前書いたとおりlistenは負荷の原因となる可能性が高く、多用はお勧めできません。
llMessageLinked()関数が使える場合は極力llMessageLinked()関数を使うべきでしょう。

llMessageLinked()関数はlisten同様、非常に汎用性が高いので、ぜひ覚えて活用してみて下さい。


同じカテゴリー(初級スクリプト)の記事画像
衝突判定(スクリプト初級第二十三回)
カメラ制御(スクリプト初級第二十二回)
センサーを使おう(スクリプト初級第二十回)
HUDを作ろう(スクリプト初級第十六回)
アニメさせよう(スクリプト初級第十三回)
お金を扱う(初級スクリプト第十一回)
同じカテゴリー(初級スクリプト)の記事
 衝突判定(スクリプト初級第二十三回) (2007-05-08 12:15)
 カメラ制御(スクリプト初級第二十二回) (2007-05-07 14:36)
 デモ商品を作ろう(スクリプト初級第二十一回) (2007-05-02 12:15)
 センサーを使おう(スクリプト初級第二十回) (2007-05-01 12:15)
 ステートのこと(スクリプト初級第十九回) (2007-04-27 12:15)
 rez!(スクリプト初級第十八回) (2007-04-26 12:15)
この記事へのトラックバック
 
HUDの機能としては、19ある表情を単独でアニメーションさせる「表情モード」と表情を自由に組み合わせて
表情チェンジ&口パクHUDスクリプト構文を練ってござる【Rinsui SL+ Making Blog】at 2007年08月23日 20:18
この記事へのコメント
いつもいつも大変おもしろいLSLを紹介していただきありがとうございます!!
しかも図付きで!
素晴らしい!!
Posted by 5号 at 2007年04月23日 12:21
>5号さん

どうもです。

図がぼけてしまうのはなんとかならないものですかね(^^;
スタイルシートいじらなくちゃ駄目かな・・・。
Posted by Miz at 2007年04月23日 14:16
↑ああ、OKです。
画像じゃなくて私がボケてましたw
Posted by Miz at 2007年04月23日 14:20
一番知りたい関数でしたっ
頑張って色々応用してみます〜
Posted by Nitaro at 2007年04月23日 16:47
>Nitaroさん

llMessageLinked()関数はわかると便利ですが、わかるまでが意味不明だったりしますね~(^^;

いろいろ例を出すのがわかりやすいと思いますので、次回も何かllMessageLinked()関数を使ったスクリプトを紹介してみたいと思います。
Posted by Miz at 2007年04月23日 17:08
いつも楽しく読ませていただいています。

ダンスパッドでがんばってやっとスキンと服を買って少しまともな姿になったばかりの初心者です。 今、スクリプトの勉強中です。

簡単なHUDを作って、HUDから自分にアタッチしているオブジェクトの色を変更するスクリプトを作成しようとしています。普通なら listen を使うのでしょうがラグも気になるので、何か他に良い方法があるのでしょうか?
Posted by サトル at 2007年05月30日 12:37
追伸:
HUD(リモコン)と自分にアタッチしているオブジェクトはリンクさせていません。 たぶんHUDと自分にアタッチしているオブジェクトはリンクできませんもんね~(?)
Posted by サトル at 2007年05月30日 12:48
>サトルさん

仰るとおり、HUDとアタッチメントはリンクできません。
リンクされていない場合はlistenを使うのが常套手段になります。
やろうと思えばEmailやスクリプトのリモートロードを使ってHUD/アタッチメント間通信を実現できますが、どちらもlistenほど早くないので、実装方法としては現実的ではありません。

listenは確かにラグの原因になりますが、一つのlistenが原因でSIMが落ちるとか、そこまで派手なことにはなりませんので、listenを使って問題ないと思います。
もっとも、使わないときはlistenをOFFにする気遣いはしたほうが良いですけど(^^;
Posted by Miz at 2007年05月30日 13:14
Miz さん、説明ありがとうございました。
ラグで土地に迷惑かけるかと思い Listen を使うのをためらっていましたが、
Miz さんの説明で安心しました。
実は僕は楽器が大好きで、前から欲しかったMizさんのギターを買いに行きますねー (^^)/
Pringa Yoshikawa (SL name)
Posted by サトル at 2007年05月30日 15:22
先日はありがとうございました。ドアはあれからも順調に動いています。
新たにプリム間通信を使用したオブジェクトを作成しようとしておりますが、うまく動かず悩んでいます。
子プリムのリンクナンバーが変わっても大丈夫なように子プリムにリンクナンバーとプリム名を親プリムに送ってそれを変数に保存しようとしているのですが、動作しません。
どこが間違っているのかわからないので、アドバイスいただけますでしょうか?
テストで以下のようなスクリプトを組みました。
 ○ 子プリムから子プリム名を親プリムに送る
 ○ 親プリムは子プリムから送られた情報を発言する
子プリムのスクリプトを開き、「リセット」ボタンを押したときは発言しますが、オブジェクトをインベントリから rez したときは無言なのです。
これを、rez した時に発言してほしいのが私のしたいことです。

親プリムのスクリプト
default{
link_message( integer p_no, integer data, string p_name, key id){
llSay( 0, "Link no = " + (string)p_no );
llSay( 0, "Link nanem = " + p_name );
}
}

子プリムのスクリプト
default{
state_entry(){
llMessageLinked( LINK_ROOT, 0, "Child_1", NULL_KEY);
}
}
Posted by つぶあん at 2007年09月16日 18:10
>つぶあんさん

state_entryイベントは「スクリプト開始時」のイベントですので、「rezしたとき」とは異なります。
「rezしたとき」にリンクメッセージを送信するには、on_rezイベントを使って見て下さい。

ということで、子プリムのコードを、state_entryイベントではなくon_rezイベントに変えれば、お望みの動作になるはずです。
Posted by Miz at 2007年09月16日 21:52
突然スイマセン;カラーチェンジについて調べていましたらこちらにたどり着きました^^ その他のスクリプトについても丁寧な解説!! 素晴らしいと思いました!! さかのぼって勉強させていただこうと思います^^

List collorの色を増やしたい場合は、 増やしたい分rgbを追加する?と言った単純なことで可能でしょうか? 少し試しに違うカラーを追加して設定してみた所、エラー表記が;;; 

全く低レベルな質問で申し訳ございません^^;
Posted by キット at 2008年02月14日 13:33
>キットさん

ダイアログで一度に表示できるボタンは12個までなので、リストを増やすと制限に引っかかるかと思います。
ですので12色以上に対応する際にはさらに工夫がいります。

よくある手法では、ダイアログに「next」「back」のボタンを用意し、ページ切り替えをするように選択できる色を変える方法ですね。

ダイアログのページ切り替えをするサンプルコードや解説は当blogにはありませんが、ぜひ挑戦してみて下さいまし。
Posted by Miz at 2008年02月15日 15:49
ご回答ありがとうございます^^

そうでしたか;;お恥ずかしいです。良く見てみれば確かに12個でしたw ダイアログ切り替え、、是非挑戦してみますっ!
Posted by キット at 2008年02月15日 18:20
キットさん、こんにちは。

割り込み、失礼します。

私のブログにBack、Nextでダイアログの表示を切り替えていくサンプルがありますので、参考にしてみてください。

http://sasapy.slmame.com/e121995.html
Posted by ささぴ at 2008年02月18日 09:44
Mark Zuckerberg maybe interested in Viuly crypto currency. Viuly сrypto currency can be integrated into Facebook.
Posted by FinanceNews at 2018年09月04日 10:05
 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。