ソラマメブログ

2007年05月02日

デモ商品を作ろう(スクリプト初級第二十一回)

今回はデモ用の商品を作る方法について書こうと思います。
デモ用とは、例えば一定期間のみ使用可能なもの、特定回数使ったら使えなくなるようなサンプル商品です。
当然ながら、スクリプトを使って制御をしますので、スクリプトが組み込めない商品(スキン・服・テクスチャ・アニメーションなど)に関してはここで解説する方法は使えません。

スクリプトが組み込める商品であれば、どんなものにも応用は利きますが、今回は特にアタッチメントについて見ていきます。
というのも、アタッチを制御するスクリプトについて今まで説明していないからです(^^;

デモ用商品のスクリプトを通して、スクリプトによるアタッチの制御についてぜひ覚えて下さい。
デモ用商品

デモ用の商品に関しては、いろいろと制限方法があるかと思いますが、今回は単純に、
「一定時間のみ使用可能」
「特定回数のみrez可能」
この二つを実装してみたいと思います。

まず「一定時間のみ使用可能」ですが、ここまで読み進んできた皆さんであれば、タイマーイベントを使えば「一定時間が経過したとき」の処理ができることはすぐ思い当たるかと思います。
「特定回数のみrez可能」のほうについても、少し前にやったrezのスクリプトで、「rezされたとき」のイベントがありましたので、回数を数える変数を用意すれば簡単にできますね。
スクリプトの前半部分では、タイマーイベントとrezイベントを使い、デモ商品の使用期限をチェックするようにします。

使用期限が切れた後が今回のポイントです。
今回はアタッチメントを想定していますので、「使用期限が切れたら」まず「アタッチを解除」します。
強引にインベントリに戻してしまうわけですね。
しかしそれだけでは再びアタッチ可能です。

「期限が切れたあと」に「アタッチされたら」そのたびに「アタッチを解除」する仕組みが必要でしょう。
これでアタッチしようとしてもアタッチできなくなります。

これだけでも使用不能にはなりますが、人によっては使用期限の切れたアタッチメントをオブジェとして使い続けようとするかもしれませんw
そこでトドメの一撃として、「rezされたら」「オブジェクトを消去する」ようにしておくと完璧です。
なんと陰険なスクリプトでしょうかw

以上の仕組みをまとめてみます。
例の「ステート」「イベント」「処理」を念頭に置いて考えて下さいね。

ステートイベント処理
デモ期間中
(default)
タイマー使用可能時間を減らす
使用可能時間がゼロになったら「使用不能」ステートへ
rezrez回数をカウントする
rez可能回数を超えたら「使用不能」ステートへ
使用不能ステートエントリーアタッチを解除
アタッチアタッチを解除
rezオブジェクトを消去


以上のような構成のスクリプトができればOKですね。

アタッチの制御

構成は見えましたが、実現するための関数やイベントは今までに登場していないものです。
まず難しいところから行きましょう。
「アタッチを解除」する関数です。

  llDetachFromAvatar();

この関数でアタッチを解除することができます。
引数や戻り値はありません。
一体これのどこが難しいのかと言うと、この関数、パーミッションがなければ使えないのです。

パーミッションについてはお金の支払いやアニメーションのところで説明しましたが、覚えていますでしょうか。
lslの中でも特にわかりにくい概念の一つがパーミッションかと思います。

復習をかねて簡単に説明すると、セキュリティの観点から勝手に実行してはマズイようなスクリプトに関して、使用者の許可を取らなければいけない仕組みがパーミッションです。

アタッチメントを勝手に着脱されてしまったら、やはり困りますよね。
例えば公衆の面前でいきなりハゲにされたり、股間から何やらにょっこり生えてきたりしたら、不愉快に感じる人がいるでしょう。
ですのでアタッチメントの制御にはパーミッションが要ります。

アタッチメント制御のパーミッションを取得するには以下のようにします。

  llRequestPermissions(key, PERMISSION_ATTACH);

keyはパーミッションを要求する相手のUUIDですね。
PERMISSION_ATTACHがアタッチメント制御のパーミッション要求の指定です。
基本的にはこの要求が出された時点で、パーミッションダイアログが開きます。
(ここで「基本的には」と書いた理由はコメントを参照して下さい(^^)

要求に対して許可・不許可の回答が為されると、run_time_permissionsイベントが起きます。
run_time_permissionsイベントの中でアタッチメント制御が許可されたかどうかを調べるには以下のようにします。

  run_time_permissions(integer p){
    if (p & PERMISSION_ATTACH){
      // 許可された
    }else{
      // 許可されなかった
    }
  }

パーミッションが許可されて始めて、先ほどのアタッチを解除する関数llDetachFromAvatar()が使えます。
面倒ですが、この手続きは必須です。

次に、「アタッチされた」ときのイベントを見てみましょう。
これはズバリ、attachイベントというのがあります。

  attach(key attached) {
    // attachedはアタッチした人のUUID
    // NULL_KEYの場合はデタッチされたことを意味する
  }

このイベントは「デタッチされたとき」にも起こります。
その場合は引数にNULL_KEYが入ってきます。

それほど難しくはないでしょう。

最後に、これはアタッチメントに関係ありませんが、「オブジェクトを消去する」方法です。
これにはllDie()という物騒な関数を使います。

  llDie();

引数・戻り値はありません。
この関数を実行すると問答無用でオブジェクトが消去されます。

ただし、この関数はアタッチメントに対しては使えません。
あくまでも地面などにrezされた状態でなければ機能しません。

もしこの関数がアタッチメントに対して使えるのであれば、「使用期限が切れたとき」にわざわざ「アタッチ解除」などというまだるっこしい真似をせず、いきなり「オブジェクト消去」できるのですが・・・。
それができないがために「アタッチ解除」で済ませています。

デモ商品用スクリプト

では実際のスクリプトを組んでみましょう。

integer rez_count=5; // 残りrez回数(今回は5回までrez可能に設定)
integer use_hour=4; // 残り使用時間(今回は4時間使用可能に設定)

settext(){ // デモであることを示す文字列を表示するユーザー関数
  string msg="***DEMO***\n";
  msg += "You can rez this attachiment " + (string)rez_count + "times,\n"; // 残りrez回数を表示
  msg += "or use for " + (string)use_hour + " hours."; // 残り使用時間を表示
  llSetText(msg, <1.0, 0.0, 0.0>, 1.0); // 赤い文字でフロートテキストとして表示する
}

default { // デモ期間中のステート
  state_entry(){
    settext();
    llSetTimerEvent(3600.0); // 一時間ごとのタイマーを起動
  }
  
  timer(){
    use_hour --; // 一時間ごとに残り使用時間を減算
    if (use_hour < 0){ // 使用時間が0以下になったらステートdeadに遷移
      state dead;
      return;
    }
    settext();
  }

  on_rez(integer p){
    rez_count --; // rezされるたびに残りrez回数を減算
    if (rez_count < 0) { // 使用回数が0以下になったらステートdeadに遷移
      state dead;
      return;
    }
    settext();
  }
}

state dead { // デモ期間終了後のステート
  state_entry(){
    llSetObjectName("Expired Attachment - Delete Me");
    llRequestPermissions(llGetOwner(), PERMISSION_ATTACH); // アタッチ解除のためにパーミッション要求
  }
  
  run_time_permissions(integer p){
    if (p & PERMISSION_ATTACH){ // パーミッションが許可されたらメッセージを表示してデタッチ
      llSay(0, "This attachiment was expired!");
      llDetachFromAvatar();
      llDie(); // 一応書いてあるがこれは動作しない
    }else{ // パーミッションが許可されなかったらシツコク再要求w
      llRequestPermissions(llGetOwner(), PERMISSION_ATTACH);
    }
  }
  
  attach(key attached) {
    if (attached != NULL_KEY) { // アタッチされたらアタッチ解除のためにパーミッション要求
      llRequestPermissions(llGetOwner(), PERMISSION_ATTACH);
    } else {
      llDie(); // 一応書いてあるがこれは動作しない
    }
  }
  
  on_rez(integer p){
    llDie(); // 地面にrezされた瞬間にオブジェクト消滅
  }
}


デモであることを示すために、llSetText関数を使って「DEMO」の文字と残りrez回数・残り使用時間を表示するようにしました。
llSetText関数に渡す文字列msgの中で"\n"と書いてあるのは「改行」です。
メッセージは折り返されて3行に表示されます。

今回のポイント

・アタッチの解除:
  llDetachFromAvatar();

・アタッチイベント:
  attach(key attached) {
    // attachedはアタッチした人のUUID
    // NULL_KEYの場合はデタッチされたことを意味する
  }

・オブジェクト消去:
  llDie();

アタッチを制御する関数は他にもいくつかありますが、そのうちどこかでお目にかかる機会もあるでしょう。
着脱するときにはパーミッションが必要であることを忘れないで下さい。

アタッチイベントは、アタッチするたびにスクリプトを初期化したいときなどにも使えます。

llDieの使用には注意して下さい。
きちんとインベントリにオブジェクトのコピーを保持しておかないと、完全にオブジェクトは消滅してしまいます(^^;

さて、初級スクリプトも残りあとわずかです。
来週には完結予定・・・ですかね?(^^;


同じカテゴリー(初級スクリプト)の記事画像
衝突判定(スクリプト初級第二十三回)
カメラ制御(スクリプト初級第二十二回)
センサーを使おう(スクリプト初級第二十回)
HUDを作ろう(スクリプト初級第十六回)
prim間通信(スクリプト初級第十五回)
アニメさせよう(スクリプト初級第十三回)
同じカテゴリー(初級スクリプト)の記事
 衝突判定(スクリプト初級第二十三回) (2007-05-08 12:15)
 カメラ制御(スクリプト初級第二十二回) (2007-05-07 14:36)
 センサーを使おう(スクリプト初級第二十回) (2007-05-01 12:15)
 ステートのこと(スクリプト初級第十九回) (2007-04-27 12:15)
 rez!(スクリプト初級第十八回) (2007-04-26 12:15)
 音を鳴らそう(スクリプト初級第十七回) (2007-04-25 13:35)
この記事へのトラックバック
 
難局にぶつかり変な座り様で途方に暮れる拙者 kichijoujiショップ2階にて
 
昨日見知らぬ方よりIMにて相談があり申した。
拙者のブ
頼まれ仕事ですったもんだ【Rinsui SL+ Making Blog】at 2007年09月03日 00:59
この記事へのコメント
ダイアログでNoを押してればずっと使用可......φ(..)
Posted by Ichigo Mayo at 2007年05月02日 13:11
>Ichigo Mayoさん

確かに、それは誰もが考えそうなところですね(^^;
実際に試していただくとどうなるかわかりますが・・・補足です。

ややこしくなるので書かなかったのですが、実はアタッチメントのルートプリムから実行したllRequestPermissions要求は、「自動的に」承認されます。
つまりパーミッションダイアログは表示されません。
llRequestPermissions要求後にすぐに自動的にrun_time_permissionsイベントが発生し、オブジェクトはデタッチされます。

・・・となるとパーミッションが拒否されたときの処理はいらないじゃないかと言われそうですが、ハイ、その通りでございます(__;

なお、deadステートになった時点で、オブジェクトに入っている他のスクリプトやテクスチャ、アニメーションなどを全て消去すると、デモ商品の不正利用をさらに防げます。

パーミッションの自動承認は、悪用しようと思うといろいろできてしまうので諸刃の剣ではありますが・・・。
Posted by Miz at 2007年05月02日 14:25
想定外時の処理もバグ回避に有用だと思いますよ
今後LSLが不変とも思えませんし
Posted by 通りすがり at 2007年05月02日 16:05
>通りすがりさん

そうですね。
フォローありがとうございます(^^
Posted by Miz at 2007年05月02日 17:19
このスクリプトは、ルートプリムに組み込めば、そのまま
使える物なのでしょうか?
あと、テストをする場合、自分(製作者)でm、テスとできるのでしょうか??
Posted by takahiro at 2007年08月07日 21:47
>takahiroさん

基本的にはそのまま使えるはずですが、オブジェクトを消滅させたりする処理が含まれるので、一通り理解してから使ったほうがいいかもしれません。

また、製作者か否かの判定は組み込んでいませんので、誰が使っても同じ動作になります。
Posted by MizMiz at 2007年08月13日 11:14
こんばんは、またまた質問させていただきます。
ここの中にあるアタッチをさせることはできたんですけど(デモ商品としてではなく、単にアタッチです)、オブジェクトの中にある物をアタッチする場合は、どうしたらよいのでしょうか?
default {
touch_start(integer num_detected) {
llRequestPermissions(llDetectedKey(0), PERMISSION_ATTACH);
}

run_time_permissions(integer perm) {
if (perm & PERMISSION_ATTACH) {
llAttachToAvatar(ATTACH_RHAND);
}
}
}
コンテンツ内のパーミッションを取得して、どうこうするのかな?とか思ったんですけど、改造ポイントがいまひとつ・・・
ご指導のほど、よろしくお願いします
Posted by 遮那 at 2007年08月24日 00:09
>遮那さん

残念ながら、オブジェクトのコンテンツの中から直接アタッチは出来ません。

一度コンテンツ内のオブジェクトをrezし、それからrezされたオブジェクト内のスクリプトでアタッチを行います。

簡単に処理の流れを書くと以下のようになります。

(1)タッチ等のきっかけでコンテンツ内のオブジェクトをrez
  rezの方法は「初級スクリプト:rez!」を参考にしてみて下さい。
(2)rezされたオブジェクトに対して、タッチした人のUUIDを通知
  通知にはlistenを使うのが一般的です。
(3)rezされたオブジェクトが通知されたUUIDに対してパーミッション要求
(4)パーミッションが取得できたらアタッチ

(3)、(4)は遮那さんが書いてらっしゃるコードとほぼ同じです(タッチイベントではなくリッスンイベントに変わるでしょうが)。

おそらく工夫が必要なのは(2)でしょう。
(2)の部分を細かく考えると、

a、(rezオブジェクト側)on_rezイベントでリッスン開始
b、(親オブジェクト側)object_rezイベントでUUIDをsay

双方のオブジェクトで連携しあう処理が必要です。
厳密に考えるのであれば、タイミングが重要になりますが、object_rezイベントの最初にllSleep(1.0)などとして、UUIDのsayを一瞬遅らせる(遅らせる間にリッスンが発動していることを想定する)だけでも一応動作はするかと思います。
Posted by MizMiz at 2007年08月24日 10:41
MIZさんこんにちは^^。

Mizさんのおっしゃっているように、スクリプトを組んでみました。
椅子に座るとアクセサリを座った人にアタッチする、というものです。
llAttachToAvatarを使っています。

自分では上手くいったのですが、ほかの人がすわったときに
「オーナ以外の人にアタッチしようとしています」という警告がでます。
リファレンスを見るとllAttachToAvatarはアイテムのオーナーに
しか実行できないとありました・・泣

ただ、どこかの店で、アイテムをダイレクトに手に持たされたような記憶があるので(勘違いかもしれませんが・・)抜け道があるのかなぁと・・。
なにかいいアイデアなどあれば教えていただけないでしょうか。
よろしくお願いしますー。

ここでスクリプトを学んでひと月半、そろそろ初級スクリプト講座を卒業できそうです^^ 

ようやく自分の作りたい物がつくれるようになってきましたー。
Posted by joju at 2007年10月16日 00:54
>jojuさん

>どこかの店で、アイテムをダイレクトに手に持たされたような記憶がある

ほうほう。
それは興味深い処理ですね。

そのダイレクトにアタッチされたオブジェクトは、その後当然外したと思いますが、インベントリに残っていますよね?
残ってれば、当然オーナーはjujuさんのはずですが、残ってないとなると・・・何か特殊なことをしてるのかもしれませんね。

うーん。
そのお店がどこだったか思い出せるようなら、もう一度試してみていただきたいところですが・・・。
Posted by MizMiz at 2007年10月17日 09:47
はい、もうすこし調べてみますw。
でもMizさんでもご存じないとなると・・やっぱり私の勘違いのような気がしてきました・・。

渡したオブジェクトをわざわざインベントリから探して装着ってけっこうわずらわしい作業なのでなんとかしようと思ったのですが・・。
やっぱり素直にllGiveInventoryしかないのかなぁ・・・・。
Posted by joju at 2007年10月18日 00:23
こんばんは。
これって、商品を買うユーザーの立場から考えて、スクリプトを削除したとしたら、どうなるんですか?
おそらく、スクリプトは機能しないような気がするのですがどうなるでしょうか?
素人な発言で申し訳ないです。
もし、スクリプトを削除されない方法があるのでしたら、
教えてほしいです。
以上、よろしくお願いします。
Posted by Karin at 2007年12月17日 23:07
>Karinさん

そうですね、そこは注意すべきところです。
最近知られるようになってきましたが、NoModの中にあるスクリプトを取り出したり、削除することは可能になっています(バグではなく仕様だそうです)。

したがって、デモスクリプトと本来の商品の機能を実装した2つのスクリプトがオブジェクトの中に入っていた場合、どちらがデモスクリプトなのかわかれば、それを削除して本物と同じように使い続けることもできてしまいます。

対応方法はいくつか考えれれますが、一つはデモスクリプトの機能を本来の商品の機能のスクリプトと合体させて1つのスクリプトにしてしまうことです。
つまりスクリプトを消せば商品の機能自体も失われるような実装です。

二つのスクリプトを一緒にできないような場合、商品機能のスクリプトのほうに、デモスクリプトの存在チェックを組み込むという方法もあります。

スクリプトを使っていないような商品の場合は、「コンテンツから何かが削除された時点でオブジェクトを壊す」ようなスクリプトを複数組み込んでおく手もあります。
このときの「壊す」というのは、例えばテクスチャをデフォルトに戻してしまうとか、各primを全部デフォルトキューブにしてしまうとか、クリエイターにアラーとを送るとかw、いろいろな方法がとれます。
破壊スクリプトを複数にするのは、どれか一つでもスクリプトが残っていれば自爆してくれるようにするためです。

NoModのオブジェクトからスクリプトが取り出せる・削除できる件については以下の記事が参考になります。

http://blog.livedoor.jp/nockme/archives/64839670.html
Posted by MizMiz at 2007年12月19日 09:57
no modify で参照できるのはルートプリムに組み込まれているスクリプトだけです。
なので、ルートプリム以外に組み込めば、問題ありません。
Posted by TOSHI at 2008年01月14日 10:27
 こんにちは^^
スクリプト、ド素人なBackard Wylie >w<;;;(バイク屋してるのにw
 と言っても、外装製作担当なのですが、、、w
さてさて本題ですが、今回のRez講座は、ひじょ~に!興味が有ります^0^
 なぜ?ってw それはですね~、私の作っているバイクは、乗り物としてのバイク本体(ベース)と、ボディーを切り離して装着する事で、30プリム制限を乗り越えたバイクを製作しているからです。
 内容的には、本体(ベース)には、エンジンスクリプトを仕込み、30プリム以内で作り上げます。(タイヤ、シャーシー、テールランプ、カウルなどなど)
 そして、装着するボディーは、255プリム以内で製作して、物理的な立体感を実現しています。
ここで問題になってくるのが、乗車準備の手間です^^;
 本体を、イベントリーからRezした後に、ボディーを手動で装着しなくてはいけない、、、これが、ネックに成っています>w<;;;
 いろいろ調べて、本体のコンテンツ内に、ボディーを入れて本体をRezするとボディーも同じ座標にRezさせて、、、且、ボディーにタッチすると、アタッチが作動するところまで来たのですが、、、大問題が、、、>w<;;;

1.本体(ベース)を、Rez

2.本体Rezと同時に、ボディーも、Rezされる

3.ボディーに、タッチして、アタッチさせる

4.本体に乗る

の過程を踏めば、正常に乗車可能なのですが、、、
「3.」と、「4.」の過程を逆に行うと、SIMが落ちてしまいます>w<;;;
本当は、スクリプトを提示した説明をしたいのですが、悪用されると困るので、公表できな問いかけで、ごめんなさい^^;
 上記の内容から、先生が思い当たる注意事項等、教えて下さい^^
また、保々毎日INしてるので、SL内でのIMや、待ち合わせなどでも構いませんので、宜しく御願い致します。 m(_ _)m
Posted by Backard Wylie at 2008年04月01日 14:18
>Backard Wylieさん

SIM落ち・・・というのは尋常じゃないですね。
問題が大きそうなので、以下のBBSに転載してもよろしいですか?

http://bb2.atbb.jp/lslbbs/index.php
Posted by Miz at 2008年04月01日 15:08
 あい^^
宜しく御願い致します。 m(_ _)m
Posted by Backard Wylie at 2008年04月01日 16:27
>Backard Wylieさん

以下のトピックに転載しましたので、補足などありましたらよろしくお願いいたします。
http://bb2.atbb.jp/lslbbs/viewtopic.php?t=71
Posted by Miz at 2008年04月01日 18:05
 BBSの使い方が解らなくて使ってなかったのですが、今回の件で使い方が解りました^0^

まだ問題の解決は出来ていませんが、なにか摑めた気がします^^
 今後も、素人的な質問しちゃうと思いますが、
宜しく御願い致します。 m(_ _)m

PS:いつも、このHPのスクリプトを使って楽しんでいるので、これからも楽しいスクリプトお願いします~^0^ノ
Posted by Backard Wylie at 2008年04月05日 17:04
 問題の解決が、できたました~^0^
臨時を有効にして、Rezし忘れ放置の防止。
 ファントム化により、ベースとの無限連鎖衝突の回避。
やってみると、「あ~、こんだけの事だったのかぁ~、、、」とw
 プチ、カルチャーショック!www
Posted by Backard Wylie at 2008年04月07日 16:29
 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。