BlackSheep-LSL@Wiki ベンダーを作ろう

※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。


はじめに


SLでアイテムを販売するには、必ずしもスクリプトが必要になるわけではありません。
販売用のオブジェクト(ポスター、看板、箱など)を用意し、そのオブジェクトのコンテンツの中に売りたいアイテムを詰め、値段とコンテンツを販売する設定にすれば、ひとまず売ることは可能になります。
ですが、この方法はアイテム一つ(あるいは1セット)につき一つのオブジェクトが必要となり、商品が増えるに従ってprim数が増加していくという問題点があります。
また、販売用オブジェクトを並べるにはどうしてもスペースが必要になりますので、限られた店舗スペースでは、自ずと販売できるアイテムの数が限られてしまいます。

スクリプトを使ったベンダーは、そのような問題点を解決するために使われます。
よく見かけるものは、矢印ボタンを押すと商品の画像が切り替わり、欲しいものを見つけたらL$を支払い、アイテムが手に入るという仕組みのものです。
このようなベンダーでは、商品が増えたときにオブジェクトを増やさなくて済みます。

念のために言っておきますが、スクリプトによるベンダーのほうが優れているというわけではありません。
いちいち矢印をクリックして商品を探すのは、お客さんからすると面倒な作業です。
順番に見て行っても、欲しいアイテムが見つかるかどうかも確実ではないですし、途中で面倒になって見るのをやめてしまうことも考えられます。
どんな商品があるのか一目瞭然にするには、別途工夫が必要になるでしょう。

