BlackSheep-LSL@Wiki

ポーズボール

最終更新:

mizcremorne

- view
メンバー限定 登録/ログイン

はじめに


引き続いてアニメーションのスクリプトです。
前回は単にタッチするとアニメーションするだけのスクリプトでしたが、これではいまひとつ実用性がありません。
アニメーションが最もよく利用されているのはポーズボールでしょう。

SLで遊んだ人なら誰もが一度は見たことがあると思います。
しばしば木陰とかベッドの上に置いてある、赤と青のボール。
あれですw
クラブのフロアや、バーのストゥールなんかにも置いてあるところがあります。

これらのボールは、アバターが座ったときに特定のアニメーションを実行するものです。

座ったときのイベント


スクリプトの構成はすぐに予想がつくかと思います。
前回のスクリプトは「タッチしたとき」にアニメーションさせましたが、今回は「座ったときに」アニメーションさせるだけの違いですね。
ですので、「座ったとき」のイベントさえわかれば簡単に作れそうに思えます。

では「座ったとき」のイベントは・・・。

実は、これが存在しないのです。
SLの中で何かに座るというのはよくある行動なので、イベントとして存在していてもいいと思うのですが、ありません。
代わりに、「オブジェクトの状態が変化したとき」というイベントを使うことになります。

「オブジェクトの状態が変化したとき」のイベントはその名もchanged?イベントと言います。

changedイベント


 changed(integer change){
   // 処理
 }

引数changeには、オブジェクトの何が変化したのか、そのパラメータが入ってきます。
例えば「オブジェクトのコンテンツの中身が変わった」とか「色が変わった」「大きさが変わった」「テクスチャが変わった」「オブジェクトのリンクが変わった」「オーナーが変わった」などです。
とにかくオブジェクトのいろいろな変化を捉えるイベントですので、用途は広いです。
しかし、このイベントと「座ったかどうか」がどのように結びつくのでしょうか。
changed?イベントのパラメータには、「オブジェクトに座ったかどうか」そのものはありません。

実はアバターがオブジェクトに座るという行為は、一種のリンクとして処理されます。
「座る」ことによってオブジェクトとアバターがリンクされると考えてください。
すると、changed?イベントの中で「オブジェクトのリンクが変わった」ことを捉えることができますので、これを利用して「座ったとき」を検出します。

まずオブジェクトのリンクが変わった時を捉えるには以下のように書きます。

 changed(integer change){
   if (change & CHANGED_LINK) { // リンクが変わったかどうか
     // リンクが変わったときの処理
   } else {
     // リンク以外が変わったとき(リンクが変わっていないとき)
   }
 }

CHANGED_LINKというのは「リンクの変化」を示す定数です。
例えばこの値の変わりにCHANGED_OWNERを使うと、「オーナーが変わったかどうか」を調べられますし、
CHANGED_INVENTORYを使うと、「オブジェクトのコンテンツの中身が変わったかどうか」を調べられます。

 changed(integer change){
   if (change & CHANGED_OWNER) { // オーナーが変わったかどうか
     // オーナーが変わったときの処理
   } else {
     // オーナー以外が変わったとき(オーナーが変わっていないとき)
   }
 }

 changed(integer change){
   if (change & CHANGED_INVENTORY) { // コンテンツが変わったかどうか
     // コンテンツに変化があったときの処理
     // 多くの場合、何かがコンテンツに追加されたのを検出するのに使う
   } else {
     // コンテンツ以外が変わったとき(コンテンツが変わっていないとき)
   }
 }

CHANGED_LINK、CHANGED_OWNER、CHANGED_INVENTORYの3つは比較的使う機会が多い定数かと思います。
この書き方はお決まりと言ってもいいでしょう。

さて、changed?イベントとCHANGED_LINKを使って、オブジェクトのリンクに変化があったことは検出できますが、リンクの変化と言っても単純ではありません。
今回検出しようとしている「アバターが座った/立った」も含まれますし、純粋にオブジェクトのリンクに新たなprimが追加された、外された場合もありえます。
「座ったのかどうか」を調べるには、もう一工夫が必要です。
そのためにllAvatarOnSitTarget()関数を使います。

llAvatarOnSitTarget関数


 key llAvatarOnSitTarget()

この関数はオブジェクトの上に座っているアバターのUUIDを返します。
誰も座っていない場合はNULL_KEYが返ってきます。
これを利用すれば、以下のようにして「座ったかどうか」の判定が可能です。

  • 誰も座ってないときに
    • オブジェクトのリンクが変化し
      • 座っている人がいたら・・・「誰かが座ったのだとわかる」
      • 座っている人がいなければ・・・「純粋にprimのリンクが変わった」
  • 誰かが座っているときに
    • オブジェクトのリンクが変化し
      • 座っている人がいたら・・・「純粋にprimのリンクが変わった」
      • 座っている人がいなければ・・・「座っていた人が立ったとわかる」

