FLATzブログ

[LSL]の記事一覧

Second Life: LSLのスクリプト間のメッセージング

2008年04月18日(金)16:00|amakata|FLATzブログ, LSL, 技術情報このエントリをdel.icio.usに追加このエントリをはてなブックマークに追加

実用的なメッセージのやりとり


前回は、LSLで16KBを超えるプログラミングを行う場合の、基本的になメッセージのやり取りついてお話しました。
今回は、前回お話した通信方法を使って実用的なメッセージのやりとりを行うプログラムの方法についてお話したいと思います。


実用的なというのは、ここでは


  • セカンドライフ内のオブジェクト(複数のプリムを組み合わせたモノ。建物や車などはみんなオブジェクト)が複数のプリム(セカンドライフのな型のボックスとかシリンダーとか)で構成されている
  • ひとつのプリムに複数のスクリプトがある
  • 後で、プリムやスクリプトは拡張されていく可能性がある。

というような状況に対応できるということです。


実用的なメッセージのやりとりのための方針


このようなスクリプトをLSLで美しくつくることは困難ですが、だからといって方針を立てずに作ることは危険です。
そこで、私は、上記の状況を想定したうえでプログラムをするための方針をまとめてみました。


  • 方針1 メインの処理は中央集権(1つの開始ポイントから始まる)
    LSLでは、複数のスクリプトが同時に開始されます。(少なくとも、どの順番で初期化されるか明示的ではない) したがって、複数の開始ポイントを持つ実装は簡単にできます。複数の開始ポイントをむやみに利用するとどの順番で初期化されるかがあいまいになります。そういった状況の上でメッセージが追加されていくと、メッセージのやり取りの前提を設計することも難しくなります。そこで、ある機能の塊を実現するプリム群の初期化は、一つのスクリプトにメイン処理として記述するのがよいでしょう。メッセージをやり取りしない独立したプリム群が複数ある場合には、群ごとにメインの処理を決めてもよいと思います。
    lslmodel1.png
  • 方針2 メッセージの通信は階層的
    ここで通信は階層的といっているのは、メインの処理を行うスクリプトを親として、ツリー上にスクリプトを配置して、ツリーの枝に沿って通信を行うということです。この方法だと、メッセージの流れをメインの処理でハンドリングしやすくなります。もちろん、子のスクリプトが孫から来たメッセージをすべて親のスクリプトに返す必要はありません。必要に応じて、子は別の孫にメッセージを送ることもあるでしょう。通信を階層的にしておく理由の一つは、デバッグ時にメッセージの流れを捕まえやすくするためです。もしも、階層的ではなく、全スクリプトがお互いに通信を行った場合、通信の組み合わせは無数にできることになります。そのような状態を考慮してデバッグすることは不可能に近いでしょう。lslmodel2.png
  • 方針3 一つのスクリプトは1機能
    スクリプトを1機能にする理由は、モジュール化という観点もありますが、LSLではオブジェクト指向的な実装は難しく、あまりそういった指標で問題をとらえることは難しいでしょう。どちらかというとメモリ対策として、この方針は存在しています。たとえば、自分のプリム内にメッセージを送信した場合、そのプリム内のスクリプトは、メッセージを受け取ることになります。これは、そのメッセージを必要としていないスクリプトのメッセージを一度はメモリに展開するということです。したがって、ひとつのスクリプトには可能な限り最少単位の機能を書いておきメッセージを受け取るスペースを確保すべきでしょう。
  • 方針4 一つのメッセージは短く
    これも方針3と同じ理由です。特にLINK_THISを宛先とする場合よりもLINK_SETを宛先とするメッセージについて気を使う必要があります。
    lslmodel3.png
  • 方針5 プリムの制御が必要ないスクリプトは、親と同じプリムにまとめる。
    親と同じプリムにまとめることで、LINK_THISでメッセージを送ることができます。そうすることでメッセージを受信処理するスクリプトを減らすことができるためです。

方針の例外


また、LSLの制約から、上記のルールを厳密に守ることは難しいため、次のような例外的な対応は行ってもよいと思います。


  • 各プリムの制御(テクスチャの張り替え、クリックのハンドリング等)のための初期化は分散化するこれは、テクスチャの張り替えやクリックのハンドリングのイベントは、そのプリムに入っているスクリプトからしか実施できないからです。
  • 大きなメモリを必要とするデータを持つスクリプトは、階層的でなく直接的に他のスクリプトと通信するこれは、階層的な通信をすることで、無駄にデータコピーが発生するからです。また階層をまたぐことによって、メモリも多く消費してしまいます。
  • リアルタイム性のあるイベントが必要な場合には、階層を飛ばした通信をするメッセージを送る場合、メッセージはただちに送れない場合があり得ます。複数の階層を通ることによって少なからず処理に遅延が発生します。また、階層的に通信をする場合、メッセージの伝わる順番等に問題が発生する場合もあるかと思います。そういったことを回避する意味で、例外的に階層を飛ばした通信をすることを許す場合があります。ただ、方針を骨抜きにしないためにも、通常そういった機能は全体で共通化するべきものに限るべきです。