とは言うものの、この記事はスクリプトの勉強ですので、商売のやり方について書くのは少々的外れですね(^^;
とにかくベンダースクリプトを作ってみて、使えるか使えないかは別問題としておきましょう。

ベンダーの仕組み


シンプルなもので考えます。
商品の画像を表示するための板と、商品を選択するための矢印のオブジェクトを二つ(左右、あるいは上下)の3primのオブジェクトをまずは作りましょう。
矢印をクリックすると、商品の画像が変わります。
商品の画像をクリックすると、その商品を購入するための支払いダイアログが表示され、お客さんがお金を払ったら商品を渡すようにします。

ベンダーのオブジェクトは、リンクするのを忘れないで下さい。
その際、商品を表示するprimをルートしておいて下さい。スクリプト本体はルートプリムに書きます。


こんな感じのベンダーですね・・・あまりに適当過ぎですが(^^;

スクリプトの構成はどのようにしたら良いでしょうか。
まず、ステートは一つで問題はないでしょう。
商品の画像はいろいろと変わりますが、「表示されている商品を販売する」という機能は変わることがありません。
機能が変わらないということは、処理すべきイベントも変わりませんので、わざわざステートを複数用意する意味が無いという事です。

イベントは、商品を選択する際のタッチイベント、それから「お客さんがお金を支払ったとき」のイベントが必要になりますね。

実装しなければならない処理の中に「画像の変更」がありますが、これにはllSetTexture?()という関数を使います。

llSetTexture関数


 llSetTexture(string texture, integer side);

引数「string texture」はセットするテクスチャの名前です。
基本的にはオブジェクトのコンテンツに入っているテクスチャのみ指定可能です。
「integer side」は前回のテクスチャアニメで出てきましたが、primのどの面に貼るかです。
ALL_SIDEを指定すると全面に貼られます。
ベンダーをどのようにbuildしたかによって、ここに指定すべき番号は異なってきますので、自分の作ったベンダーの画像用の面が何番なのか、確認しておいて下さい。
立方体の面については、前回の記事に番号を掲載してあります。

余談です。
引数「string texture」にUUIDを使うことで、コンテンツにないテクスチャを貼ることが可能です。
例えば、以下のUUIDを使うと、真っ白なテクスチャを貼ることができます。
 llSetTexture("5748decc-f629-461c-9a36-a35a221fe21f", ALL_SIDE);
あるいは100%透明なテクスチャを貼るには以下のUUIDが使えます。
 llSetTexture("f54a0c32-3cd1-d49a-5b4f-7b792bebc204", ALL_SIDE);

さて、これで必要なイベント、関数は把握できました。
ベンダーのスクリプトを作ることができるでしょうか?

タッチされたのは誰?


結論から言ってしまいますが、これだけではまだベンダーを作ることができません。
なぜかと言うと、商品を選択するための矢印は二つ、どちらをタッチしても同じタッチイベントが起こります。
一体どっちの矢印がタッチされたのかを判定しないと、正しく画像の変更ができませんよね。

リンクされたオブジェクトで、どの子プリムがタッチされたのかを判定する方法はいくつかあります。
最も簡単なのはllDetectedLinkNumber()関数を使う方法です。

llDetectedLinkNumber関数


 integer llDetectedLinkNumber(integer number)

この関数はタッチイベントの中で使います(特定のイベントの中でしか使えません)。
「何番目のprimがタッチされたのか」を調べることができます。
引数numberには通常0を指定します。

余談ではあるのですが、0以外の値を使う場合について大事なことなので説明しておきます。

タッチイベントには、実は3種類のイベントが存在します。
 touch_start(integer num_detected){}
 touch(integer num_detected){}
 touch_end(integer num_detected){}
touch_start?は「タッチし始めた時=マウス左ボタンを押し下げた時」に発生します。
touch_end?は「タッチを終えた時=マウス左ボタンを話した時」に発生します。
そしてtouch?は「タッチし続けている時=マウスボタンを押したままの時」に繰り返し発生します。
これらのイベントの引数num_detectedは、何人の人がタッチしているのかを示しています。
touch_start?及びtouch_end?は、瞬間的に発生するイベントですので、同時に複数の人がタッチ・イベントを引き起こすことは通常考えられません。
ですのでタッチした人は常に1人です。
一方、touch?イベントは継続的に発生するイベントですので、同時に何人かの人がオブジェクトにタッチし続けることが有り得ます。
そのようなときには引数num_detectedに2とか3などの数字が入ってくることになります。

llDetectedLinkNumber()関数、あるいは過去に使ったllDetectedKey()関数に指定する引数は、タッチしている人のうち、何番目の人の情報を得るかを指定する数字です。
例えば3人の人がtouch?イベントを発生させたとして、一人目のUUIDを調べるときにはllDetectedKey(0)、二人目はllDetectedKey(1)のようになります(最初の人を0番目として数えます)。
このスクリプト講座で使ってきたタッチイベントはtouch_startですから、タッチした人数は常に1です。
ですのでllDetectedKey()関数もllDetectedLinkNumber()関数も引数0の固定値で実行しています。
0以外は意味がないからです。

なお、touch?イベントはその性質上、サーバー負荷に成り得ます。
マウスをホールドしている間はイベントが発生し続けるわけですから(一秒間に数回のイベントが起こる模様)、どうしてもサーバーのCPUを喰う処理になってしまいます。
しかも、ユーザーがマウスを離してからも継続してイベントを発生させ続けるバグがあるそうで、そんなバグにぶち当たったら、まさにラグ発生スクリプトに他なりません。
「タメ攻撃」みたいな処理をしたいときには便利なイベントではありますが、同時に「サーバー攻撃」をしてしまう諸刃の剣だということを理解しておくべきでしょう。

そのような理由から、通常「タッチした時」のイベントとしてはtouch_start?を使用します。

……と、話が横道にそれました。
以上のような理由から、llDetectedLinkNumber()はtouch_start?イベントの中で使う限り、引数は0で構いません。
llDetectedLinkNumber()関数はタッチされたprimのリンクナンバーを返します。

リンクナンバーというのは、リンクされたオブジェクトの各primについている番号です。
ルートプリムを1番として、子プリムには2番以降の番号がついています。
参考までに、リンクされていないprimは0番です。

primのリンクについては詳しく説明しませんが、ルートprimは一番最後に選択したprimですよね。
リンクナンバーは、リンクの際にprimを選択した順番の逆の順番になっています。

例えば今回、ベンダーには3つのprimがありますが、
次の商品ボタン、前の商品ボタン、商品表示画面、の順番で選択してリンクしたとしたら、リンクナンバーは、
 1・・・商品表示画面(ルート)
 2・・・前の商品ボタン
 3・・・次の商品ボタン
になります。

従って、タッチイベントの中でllDetectedLinkNumber()関数を使用し、タッチされたのが2番のprimだったら前の商品を表示、3番のprimだったら次の商品を表示してやれば良いことになります。

画像切り替えスクリプト


さて。
商品を販売するためには他にもまだ考えなければいけない点がありますが、とりあえず、画像を切り替えて商品を選べるところまでを作ってみましょう。
単にクリックして画像を切り替えるだけですので、それほど悩まずにできるかと思います。

list commodity = [
  "GuiterImage",
  "PianoImage", 
  "TrumpetImage", 
  "ViolinImage" 
];

integer current_id = 0;
integer view_side = 1;

set_commodity(){
  llSetTexture(llList2String(commodity, current_id ), view_side);
}

default {
  state_entry(){
    set_commodity();
  }
  
  touch_start(integer detected){
    integer i = llDetectedLinkNumber(0);
    if (i == 2) { // back button
      current_id --;
      if (current_id < 0){
        current_id = llGetListLength(commodity) - 1;
      }
      set_commodity();
    }else if (i == 3) { // next button
      current_id ++;
      if (current_id >= llGetListLength(commodity) ) {
        current_id = 0;
      }
      set_commodity();
    }
  }
}

こんな感じでしょうか。
切り替えるテクスチャの名前を、list型の変数commodityに列挙しています。
これらのテクスチャはオブジェクトのコンテンツの中に入れておく必要があります。

integer型の変数current_idは「現在表示中の商品番号」を管理するために用意しました。
初期値は0番目ということで0です。

変数view_sideはテクスチャを表示する面の定義です。
私の作ったベンダーでは1の面になってますが、皆さんのベンダーでは違う面かもしれませんのでそれぞれ修正して下さい。

set_commodity()は商品の画像を表示するためのユーザー関数です。
llSetTexture?()関数を使って、llList2String(commodity, current_id)で示されるテクスチャをview_sideの面に貼っています。
llList2String()は前回少々触れましたが、リストから文字列を抜き出してくる関数です。
リストcommodityの中のcurrent_id番目のデータを文字列として取り出します。

ですので、current_idが0だった場合、リストcommodityの中の0番目、すなわち"GuiterImage"テクスチャが貼られます。
current_idが3なら、リストcommodityの中の3番目の"ViolinImage"テクスチャですね。

current_idの値は、タッチイベントの中でコントロールしています。
まずllDetectedLinkNumber()を使って、タッチされたprimのリンクナンバーを調べます。
リンクナンバー2の場合は「前の商品ボタン」なので、current_idを1減らします。
前に++という表現が出てきましたが、--というのは同様に1を引く計算です。
そしてset_commodity()関数を実行すれば、一つ前の商品が表示されますよね。

ですが、もしもcurrent_idが0のときに「前の商品ボタン」が押されると、current_idは-1になってしまいます。
その対策のためにif文でcurrent_idが0以下(マイナス)になったときの処理をしています。
もしもcurrent_idがマイナスになったら、一番最後の商品に戻るのが便利ですので、

 current_id = llGetListLength(commodity) - 1;

こんなふうに書きました。
llGetListLengthはリストに入っているデータの個数を調べる関数です。
上記のスクリプトですと、commodityリストの中には4つのテクスチャ名が定義されていますので、4という数字が返ってきます。
current_idは0番目から始まるようにしているため、そこから-1し、一番最後のIDである3という数字を算出しています。

タッチされたのが「次の商品ボタン」だった場合(=リンクナンバーが3だった場合)は、current_idを1増やします。
リストに定義された商品の数より大きくなってしまわないよう、

 if (current_id >= llGetListLength(commodity) ) 

このように判定をしています。
current_idがcommodityのデータ数(=4)以上の場合、ですね。
一番最後の商品からさらに次の商品を表示しようとしたら、今度は先頭に戻るように、ここではcurrent_idを0にします。

人によっては「次」「前」のボタンのリンクナンバーが違うかもしれません。
その場合はiの値を判定するif文を修正して下さい。

また、テクスチャのリストを適切なものに変更しないと実際の動きを確認できません。念のため。

今回のポイント


  • テクスチャの変更:

 llSetTexture(string texture, integer side);

  • タッチされたprimのリンクナンバーの取得:

 integer llDetectedLinkNumber(integer number);
※タッチイベントの内部でのみ

  • リスト内のデータ数の取得:

 llGetListLength(list src);

特に難しいところは無いかと思います。
今回のスクリプトを応用し、タッチイベントをタイマーイベントに変えると、一定時間でテクスチャが切り替わるスライドショー・スクリプトが作れますね。
余力のある方は挑戦してみて下さい。

さて、商品は選べるようになりましたが、ベンダーとして機能させるためにはL$を扱う部分を実装しなければなりません。
例えば、正しくない金額が支払われたときに返金する処理は必須でしょう。
また、商品を売る以上は確実に相手にアイテムが渡るようにしておくべきです。
うっかりベンダーの中に商品を入れ忘れ、お金だけ取ってスクリプトはエラーを吐いたりしたら、詐欺と言われても文句が言えません(^^;

次回はその辺りを踏まえ、ベンダーの作成を続けたいと思います。

  • 画像切り替えスクリプトの説明文の中に -- お世話になっております (2008-07-11 07:59:10)
  • すいません、勘違いしました -- お世話になっております (2008-07-11 08:00:53)
名前:
コメント: