2007年04月17日
お金を扱う(初級スクリプト第十一回)
ベンダー作成の続きです。
簡単なようでいて、意外に面倒なのがベンダーです(^^;
やはりお金を扱うスクリプトですから、間違いのないような仕組みにしておかなければいけません・・・。
前回はタッチして商品の画像を切り替えるスクリプトを作りました。
今回はお金を受け取って商品を渡す部分に取り組みます。
そこまで出来ればベンダーとしては最低限のものにはなるでしょう。

簡単なようでいて、意外に面倒なのがベンダーです(^^;
やはりお金を扱うスクリプトですから、間違いのないような仕組みにしておかなければいけません・・・。
前回はタッチして商品の画像を切り替えるスクリプトを作りました。
今回はお金を受け取って商品を渡す部分に取り組みます。
そこまで出来ればベンダーとしては最低限のものにはなるでしょう。

buyとpay
buildツールをいじっていて、オブジェクトをクリックしたときの動作に、「buy」と「pay」があるのを見て、
「どう違うんじゃい?」
と思った方もいらっしゃるのではないでしょうか。
ごく簡単に言ってしまうと、「buy」はスクリプトを使わずにアイテムを売る際に使います。
「pay」はスクリプトでお金を受け取る際に使います。
「buy」は、buildツール上で販売価格を設定し、コンテンツの中身を売ったり、コピーを売ったりします。
普通に商品を売るときにはこの方法が一番手軽で便利です。
一方「pay」はスクリプトで制御することが出来ますので、商品販売以外にも応用が利きます。
例えば、10L$払うと上空まで運んでくれる乗り物とか。
お金を払うと一定時間動く、遊園地のアトラクションなども出来ます。
カジノマシーンなどもそうですね。
今回はスクリプトによるベンダーを作っているわけですから、当然「pay」を使います。
buildしたベンダーをクリックしたときの動作は、「pay」にセットしておきましょう。
誰かが「pay」をしようとすると、まずその人のクライアント画面にpayダイアログが表示されます。
このダイアログには金額ボタンや金額の入力欄があります。
いくら支払うのかを確認・入力し、payを実行すると、オブジェクトの中のスクリプトが動き出します。
「payされた」というイベントが発生するわけです。
「pay」されたときに発生するイベントは、前回名前だけちょろっと書きましたが、moneyというイベントになります。
money(key id, integer amount){
// 処理
}
引数「key id」には支払った人のUUIDが入ってきます。
「integer amount」は支払われた金額です。
これで「誰が」「いくら」支払ったのかがわかりますので、正しい金額が支払われたかどうかをチェックし、支払った人にアイテムを渡すことができます。
なお、スクリプトにmoneyイベントが書かれていないと、「pay」は動作しません。
「pay」できるようにしたいときは、必ずmoneyイベントが必要になります。
難しい引数があるわけでもないので、簡単に出来てしまいそうに思えますが、まだまだこの先があります(^^;
金返せ!
「pay」は基本的にはスクリプト側で受け取った金額を判断し、商品を渡す処理を実装しなければなりません。
設定にもよりますが、「pay」による支払いは支払う側が自由に金額を入力することもできますので、何も考えずに実装すると、100L$の商品に対して、1000L$の支払いや10L$の支払いをすることができてしまう可能性があります。
きちんと金額の判定を行わなければ、トラブルになるのは目に見えています。
非常にシビアですので、お金のやり取り部分が正しく動作するよう、二重三重の対策を施しておくべきでしょう。
まず第一に、「pay」で表示される支払いダイアログをコントロールし、決まった金額しか支払えないようにしておきます。
通常payダイアログには、1L$、5L$、10L$、20L$のボタンと、自由に金額を入力できる欄があります。
このままですと、操作する人がいくらでも好きな金額を支払えます。
Tipjarなどの場合はそれでもいいのですが、今回は決まった商品の販売ですから、商品の値段のみを支払えるように制限をかけます。
それにはllSetPayPrice()という関数を使用します。
llSetPayPrice(integer price, list quick_pay_buttons);
integer price
金額入力欄に表示される額を設定します。
例えばここに100を設定しておくと、payダイアログの金額入力欄には100という数字が表示されます。
ですが、入力欄は操作する人が自由に変更できてしまいますので、初期値を設定するだけでは十分ではありません。
今回はこの入力欄を一切表示しないようにしてしまいましょう。
入力欄を消すには、ここにPAY_HIDEをいう値をセットします。
list quick_pay_buttons
金額ボタンを設定します。
デフォルトでは1L$、5L$、10L$、20L$の4つのボタンがあります。
これを例えば、10L$、100L$、1000L$にしたい場合は、
[10, 100, 1000, PAY_HIDE]
と指定します。
PAY_HIDEを指定するとそのボタンは表示されなくなります。
今回は商品の値段に固定しますので、ボタンを一つだけにします。
例えば50L$の商品であれば、
[10, PAY_HIDE, PAY_HIDE, PAY_HIDE]
のようにセットします。
このllSetPayPrice()を使うことで、商品の金額以外のお金は支払えなくなります。
これで対策は十分でしょうか?
次のような場合を考えてみましょう。
(1)100L$の商品を選択する
(2)payを選ぶと100L$のボタンが表示される
(3)実際に100L$を支払う前に次の商品(50L$)を表示する
(4)100L$支払いのボタンを押す
このように、お客さんが想定外の操作をする可能性があります(^^;
上記のように操作されてしまうと、表示している商品は50L$にも関わらず、100L$の支払いが出来てしまいます。
ですので、moneyイベントの中で、支払われた金額と表示している商品の金額を判定し、一致していない場合は返金するような仕組みを作っておかなければなりません。
例えば商品の値段がpriceという変数に入っているとして、
money(key id, integer amount){
if (amount == price) {
// 金額が一致するので商品を渡す処理
} else {
// 金額が一致しないので返金する
}
}
このような判定が必須です。
返金する際にはllGiveMoney()関数を使います。
この関数は指定した相手に指定した金額を支払う関数です。
llGiveMoney(key destination, integer amount)
key destination
支払う相手のUUIDを指定します。
今回は返金ですので、moneyイベントで受け取った支払者のUUIDをセットしてやればOKです。
integer amount
いくら支払うかです。
差額返金などを考えてもいいのですが、ややこしくなるので全部返してしまったほうが楽でしょう。
moneyイベントで受け取った、入金額をそのまま指定してやれば良いですね。
まだ考えるべきところはありますが、これでずいぶんと安全なスクリプトになるでしょう。
しかしながら、llGiveMoney()関数を使うときには、少々考えなければならないことがあります。
llGiveMoney()関数は実は、いきなり使うことのできない関数なのです。
パーミッション
パーミッションという考え方があります。
スクリプトの実行許可のことです。
lslでは様々な処理が可能ですが、全ての処理を無条件に行えるわけではありません。
例えば、さきほど出てきたllGiveMoney()について考えてみてください。
llGiveMoney()を使って、自分に対してL$を支払うスクリプトを書き、それを他人に渡したとします。
もしもllGiveMoney()が無条件に実行できてしまったら、他人からお金をふんだくるスクリプトが簡単に書けてしまうわけです。
アバターのアニメーションなどもそうです。
無条件に実行できるのなら、恥ずかしい格好をするアニメーションを作り、視界に入る人を誰彼かまわずアニメーションさせるようなスクリプトを組むことが可能です(^^;
そんな事態にならないよう、お金を扱う関数を始めとして、アバターのアニメーション、オブジェクトのリンクの変更、カメラ(視点)の制御、キー入力の判定などの処理については、使う前にパーミッション(許可)を取らなければなりません。
少々面倒な手続きになりますが、重要な処理ですのでしっかり押さえておきましょう。
まずはパーミッションを取得するための関数ですが、llRequestPermissions()といいます。
llRequestPermissions(key agent, integer perm)
key agent
誰に対して許可を取るかの指定です。
UUIDをセットします。
今回のllGiveMoney()の実行許可を取る際には、ここにはオブジェクトオーナーのUUIDしか使えません。
integer perm
何の許可を取るかです。
いくつか値がありますが、今回使う値はPERMISSION_DEBITという値です。
アニメーションの許可やリンク変更の許可を取る場合には別の値が用意されています(別の機会に説明します)。
このllRequestPermissions()関数を使うと、許可を求める人のクライアント画面にダイアログが表示されます。
アニメーションの許可確認のダイアログなど見たことがある人もいるでしょう。
このダイアログに対して「OK」を出さない限り、パーミッションは取得されません。
もしも予期せぬところで「支払い許可のダイアログ」などが出てきた場合は、安易にOKしてはいけません!
パーミッションさえ与えなければ、勝手に支払いが行われることはありませんので、何かダイアログが出てきた場合はよく注意してから操作しましょう。
なお、このパーミッションというのはスクリプトごとに個別のものです。
一つのスクリプトに対して許可を与えたからといって、他のスクリプトも許可されるわけではありません。
スクリプトを作る側からすると、いちいち毎回パーミッションを取らなければいけないのはわずらわしいことではありますが、全てはSLの健全な秩序維持のためですので、文句を言わずに実装しましょう(^^;
パーミッションが取れると(あるいは不許可の場合も)スクリプトではrun_time_permissionsイベントが発生します。
run_time_permissions(integer perm) {
// 必要なパーミッションが取れたかどうかの確認
}
integer perm
パーミッションのデータが入ってきます。
PERMISSION_DEBITパーミッション以外の値もまとまって入ってきますので、判定する際には以下のような小細工が必要です。
run_time_permissions(integer perm) {
if (perm & PERMISSION_DEBIT){
// PERMISSION_DEBITパーミッションが許可された
} else {
// PERMISSION_DEBITパーミッションが許可されなかった
}
}
この書き方はパーミッションを扱うときのお約束パターンです。
「perm & PERMISSION_DEBIT」と書いてやると、PERMISSION_DEBITパーミッションが許可されているかどうかが判定できます。
今回のベンダースクリプトでは、PERMISSION_DEBITパーミッションが取得できないと返金処理ができませんので、スクリプトの最初でパーミッション取得の処理を行い、run_time_permissions()で許可が取れたことを確認した後、実際のベンダー機能が使えるようにすべきでしょう。
パーミッションについては、少々わかりにくいかと思いますので、今回全てを理解できなくても構いません。
いずれアニメーションのスクリプト等でも再び出てくることになりますから、少しずつ理解を深めていくことにしましょう。
今回知っておいて欲しいことは、lslには勝手に実行できない命令がいくつかあり、実行する際にはパーミッションを取得しなければならないのだ、という概要のみです。
アイテムの存在確認
安全なベンダーにするために、最後にもう一つ工夫しましょう。
お金の受け取りについては必要な対策を考えましたが、今度はアイテムを渡す際の間違いを防ぐ方法です。
スクリプトからアイテムを渡す際には、llGiveInventoryList()関数を使えば出来ることをすでに学びました。
ですが、この関数が正しく動作するためには、オブジェクトのコンテンツの中にアイテムが入っていなければなりません。
うっかりアイテムを入れ忘れたままベンダーを設置し、お客さんがお金を払ったとき、スクリプトがエラー終了してしまったらオオゴトです(^^;
アイテムがきちんとコンテンツの中にあるかどうか確認し、もし無いようならお客さんにお金を返すような仕組みにしておくべきでしょう。
アイテムの存在確認のためには、llGetInventoryType()関数を使います。
integer llGetInventoryType(string name)
この関数は、本来は指定された名前のアイテムの種別(サウンドとか、アニメーションとか、スクリプトとか)を調べるための関数です。
ですが、もしも指定された名前のアイテムがコンテンツに見つからないときは、INVENTORY_NONEという値を返してくれます。
これを利用し、もしINVENTORY_NONE以外の値が返ってきたら、アイテムはちゃんとインベントリの中にあるということがわかります。
例えば、"Violin"というアイテムがコンテンツの中にあるかどうかを調べるには、
if (llGetInventoryType("Violin") != INVENTORY_NONE) {
// INVENTORY_NONE以外なので"Violin"はある
} else {
// "Violin"は無い
}
このようにします。
厳密に言うと、さらに、アイテムがトランスファー可能かどうかを調べるべきでしょう。
コンテンツの中にあっても、トランスファー不可能な状態だったら、アイテムを渡すことができません。
ですが、自分で作成したアイテムに関しては、トランスファーが不可になることは通常ありませんので、ここではトランスファー可能かどうかを調べる処理は省略します。
ベンダースクリプト
以上の安全対策を踏まえたスクリプトが以下です。
最初の頃に比べると、ずいぶんとスクリプトっぽくなってきました(^^;
まず、商品のデータの管理ですが、commodityというlist型の変数で行っています。
このリストには商品名、テクスチャ名、実際のオブジェクト名、価格の順番でデータを並べてあります。
データが4つで1商品ですね。
このようなデータの場合、特定の商品の情報を取り出すにはどのようにすればよいでしょうか。
例えば3番目の商品のデータを取り出す場合には、
llList2String(commodity,3 * 4) // 商品名
llList2String(commodity,3 * 4 + 1) // テクスチャ名
llList2String(commodity,3 * 4 + 2) // オブジェクト名
llList2Integer(commodity,3 * 4 + 3) // 価格
このようになります。
商品データは4つで1商品ですので、3番目の商品のデータはリストの3 * 4番目から始まっています。
それに+1すると3番目の商品のテクスチャ名ですし、+2ならオブジェクト名です。
+3した価格はinteger型なので、llList2Integer()関数で取り出します。
今回のスクリプトでは、商品の番号をcurrent_idという変数で管理していますので、
llList2String(commodity,current_id * 4 + n)
のような表現がたくさん出てきます。
リストの使い方としては基本形の一つです。
set_commodityユーザー関数には、llSetPayPrice()を使った支払額の設定処理を追加しています。
表示中の商品の金額のみがpayダイアログに表示されるようにしています。
defaultステートは、パーミッション取得のみを行うステートにしました。
パーミッションが取得できて初めて、activateステートに移ります。
こうすることで確実に返金処理が実行可能なスクリプトにしています。
activeステートは前回のスクリプトにmoneyイベントを追加したものです。
moneyイベントの中では、まず支払われた金額と商品の価格とが一緒かどうかを判定しています。
価格が異なる場合は
"You paid wrong amount. I repays to you " + (string)amount + "L$."
とメッセージを表示して、返金を行います。
「(string)amount」の部分はまだ説明していませんでした。
amountはinteger型の変数ですが、メッセージはstring型です。
integer型のものをメッセージに含ませる場合は、string型に変換してやらないと表示できません。
「(string)amount」というのは、「amountをstring型に変更する」という意味です。
これで、例えばamountが100だった場合は、
"You paid wrong amount. I repays to you 100L$."(金額が違うよ。100L$返すよ)
と表示されるようになります。
支払われた金額と商品の価格とが一致していた場合、今度はllGetInventoryType()関数を使ってアイテムの存在確認をします。
アイテムが無かった場合、
"I am sorry very much. This commodity is sold out now."(ごめんね、その商品は今売り切れなんだわ)
苦し紛れの言い訳メッセージを表示するようにしました。
そして受け取ったL$を返金しています。
アイテムがちゃんと存在していた場合は、llGiveInventoryList()関数を使ってアイテムを渡しています。
これでようやく、一通り動作するベンダーが完成しました。
今回のポイント
payされたときのイベント:
money(key id, integer amount){
// 処理
}
payダイアログの設定:
llSetPayPrice(integer price, list quick_pay_buttons);
L$を支払う関数:
llGiveMoney(key destination, integer amount)
パーミッションの取得:
llRequestPermissions(key agent, integer perm)
パーミッションが変わったときのイベント:
run_time_permissions(integer perm) {
if (perm & PERMISSION_DEBIT){
// PERMISSION_DEBITパーミッションが許可された
} else {
// PERMISSION_DEBITパーミッションが許可されなかった
}
}
この例は「支払いパーミッション」の確認時。
アイテムの存在確認:
if (llGetInventoryType("アイテム名") != INVENTORY_NONE) {
// INVENTORY_NONE以外なので"アイテム名"はある
} else {
// "アイテム名"は無い
}
n個ずつのデータが管理されているリスト型変数listにおいて、m番目のデータセットのl個目のデータを取得する方法:
llList2String(list, m * n + l)
この例はデータがstring型の場合。
初級スクリプトと言いつつ、中身は結構濃くなってきています(^^;
今回特に重要なのはパーミッションの考え方です。
また他のスクリプトでも出てきますので、パーミッションについては覚えておいて下さい。
さて、ベンダースクリプトは機能的には完成しましたが、まだ改良の余地があります。
例えば、商品データの管理をリストで行っている部分。
商品が増えるたびにここを修正しなければいけないのは少々手間かもしれません。
販売可能なアイテムが一度に一つずつなのも不便です。
箱詰めにして売るならばこのスクリプトでも良いのですが、何か工夫の余地はないものでしょうか。
次回はその辺りについて考えてみたいと思います。
buildツールをいじっていて、オブジェクトをクリックしたときの動作に、「buy」と「pay」があるのを見て、
「どう違うんじゃい?」
と思った方もいらっしゃるのではないでしょうか。
ごく簡単に言ってしまうと、「buy」はスクリプトを使わずにアイテムを売る際に使います。
「pay」はスクリプトでお金を受け取る際に使います。
「buy」は、buildツール上で販売価格を設定し、コンテンツの中身を売ったり、コピーを売ったりします。
普通に商品を売るときにはこの方法が一番手軽で便利です。
一方「pay」はスクリプトで制御することが出来ますので、商品販売以外にも応用が利きます。
例えば、10L$払うと上空まで運んでくれる乗り物とか。
お金を払うと一定時間動く、遊園地のアトラクションなども出来ます。
カジノマシーンなどもそうですね。
今回はスクリプトによるベンダーを作っているわけですから、当然「pay」を使います。
buildしたベンダーをクリックしたときの動作は、「pay」にセットしておきましょう。
誰かが「pay」をしようとすると、まずその人のクライアント画面にpayダイアログが表示されます。
このダイアログには金額ボタンや金額の入力欄があります。
いくら支払うのかを確認・入力し、payを実行すると、オブジェクトの中のスクリプトが動き出します。
「payされた」というイベントが発生するわけです。
「pay」されたときに発生するイベントは、前回名前だけちょろっと書きましたが、moneyというイベントになります。
money(key id, integer amount){
// 処理
}
引数「key id」には支払った人のUUIDが入ってきます。
「integer amount」は支払われた金額です。
これで「誰が」「いくら」支払ったのかがわかりますので、正しい金額が支払われたかどうかをチェックし、支払った人にアイテムを渡すことができます。
なお、スクリプトにmoneyイベントが書かれていないと、「pay」は動作しません。
「pay」できるようにしたいときは、必ずmoneyイベントが必要になります。
難しい引数があるわけでもないので、簡単に出来てしまいそうに思えますが、まだまだこの先があります(^^;
金返せ!
「pay」は基本的にはスクリプト側で受け取った金額を判断し、商品を渡す処理を実装しなければなりません。
設定にもよりますが、「pay」による支払いは支払う側が自由に金額を入力することもできますので、何も考えずに実装すると、100L$の商品に対して、1000L$の支払いや10L$の支払いをすることができてしまう可能性があります。
きちんと金額の判定を行わなければ、トラブルになるのは目に見えています。
非常にシビアですので、お金のやり取り部分が正しく動作するよう、二重三重の対策を施しておくべきでしょう。
まず第一に、「pay」で表示される支払いダイアログをコントロールし、決まった金額しか支払えないようにしておきます。
通常payダイアログには、1L$、5L$、10L$、20L$のボタンと、自由に金額を入力できる欄があります。
このままですと、操作する人がいくらでも好きな金額を支払えます。
Tipjarなどの場合はそれでもいいのですが、今回は決まった商品の販売ですから、商品の値段のみを支払えるように制限をかけます。
それにはllSetPayPrice()という関数を使用します。
llSetPayPrice(integer price, list quick_pay_buttons);
integer price
金額入力欄に表示される額を設定します。
例えばここに100を設定しておくと、payダイアログの金額入力欄には100という数字が表示されます。
ですが、入力欄は操作する人が自由に変更できてしまいますので、初期値を設定するだけでは十分ではありません。
今回はこの入力欄を一切表示しないようにしてしまいましょう。
入力欄を消すには、ここにPAY_HIDEをいう値をセットします。
list quick_pay_buttons
金額ボタンを設定します。
デフォルトでは1L$、5L$、10L$、20L$の4つのボタンがあります。
これを例えば、10L$、100L$、1000L$にしたい場合は、
[10, 100, 1000, PAY_HIDE]
と指定します。
PAY_HIDEを指定するとそのボタンは表示されなくなります。
今回は商品の値段に固定しますので、ボタンを一つだけにします。
例えば50L$の商品であれば、
[10, PAY_HIDE, PAY_HIDE, PAY_HIDE]
のようにセットします。
このllSetPayPrice()を使うことで、商品の金額以外のお金は支払えなくなります。
これで対策は十分でしょうか?
次のような場合を考えてみましょう。
(1)100L$の商品を選択する
(2)payを選ぶと100L$のボタンが表示される
(3)実際に100L$を支払う前に次の商品(50L$)を表示する
(4)100L$支払いのボタンを押す
このように、お客さんが想定外の操作をする可能性があります(^^;
上記のように操作されてしまうと、表示している商品は50L$にも関わらず、100L$の支払いが出来てしまいます。
ですので、moneyイベントの中で、支払われた金額と表示している商品の金額を判定し、一致していない場合は返金するような仕組みを作っておかなければなりません。
例えば商品の値段がpriceという変数に入っているとして、
money(key id, integer amount){
if (amount == price) {
// 金額が一致するので商品を渡す処理
} else {
// 金額が一致しないので返金する
}
}
このような判定が必須です。
返金する際にはllGiveMoney()関数を使います。
この関数は指定した相手に指定した金額を支払う関数です。
llGiveMoney(key destination, integer amount)
key destination
支払う相手のUUIDを指定します。
今回は返金ですので、moneyイベントで受け取った支払者のUUIDをセットしてやればOKです。
integer amount
いくら支払うかです。
差額返金などを考えてもいいのですが、ややこしくなるので全部返してしまったほうが楽でしょう。
moneyイベントで受け取った、入金額をそのまま指定してやれば良いですね。
まだ考えるべきところはありますが、これでずいぶんと安全なスクリプトになるでしょう。
しかしながら、llGiveMoney()関数を使うときには、少々考えなければならないことがあります。
llGiveMoney()関数は実は、いきなり使うことのできない関数なのです。
パーミッション
パーミッションという考え方があります。
スクリプトの実行許可のことです。
lslでは様々な処理が可能ですが、全ての処理を無条件に行えるわけではありません。
例えば、さきほど出てきたllGiveMoney()について考えてみてください。
llGiveMoney()を使って、自分に対してL$を支払うスクリプトを書き、それを他人に渡したとします。
もしもllGiveMoney()が無条件に実行できてしまったら、他人からお金をふんだくるスクリプトが簡単に書けてしまうわけです。
アバターのアニメーションなどもそうです。
無条件に実行できるのなら、恥ずかしい格好をするアニメーションを作り、視界に入る人を誰彼かまわずアニメーションさせるようなスクリプトを組むことが可能です(^^;
そんな事態にならないよう、お金を扱う関数を始めとして、アバターのアニメーション、オブジェクトのリンクの変更、カメラ(視点)の制御、キー入力の判定などの処理については、使う前にパーミッション(許可)を取らなければなりません。
少々面倒な手続きになりますが、重要な処理ですのでしっかり押さえておきましょう。
まずはパーミッションを取得するための関数ですが、llRequestPermissions()といいます。
llRequestPermissions(key agent, integer perm)
key agent
誰に対して許可を取るかの指定です。
UUIDをセットします。
今回のllGiveMoney()の実行許可を取る際には、ここにはオブジェクトオーナーのUUIDしか使えません。
integer perm
何の許可を取るかです。
いくつか値がありますが、今回使う値はPERMISSION_DEBITという値です。
アニメーションの許可やリンク変更の許可を取る場合には別の値が用意されています(別の機会に説明します)。
このllRequestPermissions()関数を使うと、許可を求める人のクライアント画面にダイアログが表示されます。
アニメーションの許可確認のダイアログなど見たことがある人もいるでしょう。
このダイアログに対して「OK」を出さない限り、パーミッションは取得されません。
もしも予期せぬところで「支払い許可のダイアログ」などが出てきた場合は、安易にOKしてはいけません!
パーミッションさえ与えなければ、勝手に支払いが行われることはありませんので、何かダイアログが出てきた場合はよく注意してから操作しましょう。
なお、このパーミッションというのはスクリプトごとに個別のものです。
一つのスクリプトに対して許可を与えたからといって、他のスクリプトも許可されるわけではありません。
スクリプトを作る側からすると、いちいち毎回パーミッションを取らなければいけないのはわずらわしいことではありますが、全てはSLの健全な秩序維持のためですので、文句を言わずに実装しましょう(^^;
パーミッションが取れると(あるいは不許可の場合も)スクリプトではrun_time_permissionsイベントが発生します。
run_time_permissions(integer perm) {
// 必要なパーミッションが取れたかどうかの確認
}
integer perm
パーミッションのデータが入ってきます。
PERMISSION_DEBITパーミッション以外の値もまとまって入ってきますので、判定する際には以下のような小細工が必要です。
run_time_permissions(integer perm) {
if (perm & PERMISSION_DEBIT){
// PERMISSION_DEBITパーミッションが許可された
} else {
// PERMISSION_DEBITパーミッションが許可されなかった
}
}
この書き方はパーミッションを扱うときのお約束パターンです。
「perm & PERMISSION_DEBIT」と書いてやると、PERMISSION_DEBITパーミッションが許可されているかどうかが判定できます。
今回のベンダースクリプトでは、PERMISSION_DEBITパーミッションが取得できないと返金処理ができませんので、スクリプトの最初でパーミッション取得の処理を行い、run_time_permissions()で許可が取れたことを確認した後、実際のベンダー機能が使えるようにすべきでしょう。
パーミッションについては、少々わかりにくいかと思いますので、今回全てを理解できなくても構いません。
いずれアニメーションのスクリプト等でも再び出てくることになりますから、少しずつ理解を深めていくことにしましょう。
今回知っておいて欲しいことは、lslには勝手に実行できない命令がいくつかあり、実行する際にはパーミッションを取得しなければならないのだ、という概要のみです。
アイテムの存在確認
安全なベンダーにするために、最後にもう一つ工夫しましょう。
お金の受け取りについては必要な対策を考えましたが、今度はアイテムを渡す際の間違いを防ぐ方法です。
スクリプトからアイテムを渡す際には、llGiveInventoryList()関数を使えば出来ることをすでに学びました。
ですが、この関数が正しく動作するためには、オブジェクトのコンテンツの中にアイテムが入っていなければなりません。
うっかりアイテムを入れ忘れたままベンダーを設置し、お客さんがお金を払ったとき、スクリプトがエラー終了してしまったらオオゴトです(^^;
アイテムがきちんとコンテンツの中にあるかどうか確認し、もし無いようならお客さんにお金を返すような仕組みにしておくべきでしょう。
アイテムの存在確認のためには、llGetInventoryType()関数を使います。
integer llGetInventoryType(string name)
この関数は、本来は指定された名前のアイテムの種別(サウンドとか、アニメーションとか、スクリプトとか)を調べるための関数です。
ですが、もしも指定された名前のアイテムがコンテンツに見つからないときは、INVENTORY_NONEという値を返してくれます。
これを利用し、もしINVENTORY_NONE以外の値が返ってきたら、アイテムはちゃんとインベントリの中にあるということがわかります。
例えば、"Violin"というアイテムがコンテンツの中にあるかどうかを調べるには、
if (llGetInventoryType("Violin") != INVENTORY_NONE) {
// INVENTORY_NONE以外なので"Violin"はある
} else {
// "Violin"は無い
}
このようにします。
厳密に言うと、さらに、アイテムがトランスファー可能かどうかを調べるべきでしょう。
コンテンツの中にあっても、トランスファー不可能な状態だったら、アイテムを渡すことができません。
ですが、自分で作成したアイテムに関しては、トランスファーが不可になることは通常ありませんので、ここではトランスファー可能かどうかを調べる処理は省略します。
ベンダースクリプト
以上の安全対策を踏まえたスクリプトが以下です。
list commodity = [
// name, texture, object, price
"Guiter", "GuiterImage", "GuiterBox", 500,
"Piano", "PianoImage", "PianoBox", 600,
"Trumpet", "TrumpetImage", "TrumpetBox", 500,
"Violin", "ViolinImage", "ViolinBox", 550
];
integer current_id = 0;
integer view_side = 1;
set_commodity(){
llSetTexture(llList2String(commodity, current_id * 4 + 1), view_side);
llSetPayPrice(PAY_HIDE, [llList2Integer(commodity, current_id * 4 +3), PAY_HIDE, PAY_HIDE, PAY_HIDE]);
}
default {
state_entry(){
llRequestPermissions(llGetOwner(), PERMISSION_DEBIT);
}
run_time_permissions(integer perm) {
if (perm & PERMISSION_DEBIT){
state active;
} else {
llRequestPermissions(llGetOwner(), PERMISSION_DEBIT);
}
}
}
state active {
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) / 4 - 1;
}
set_commodity();
}else if (i == 3) { // next button
current_id ++;
if (current_id >= llGetListLength(commodity) / 4) {
current_id = 0;
}
set_commodity();
}
}
money(key id, integer amount){
integer p = llList2Integer(commodity, current_id * 4 +3);
if (amount == p){ // payの金額と商品の価格が一致
if (llGetInventoryType( llList2String(commodity, current_id * 4 +2)) != INVENTORY_NONE) { // アイテム存在確認
llSay(0, "Thank you for purchasing! Please wait until getting the commodity...");
llGiveInventoryList(id, llList2String(commodity, current_id * 4), [llList2String(commodity, current_id * 4 +2)]);
}else{
llSay(0, "I am sorry very much. This commodity is sold out now.");
llGiveMoney(id, amount);
}
}else{
llSay(0, "You paid wrong amount. I repays to you " + (string)amount + "L$.");
llGiveMoney(id, amount);
}
}
}
最初の頃に比べると、ずいぶんとスクリプトっぽくなってきました(^^;
まず、商品のデータの管理ですが、commodityというlist型の変数で行っています。
このリストには商品名、テクスチャ名、実際のオブジェクト名、価格の順番でデータを並べてあります。
データが4つで1商品ですね。
このようなデータの場合、特定の商品の情報を取り出すにはどのようにすればよいでしょうか。
例えば3番目の商品のデータを取り出す場合には、
llList2String(commodity,3 * 4) // 商品名
llList2String(commodity,3 * 4 + 1) // テクスチャ名
llList2String(commodity,3 * 4 + 2) // オブジェクト名
llList2Integer(commodity,3 * 4 + 3) // 価格
このようになります。
商品データは4つで1商品ですので、3番目の商品のデータはリストの3 * 4番目から始まっています。
それに+1すると3番目の商品のテクスチャ名ですし、+2ならオブジェクト名です。
+3した価格はinteger型なので、llList2Integer()関数で取り出します。
今回のスクリプトでは、商品の番号をcurrent_idという変数で管理していますので、
llList2String(commodity,current_id * 4 + n)
のような表現がたくさん出てきます。
リストの使い方としては基本形の一つです。
set_commodityユーザー関数には、llSetPayPrice()を使った支払額の設定処理を追加しています。
表示中の商品の金額のみがpayダイアログに表示されるようにしています。
defaultステートは、パーミッション取得のみを行うステートにしました。
パーミッションが取得できて初めて、activateステートに移ります。
こうすることで確実に返金処理が実行可能なスクリプトにしています。
activeステートは前回のスクリプトにmoneyイベントを追加したものです。
moneyイベントの中では、まず支払われた金額と商品の価格とが一緒かどうかを判定しています。
価格が異なる場合は
"You paid wrong amount. I repays to you " + (string)amount + "L$."
とメッセージを表示して、返金を行います。
「(string)amount」の部分はまだ説明していませんでした。
amountはinteger型の変数ですが、メッセージはstring型です。
integer型のものをメッセージに含ませる場合は、string型に変換してやらないと表示できません。
「(string)amount」というのは、「amountをstring型に変更する」という意味です。
これで、例えばamountが100だった場合は、
"You paid wrong amount. I repays to you 100L$."(金額が違うよ。100L$返すよ)
と表示されるようになります。
支払われた金額と商品の価格とが一致していた場合、今度はllGetInventoryType()関数を使ってアイテムの存在確認をします。
アイテムが無かった場合、
"I am sorry very much. This commodity is sold out now."(ごめんね、その商品は今売り切れなんだわ)
苦し紛れの言い訳メッセージを表示するようにしました。
そして受け取ったL$を返金しています。
アイテムがちゃんと存在していた場合は、llGiveInventoryList()関数を使ってアイテムを渡しています。
これでようやく、一通り動作するベンダーが完成しました。
今回のポイント
payされたときのイベント:
money(key id, integer amount){
// 処理
}
payダイアログの設定:
llSetPayPrice(integer price, list quick_pay_buttons);
L$を支払う関数:
llGiveMoney(key destination, integer amount)
パーミッションの取得:
llRequestPermissions(key agent, integer perm)
パーミッションが変わったときのイベント:
run_time_permissions(integer perm) {
if (perm & PERMISSION_DEBIT){
// PERMISSION_DEBITパーミッションが許可された
} else {
// PERMISSION_DEBITパーミッションが許可されなかった
}
}
この例は「支払いパーミッション」の確認時。
アイテムの存在確認:
if (llGetInventoryType("アイテム名") != INVENTORY_NONE) {
// INVENTORY_NONE以外なので"アイテム名"はある
} else {
// "アイテム名"は無い
}
n個ずつのデータが管理されているリスト型変数listにおいて、m番目のデータセットのl個目のデータを取得する方法:
llList2String(list, m * n + l)
この例はデータがstring型の場合。
初級スクリプトと言いつつ、中身は結構濃くなってきています(^^;
今回特に重要なのはパーミッションの考え方です。
また他のスクリプトでも出てきますので、パーミッションについては覚えておいて下さい。
さて、ベンダースクリプトは機能的には完成しましたが、まだ改良の余地があります。
例えば、商品データの管理をリストで行っている部分。
商品が増えるたびにここを修正しなければいけないのは少々手間かもしれません。
販売可能なアイテムが一度に一つずつなのも不便です。
箱詰めにして売るならばこのスクリプトでも良いのですが、何か工夫の余地はないものでしょうか。
次回はその辺りについて考えてみたいと思います。
衝突判定(スクリプト初級第二十三回)
カメラ制御(スクリプト初級第二十二回)
デモ商品を作ろう(スクリプト初級第二十一回)
センサーを使おう(スクリプト初級第二十回)
ステートのこと(スクリプト初級第十九回)
rez!(スクリプト初級第十八回)
カメラ制御(スクリプト初級第二十二回)
デモ商品を作ろう(スクリプト初級第二十一回)
センサーを使おう(スクリプト初級第二十回)
ステートのこと(スクリプト初級第十九回)
rez!(スクリプト初級第十八回)
Posted by Miz at 12:15│Comments(16)
│初級スクリプト
この記事へのコメント
大変参考になりました。
最初、前後に切り替えるボタンを押しても、支払ダイアログが出てしまい、戸惑いましたが、「リンクされたパートを編集」で「左クリックだった場合」を、一旦、他のものにしてから、デフォルトに戻すと、無事、支払ダイアログが出ず、商品が切り替わるようになりました。
同じようなところでつまずいた方は、ご参考までに。
最初、前後に切り替えるボタンを押しても、支払ダイアログが出てしまい、戸惑いましたが、「リンクされたパートを編集」で「左クリックだった場合」を、一旦、他のものにしてから、デフォルトに戻すと、無事、支払ダイアログが出ず、商品が切り替わるようになりました。
同じようなところでつまずいた方は、ご参考までに。
Posted by Munemitsu Nishi at 2007年06月21日 01:53
はじめまして!
質問があります。
ひとつのプリムの中で touchイベント と moneyイベント を使うことはできないのでしょうか?
llDialog で商品を選んでもらってから、代金を支払うスクリプトを作りたいのですが・・・
できるようでしたら、使い方を教えてください。
お時間が有りましたら、お答えをよろしくお願いいたします。
どうぞ、よろしく~!(^^;
質問があります。
ひとつのプリムの中で touchイベント と moneyイベント を使うことはできないのでしょうか?
llDialog で商品を選んでもらってから、代金を支払うスクリプトを作りたいのですが・・・
できるようでしたら、使い方を教えてください。
お時間が有りましたら、お答えをよろしくお願いいたします。
どうぞ、よろしく~!(^^;
Posted by immortal Nishi at 2007年07月19日 20:56
>immortal Nishiさん
もちろんtouchイベントとmoneyイベント両方とも使うことができます。
touchイベントはオブジェクトを「touch」したとき、moneyイベントは「pay」したときに発生します。
ただし、オブジェクトをクリックしたときのデフォルト動作は「touch」か「pay」かどちらかしか設定できませんので、片方はパイメニューから選んでもらう形になってしまいます。
たいていのベンダーでは、デフォルト動作はタッチで、商品選択後にパイメニューから「pay」を選ぶようになっているかと思います。
自動的に支払いダイアログを表示することは残念ながら出来ません。
お客さんにパイメニューを使わせるのはわかりにくくなる可能性がありますので、この辺り工夫すると面白いものができるかもしれません。
例えば、ベンダーのデフォルト動作を「pay」にし、ベンダー前に透明でファントムな子primを設置しておいて、そこに「衝突」したらダイアログを出す等です。
そうするとベンダーの前に立つと自動的にダイアログが開き、商品を選択後、ベンダーをクリックで支払いダイアログが出ます。
そんなイメージでしょうか?
もちろんtouchイベントとmoneyイベント両方とも使うことができます。
touchイベントはオブジェクトを「touch」したとき、moneyイベントは「pay」したときに発生します。
ただし、オブジェクトをクリックしたときのデフォルト動作は「touch」か「pay」かどちらかしか設定できませんので、片方はパイメニューから選んでもらう形になってしまいます。
たいていのベンダーでは、デフォルト動作はタッチで、商品選択後にパイメニューから「pay」を選ぶようになっているかと思います。
自動的に支払いダイアログを表示することは残念ながら出来ません。
お客さんにパイメニューを使わせるのはわかりにくくなる可能性がありますので、この辺り工夫すると面白いものができるかもしれません。
例えば、ベンダーのデフォルト動作を「pay」にし、ベンダー前に透明でファントムな子primを設置しておいて、そこに「衝突」したらダイアログを出す等です。
そうするとベンダーの前に立つと自動的にダイアログが開き、商品を選択後、ベンダーをクリックで支払いダイアログが出ます。
そんなイメージでしょうか?
Posted by Miz at 2007年07月19日 22:26
Mizさん、どうもありがとうございました!
やっぱりスクリプトから支払いダイアログを出すのは無理なんですね(T_T)
2プリムも視野に入れて、また工夫してみます。
これからもお世話になるかもしれませんので、どうぞよろしくです!
ありがとうございましたm(_'_)m
やっぱりスクリプトから支払いダイアログを出すのは無理なんですね(T_T)
2プリムも視野に入れて、また工夫してみます。
これからもお世話になるかもしれませんので、どうぞよろしくです!
ありがとうございましたm(_'_)m
Posted by immortal Nishi at 2007年07月20日 12:34
はじめましてカバチタレと言います
いつも参考にさせていただいてます
ところで...
質問なんですが
チップジャーを作ったんですが
チップを受け取るスクリプトはできたんですが
これまでの累計を表示するスクリプトが判らなくて困ってます
できれば、累計はアイテムの上に表示して
払ったくれた人の情報を、チャット入力により表示できればいいなと...
すごくめんどうなことをお聞きして申し訳ありません
お暇なときで結構ですのでご教授願います...(*_ _)人
いつも参考にさせていただいてます
ところで...
質問なんですが
チップジャーを作ったんですが
チップを受け取るスクリプトはできたんですが
これまでの累計を表示するスクリプトが判らなくて困ってます
できれば、累計はアイテムの上に表示して
払ったくれた人の情報を、チャット入力により表示できればいいなと...
すごくめんどうなことをお聞きして申し訳ありません
お暇なときで結構ですのでご教授願います...(*_ _)人
Posted by カバチタレ at 2007年08月23日 02:58
>カバチタレさん
受け取ったチップと払ってくれた人を管理するための変数が必要になりますね。
チップの累計額は、お金を受け取るたびに加算していけば済むので難しくないと思いますが、払ってくれた人のほうはリスト型の変数などを利用するのでちょっと小細工がいります。
まずは累計値の表示を作ってみてはいかがでしょうか。
アイテムの上に表示するには、「看板を作ろう」の回でフローティングテキストの使い方を説明していますので参考にしてみて下さい。
受け取ったチップと払ってくれた人を管理するための変数が必要になりますね。
チップの累計額は、お金を受け取るたびに加算していけば済むので難しくないと思いますが、払ってくれた人のほうはリスト型の変数などを利用するのでちょっと小細工がいります。
まずは累計値の表示を作ってみてはいかがでしょうか。
アイテムの上に表示するには、「看板を作ろう」の回でフローティングテキストの使い方を説明していますので参考にしてみて下さい。
Posted by Miz
at 2007年08月23日 18:40

カバチタレです
ごめんなさい
まだスクリプトの知識が浅くて...
累計というのはどうやってつくればいいのか...
lslを片っ端からググッてみたのですが、わからず_| ̄|○
フローティングテキストはわかります~
まず、累計の方法を教えてくださいw
お願いしますw
ごめんなさい
まだスクリプトの知識が浅くて...
累計というのはどうやってつくればいいのか...
lslを片っ端からググッてみたのですが、わからず_| ̄|○
フローティングテキストはわかります~
まず、累計の方法を教えてくださいw
お願いしますw
Posted by Kabachi Masala at 2007年08月23日 19:13
>Kabachi Masalaさん
ここで言う累計とは、受け取ったお金の合計のことです。
例えば、10L$、15L$、8L$、11L$と受け取った場合であれば、
10+15+8+11 で 累計44L$になりますよね。
この累計値を管理するための変数を用意します。
お金は整数ですから、integer型でいいでしょう。
変数の名前はわかりやすくするために、例えば、
integer ruikei= 0;
とします。
お金を受け取ったらこの変数ruikeiを増やしていけばいいのですから、moneyイベント(お金を受け取ったときに起こるイベント)で、ruikeiを加算する計算を書きます。
変数を加算していく方法は、「基礎知識」カテゴリの「演算子」に書いてあります。
money(key id, integer amount){
ruikei += amount;
}
最初に挙げた例、10L$、15L$、8L$、11L$と受け取った場合であれば、まず10L$受け取った時点で、変数ruikeiは10、次に15L$を受け取ったときに、10+15で25、その次の8L$を受け取ると、25+8で33、最後の11L$で33+11なので44、になります。
この変数ruikeiをフローティングテキストで表示するようにしてやれば、累計値が常に表示されることになります。
ここで言う累計とは、受け取ったお金の合計のことです。
例えば、10L$、15L$、8L$、11L$と受け取った場合であれば、
10+15+8+11 で 累計44L$になりますよね。
この累計値を管理するための変数を用意します。
お金は整数ですから、integer型でいいでしょう。
変数の名前はわかりやすくするために、例えば、
integer ruikei= 0;
とします。
お金を受け取ったらこの変数ruikeiを増やしていけばいいのですから、moneyイベント(お金を受け取ったときに起こるイベント)で、ruikeiを加算する計算を書きます。
変数を加算していく方法は、「基礎知識」カテゴリの「演算子」に書いてあります。
money(key id, integer amount){
ruikei += amount;
}
最初に挙げた例、10L$、15L$、8L$、11L$と受け取った場合であれば、まず10L$受け取った時点で、変数ruikeiは10、次に15L$を受け取ったときに、10+15で25、その次の8L$を受け取ると、25+8で33、最後の11L$で33+11なので44、になります。
この変数ruikeiをフローティングテキストで表示するようにしてやれば、累計値が常に表示されることになります。
Posted by Miz
at 2007年08月24日 10:20

はじめまして!
スクリプト未経験だったんですがこちらのサイトを参考にLSLに取り組んでます!
無料配布のアイテムの配布数をカウントするようなスクリプトを思案してまして、
llGiveInventoryで受け取ってくれたら、カウントしてる変数に1を足す・・
でどうかなとおもって試してるのですが、受け取ったか破棄されたか判断するにはどのような関数(?)で対応すればいいのでしょうかぁ・・??
いきなり質問でごめんなさい^^;
スクリプト未経験だったんですがこちらのサイトを参考にLSLに取り組んでます!
無料配布のアイテムの配布数をカウントするようなスクリプトを思案してまして、
llGiveInventoryで受け取ってくれたら、カウントしてる変数に1を足す・・
でどうかなとおもって試してるのですが、受け取ったか破棄されたか判断するにはどのような関数(?)で対応すればいいのでしょうかぁ・・??
いきなり質問でごめんなさい^^;
Posted by rocklittle at 2007年10月25日 21:39
>rocklittleさん
こんにちは(^^
残念ながら受け取ったか破棄されたかを判断する関数はありません。
配布数ということであれば、llGiveInventoryを実行した数にするしかないですねぇ。
通常、タッチしてアイテムを受け取るような仕組みの場合、もらう側は当然ながらもらう意志があってタッチするのですから、破棄するのはイレギュラーな状況だと思います。
ですのでllGiveInventoryを実行した数=配布数とみなしても大きなズレはないと考えられますが、どうでしょうか。
無駄に配布数を増やすようなイタズラ(何度もタッチしてアイテムを破棄するとか)が心配でしたら、直前にタッチしたアバターのUUIDを記憶しておき、連続してタッチしても反応しないとか、同一アバターは1分以内に1度しかもらえないとか、そういう制限を設けることも可能かと思います。
こんにちは(^^
残念ながら受け取ったか破棄されたかを判断する関数はありません。
配布数ということであれば、llGiveInventoryを実行した数にするしかないですねぇ。
通常、タッチしてアイテムを受け取るような仕組みの場合、もらう側は当然ながらもらう意志があってタッチするのですから、破棄するのはイレギュラーな状況だと思います。
ですのでllGiveInventoryを実行した数=配布数とみなしても大きなズレはないと考えられますが、どうでしょうか。
無駄に配布数を増やすようなイタズラ(何度もタッチしてアイテムを破棄するとか)が心配でしたら、直前にタッチしたアバターのUUIDを記憶しておき、連続してタッチしても反応しないとか、同一アバターは1分以内に1度しかもらえないとか、そういう制限を設けることも可能かと思います。
Posted by Miz at 2007年10月26日 15:30
こんにちわ
こちらを参考にベンダーを作ってみました
ところで、このベンダーでアイテムが売れても
リンデンの履歴では、商品名は表示されず
すべてベンダー名で表示されるのですが・・・
商品名を表示させるにはどうしたらいいでしょうか?
こちらを参考にベンダーを作ってみました
ところで、このベンダーでアイテムが売れても
リンデンの履歴では、商品名は表示されず
すべてベンダー名で表示されるのですが・・・
商品名を表示させるにはどうしたらいいでしょうか?
Posted by Kabachi Masala at 2007年11月12日 18:36
>Kabachi Masalaさん
一番手っ取り早いのは、商品の選択がなされたときに(=テクスチャ切り替えたタイミングで)llSetObjectNameを使ってベンダーの名前を商品名に変えてしまうことですね。
この記事のコードですと、ユーザー関数set_commodityの中で、
set_commodity(){
llSetObjectName(llList2String(commodity, current_id * 4));
:
:
(以下略)
}
上記のように追加すればベンダー名が商品名になるので、トランザクションの履歴には商品名で載るはずです。
一番手っ取り早いのは、商品の選択がなされたときに(=テクスチャ切り替えたタイミングで)llSetObjectNameを使ってベンダーの名前を商品名に変えてしまうことですね。
この記事のコードですと、ユーザー関数set_commodityの中で、
set_commodity(){
llSetObjectName(llList2String(commodity, current_id * 4));
:
:
(以下略)
}
上記のように追加すればベンダー名が商品名になるので、トランザクションの履歴には商品名で載るはずです。
Posted by Miz at 2007年11月13日 10:50
ありがとうございます
無事できましたw
なんか ベンダーの不具合が出ているようですが・・・
リンデンがんばれ!ですねw
http://blog.secondlife.com/2007/11/12/a-word-of-warning/
無事できましたw
なんか ベンダーの不具合が出ているようですが・・・
リンデンがんばれ!ですねw
http://blog.secondlife.com/2007/11/12/a-word-of-warning/
Posted by Kabachi Masala at 2007年11月13日 12:55
activeのon_rezにリセットが必要です
Posted by TOSHI at 2008年06月10日 18:40
buildツールをいじっていて、オブジェクトをクリックしたときの動作に、「buy」と「pay」があるのを見て、
「どう違うんじゃい?」
と思った方もいらっしゃるのではないでしょうか。
「どう違うんじゃい?」
と思った方もいらっしゃるのではないでしょうか。
Posted by imitation jewelry at 2011年01月18日 16:01
よく記事を書かれ、私はこれを本当に感謝して
Posted by wedding dresses at 2011年09月17日 17:16