この判定方法もお決まりのパターンになりますので丸暗記しておくと楽です。

 key siton = NULL_KEY; // 現在座っている人のUUIDを管理する変数
      :
     中略
      :
 changed(integer change){
   if (change & CHANGED_LINK) { // リンクが変わった
     key av = llAvatarOnSitTarget(); // 現在座っている人のUUIDを調査
     if (siton != NULL_KEY) { // 今まで座っている人が居た場合
       if (av == NULL_KEY) { // 今は座っている人がいない
         // つまり「座っている人が立った」ときの処理
         siton = NULL_KEY; // 現在座っている人のUUIDを更新(誰も座ってない)
       }
     } else { // 今まで座っている人はいなかった場合
       if (av != NULL_KEY) { // 今は座っている人がいる
         // つまり「誰かが新たに座った」ときの処理
         siton = av; // 現在座っている人のUUIDを更新
       }
     }
   }
 }

key型変数sitonは「現在座っている人のUUID」を管理するための変数です。
誰も座っていないときにはこの変数の値をNULL_KEYにしておきます。

ポーズボールスクリプト


ではポーズボールスクリプトを作ってみましょう。
椅子などに組み込んでも構いません。
ただし椅子に組み込む際に注意すべき点は、ポーズによっては座る位置が大きくずれたりしますので、llSitTarget?()関数を使ってアバターの座る位置を調整しなければならない点です。

string animation_name="sexy_sit"; // 実行するアニメーション
key siton = NULL_KEY; // 現在座っている人のUUIDを管理する変数

vector sit_pos = <0.0, -0.3, 0.4>; // 座る位置
vector sit_rot = <0.0, 0.0, 270.0>; // 座る角度

default {
  state_entry(){
    llSitTarget(sit_pos, llEuler2Rot(sit_rot * DEG_TO_RAD));
  }

  changed(integer change){
    if (change & CHANGED_LINK) {
      key av = llAvatarOnSitTarget();
      if (siton != NULL_KEY) {
        if (av == NULL_KEY) { // 座ってた人が立った
          llStopAnimation(animation_name);
          siton = NULL_KEY; 
        }
      } else {
        if (av != NULL_KEY) { // 誰か座った
          siton = av;
          llRequestPermissions(siton, PERMISSION_TRIGGER_ANIMATION);
        }
      }
    }
  }

  run_time_permissions(integer perm) {
    key perm_key = llGetPermissionsKey();
    if (perm_key == siton) {
      if (perm & PERMISSION_TRIGGER_ANIMATION){
        list anms = llGetAnimationList(siton);
        integer i;
        for (i = 0; i < llGetListLength(anms); i++){
          llStopAnimation(llList2Key(anms, i));
        }
        llStartAnimation(animation_name);
      }
    }
  }
}

誰かが座ったとき、llRequestPermissions()関数でアニメーションのパーミッション要求を行います。
パーミッションが許可されると、アニメーション対象者の実行中のアニメーションを調べて、まずはそれらを全て停止します。
なぜかと言うと、座ったときにはデフォルトで「座るポーズ」のアニメーションが実行されるためです。
また、AO(アニメーションオーバーライド)のアタッチメントを付けている人などは、座ったときにデフォルト以外のアニメーションを実行する可能性があります。

もしもこのスクリプトで実行したいアニメーションの優先順位が低いと、デフォルトの座るポーズのほうが優先され、意図した通りのポーズで座ってくれません。
そのため、まず最初に実行中のアニメーションを全て停止しているのです。

今回のスクリプトでは、同じオブジェクトに同時に何人もの人が座ることは考えられませんので、アニメーション対象を細かく管理する必要はありません。
従って、「立ったとき」には余計なことを考えずにllStopAnimation?()関数でアニメーションを停止しています。

ボールの透明化


これでポーズに関しては意図した通りに動きますが、スクリプトをボールに組み込んだ場合、座ったあともボールが表示されているのは少々見苦しいところです。
椅子などに組み込んだ場合にはこのままで構いませんが、座ったときにオブジェクトを透明化する処理を考えて見ましょう。

単純にボールを消すだけなら難しくはありません。
ボールの透明度を変更し、完全な透明にしてしまえばOKです。
透明度を変更する関数はllSetAlpha()と言います。

llSetAlpha関数


 llSetAlpha(float alpha, integer face)

  • float alpha

透明度を指定する引数です。
0.0を指定すると完全透明、1.0を指定すると完全不透明になります。

  • integer face