さて、この方針についてご覧になった方はどう思われたでしょうか。この方針は、現時点までのノウハウから考えているものですが、私としては満足していません。今後、よりよい方針を作ることができたらとLSLに取り組んでいます。LSLをされている方は、よりより指針を考えてみてはどうでしょうか。

続きを読む


LSLのプログラミングスタイル

2008年04月08日(火)17:52|amakata|FLATzブログ, LSL, 技術情報このエントリをdel.icio.usに追加このエントリをはてなブックマークに追加

LSLのプログラミングスタイル


前回は、LSLの特徴をざっくりと説明しました。
ちょっと見ればわかりますが、LSLの特徴について網羅的には説明していません。
LSLの網羅的な情報については、いろいろな場所で扱われているので、このシリーズでは、扱わないつもりです。
このシリーズでは、16KBのメモリ制限のあるなかで、どのようにLSLのプログラムをするべきか?について話したいと思います。


LSLでは、1スクリプト当たりに16KBのメモリ空間を割り当てます。
16KBを超える場合には、スクリプトファイルを分割する必要があります。
複数のスクリプトファイルをどのようにまとめるかというと、プリム(Second Lifeの中で作れる物体)のコンテンツ欄に複数のスクリプトを持たせることで実現できます。


ただ、あるスクリプトファイルから他のスクリプトファイルの呼び出すを行うためには、他の言語では考えられないくらいのオーバーヘッドがあります。例として、同じオブジェクトに含まれている二つのスクリプト senderとreceiverを書いて見ましょう。


※日本語のコメントは補足情報として書いていますが、いまのところSecond Life上では日本語を記述することはできないです。


このスクリプトを設定したプリムをクリックすると、「Second Life is Wonder World」というメッセージが、チャット欄にでます。


  • sender
    integer SCRIPT_SENDER = 1;   // 送信側スクリプトのID
    integer SCRIPT_RECEIVER = 2; // 受信側スクリプトのID
    string CR = "n"; // 改行
    // デフォルトのステータスについての定義
    default
    {
    // プリムにタッチされた場合のイベント
    touch_start(integer total_number)
    {
    // メッセージの送信
    // 第一引数 メッセージの送信先。
    // LINK_THIS : 自分のプリムのスクリプトにのみメッセージを送信する
    // 第二引数 メッセージで送りたいinteger型の値 送信側スクリプトID。
    // 第三引数 メッセージで送りたいstring型の値
    // 受信側スクリプトIDとメッセージを改行区切りでセット。
    // 第四引数 メッセージで送りたいkey型の値 使わないので、NULL_KEYをセット。
    llMessagedLinked(LINK_THIS,
    SCRIPT_SENDER,
    (string) SCRIPT_RECEIVER + CR
    + "Second Life is Wonder World",
    NULL_KEY);
    }
    }

  • receiver
    integer SCRIPT_SENDER = 1;   // 送信側スクリプトのID
    integer SCRIPT_RECEIVER = 2; // 受信側スクリプトのID
    string CR = "n"; // 改行

    // デフォルトのステータスについての定義
    default
    {

    // llMessagedLinkedのメッセージを受信した場合のイベント
    // 第一引数 送信側プリムの番号
    // 第二引数 メッセージで送られてきたinteger型の値
    // 第三引数 メッセージで送られてきたstring型の値
    // 第四引数 メッセージで送られてきたkey型の値

    link_message(integer senderNum, integer senderScriptNum, string message, key id)
    {

    // messageを改行を区切りとして分割して、linesにリスト型で格納
    list lines = llParseString2List(message, [CR], []);

    // messageの第一要素をinteger型に変換
    integer receiverScriptNum = llList2Integer(lines, 0);

    // このスクリプト宛のメッセージか?
    if (receiverScriptNum == SCRIPT_RECEIVER) {

    // どのスクリプトからのメッセージか?
    if (senderScriptNum == SCRIPT_SENDER) {

    // 自分にのみメッセージを表示
    llOwnerSay(llLIst2String(lines, 1));
    }
    }
    }
    }


どうでしょうか。メッセージを出すだけでもそれなりのコードを記述しなければなりません。
この見本は、本来は、スクリプトが複数(数十個)あって、協調して動作するような場合を前提に組まれています。
ですので、メッセージを表示するだけにしては、おおげさなコードになってしまっています。ただ、こういった仕組みを最初につくっておくことが、16KBを超えるスクリプトを作る上でどうしても必要になってきます。


このとき、1スクリプト1機能として、メッセージにより連携するような仕組みをつくることが大きいLSLのプログラムを作るコツになります。


また、SCRIPTSENDERなどのIDを自分でつくっていますが、linkmessageのsenderNumを使えば同じようにスクリプトを識別できるのではと考えた人もいるのではないでしょうか。残念ながら、この番号はプリムの番号になるので、同じプリム同士であれば、同じ番号になってしまいます。また、プリムの番号はSecond Lifeによって決められてしまうので、ちょっとしたこと(プリムを結合する順番)で変わってしまい、使いずらい値となっています。ここでは自分で定数をつくって利用することをお勧めします。


以上が16KBを超えたLSLを作るためのプログラミングスタイルの基本になります。


次回は、メッセージの形式について深く取り上げたいと思います。

続きを読む


Page 1 of 212»

このページの先頭へ