透明度を変化させたいオブジェクトの面を指定します。
オブジェクト全体を対象とする場合はALL_SIDESという値を指定します。

これを先ほどのスクリプトに組み込んでみます。

string animation_name="sexy_sit"; // 実行するアニメーション
key siton = NULL_KEY; // 現在座っている人のUUIDを管理する変数

vector sit_pos = <0.0, -0.3, 0.4>; // 座る位置
vector sit_rot = <0.0, 0.0, 270.0>; // 座る角度 

default {
  state_entry(){
    llSitTarget(sit_pos, llEuler2Rot(sit_rot * DEG_TO_RAD));
  } 

  changed(integer change){
    if (change & CHANGED_LINK) {
      key av = llAvatarOnSitTarget();
      if (siton != NULL_KEY) {
        if (av == NULL_KEY) { // 座ってた人が立った
          llStopAnimation(animation_name);
          llSetAlpha(1.0, ALL_SIDES);
          siton = NULL_KEY; 
        }
      } else {
        if (av != NULL_KEY) { // 誰か座った
          siton = av;
          llSetAlpha(0.0, ALL_SIDES);
          llRequestPermissions(siton, PERMISSION_TRIGGER_ANIMATION);
        }
      }
    }
  } 

  run_time_permissions(integer perm) {
    key perm_key = llGetPermissionsKey();
    if (perm_key == siton) {
      if (perm & PERMISSION_TRIGGER_ANIMATION){
        list anms = llGetAnimationList(siton);
        integer i;
        for (i = 0; i < llGetListLength(anms); i++){
          llStopAnimation(llList2Key(anms, i));
        }
        llStartAnimation(animation_name);
      }
    }
  }
}

これで座ったときにボールは消え、立つと再び見えるようになります。

今回のポイント


  • 「座ったとき」のイベント:

 key siton = NULL_KEY; // 現在座っている人のUUIDを管理する変数
      :
     中略
      :
 changed(integer change){
   if (change & CHANGED_LINK) { // リンクが変わった
     key av = llAvatarOnSitTarget(); // 現在座っている人のUUIDを調査
     if (siton != NULL_KEY) { // 今まで座っている人が居た場合
       if (av == NULL_KEY) { // 今は座っている人がいない
         // つまり「座っている人が立った」ときの処理
         siton = NULL_KEY; // 現在座っている人のUUIDを更新(誰も座ってない)
       }
     } else { // 今まで座っている人はいなかった場合
       if (av != NULL_KEY) { // 今は座っている人がいる
         // つまり「誰かが新たに座った」ときの処理
         siton = av; // 現在座っている人のUUIDを更新
       }
     }
   }
 }

  • 「オーナーが変わったとき」のイベント:

 changed(integer change){
   if (change & CHANGED_OWNER) { // オーナーが変わったかどうか
     // オーナーが変わったときの処理
   } else {
     // オーナー以外が変わったとき(オーナーが変わっていないとき)
   }
 }

  • 「コンテンツが変わったとき」のイベント:

 changed(integer change){
   if (change & CHANGED_INVENTORY) { // コンテンツが変わったかどうか
     // コンテンツに変化があったときの処理
     // 多くの場合、何かがコンテンツに追加されたのを検出するのに使う
   } else {
     // コンテンツ以外が変わったとき(コンテンツが変わっていないとき)
   }
 }

  • オブジェクトの透明度変更:

 llSetAlpha(float alpha, integer face)

※オブジェクトが複数のprimで構成されている場合は、
 llSetLinkAlpha(integer linknumber, float alpha, integer face)
を使います。
linknumberに「LINK_SET」を指定すると全primの透明度を変更できます。

 llSetLinkAlpha(LINK_SET, 0.0, ALL_SIDES); // 全primを完全透明にする
 llSetLinkAlpha(LINK_SET, 1.0, ALL_SIDES); // 全primを完全不透明にする

今回のスクリプトはアニメーションを使う際に最も汎用的なものになります。
使用するアニメーションによってあらゆる場所で使えることでしょう。

例えば寝そべるアニメーションを使えばベッドに組み込めます。
キーを打つアニメーションと使えばパソコンデスクが作れます。
面白いアニメーションを作り、いろいろと応用してみて下さい。

さて・・・。
来週はまた新たなスクリプトに取り組みたいと思いますが、今のところ何を作るか決めていません(^^;
prim間の通信を行う方法について説明する予定ではあります。

「アレを作る方法を教えてくれ」などありましたらご提案下さい。
スクリプトの難易度を考えた上で、順次取り上げていきたいと思います。

ではまた来週~。

名前:
コメント:
記事メニュー
目安箱バナー