はじめに
prim間通信をもう少し練習するという意味で、今回はHUDを作ってみましょう。
HUDというのはヘッドアップディスプレイ (Head-Up Display)のことで、SLでは画面上に表示されるインターフェースのことを言います。
ミニマップなども広義の意味ではHUDになるのでしょうが、SLでは特にユーザーが独自に作ったインターフェースのことを指します。
日本人にとって馴染みのあるものは翻訳機などでしょうか。
チャットの自動英語翻訳を行ってくれるJ2Eは画面左中央に表示される半円型のHUDですね。
HUDというのはヘッドアップディスプレイ (Head-Up Display)のことで、SLでは画面上に表示されるインターフェースのことを言います。
ミニマップなども広義の意味ではHUDになるのでしょうが、SLでは特にユーザーが独自に作ったインターフェースのことを指します。
日本人にとって馴染みのあるものは翻訳機などでしょうか。
チャットの自動英語翻訳を行ってくれるJ2Eは画面左中央に表示される半円型のHUDですね。
何のHUDを作ってもいいのですが、あんまり出回ってなくて、それでいて便利そうなものが良いですね・・・。
テレポート用HUDなどはどうでしょうか。
HUDのボタンにテレポート先を登録しておいて、ボタンを押すとマップが開き、すぐにテレポートができるようなHUDです。
テレポート用HUDなどはどうでしょうか。
HUDのボタンにテレポート先を登録しておいて、ボタンを押すとマップが開き、すぐにテレポートができるようなHUDです。
HUDのbuild
HUDもオブジェクトです。
従ってまずはbuildしなければなりません。
凝った形のHUDを作るにはそれなりに工夫が必要ですが、ここはスクリプトの勉強ですので、簡単な形から試してみることにしましょう。
以下のようなオブジェクトを作ってみてください。
もちろんbuildに慣れている人は自由な形でも構いません。
従ってまずはbuildしなければなりません。
凝った形のHUDを作るにはそれなりに工夫が必要ですが、ここはスクリプトの勉強ですので、簡単な形から試してみることにしましょう。
以下のようなオブジェクトを作ってみてください。
もちろんbuildに慣れている人は自由な形でも構いません。
#ref error :画像を取得できませんでした。しばらく時間を置いてから再度お試しください。
ボタンの数はいくつでもいいですが、それぞれにテレポート先を登録しますので、複数あったほうが便利でしょう。
buildができたら、これをHUDとしてアタッチしてみます。
オブジェクトを一度インベントリにしまい、インベントリ内で右クリックすると「HUDとして添付」のような項目があるかと思います。
慣れない人は「HUDとして添付」する際に「Center(中央)」に添付するとわかりやすいかと思います。
右端や左端にすると、オブジェクトが画面外に装着されてしまい、どこにいったのかわからなくなる場合があります。
オブジェクトを一度インベントリにしまい、インベントリ内で右クリックすると「HUDとして添付」のような項目があるかと思います。
慣れない人は「HUDとして添付」する際に「Center(中央)」に添付するとわかりやすいかと思います。
右端や左端にすると、オブジェクトが画面外に装着されてしまい、どこにいったのかわからなくなる場合があります。
画面上に貼り付けることができたら、貼り付けたHUDを右クリックし、「編集」を選びます。
するとbuildツールが開き、位置や回転の設定ができますので、お好みの位置に調整します。
このときマウスのホイールを回すとHUDのスクリーン全体を拡大・縮小できます。
うっかり画面外にHUDが張り付いてしまったときなどはスクリーン全体を縮小すれば画面外のHUDを確認できます。
するとbuildツールが開き、位置や回転の設定ができますので、お好みの位置に調整します。
このときマウスのホイールを回すとHUDのスクリーン全体を拡大・縮小できます。
うっかり画面外にHUDが張り付いてしまったときなどはスクリーン全体を縮小すれば画面外のHUDを確認できます。
ここまではbuildの知識ですね。
ボタンのスクリプト
ではスクリプトに入っていきましょう。
HUDでは各ボタンにスクリプトを仕込むのが一般的です。
以前ベンダーを作ったときのllDetectedLinkNumber()関数を使うとスクリプトを一つにまとめることもできますが、その場合は各ボタンのリンクナンバーを把握しておく必要がでてきます。
llDetectedLinkNumber()関数は「タッチされたprimのリンクナンバーを返す」関数だからです。
ボタンが一つや二つしかなくて、今後拡張もされないというのであればそれでも構わないのですが、今回のようなHUDではあとからボタンを増やしてテレポート先をもっと多く記憶できるようにしたくなるかもしれません。
なるべく拡張性の高いスクリプトにしておいたほうがあとあと楽になります。
HUDでは各ボタンにスクリプトを仕込むのが一般的です。
以前ベンダーを作ったときのllDetectedLinkNumber()関数を使うとスクリプトを一つにまとめることもできますが、その場合は各ボタンのリンクナンバーを把握しておく必要がでてきます。
llDetectedLinkNumber()関数は「タッチされたprimのリンクナンバーを返す」関数だからです。
ボタンが一つや二つしかなくて、今後拡張もされないというのであればそれでも構わないのですが、今回のようなHUDではあとからボタンを増やしてテレポート先をもっと多く記憶できるようにしたくなるかもしれません。
なるべく拡張性の高いスクリプトにしておいたほうがあとあと楽になります。
ボタンの機能は二つです。
タッチされたとき、記憶していたテレポート先をルートプリムに送信するのが一つ。
それからテレポート先を記憶できるような仕組みがあったほうがいいでしょう。
どんな方法でもいいのですが、前回やった「一定時間タッチしつづけたとき」にテレポート先を記録できるようにしようと思います。
「この場所を記録しておきたい」と思ったときにはHUDのボタンをしばらく押しておけばよいだけになりますので、非常に便利なはずです。
タッチされたとき、記憶していたテレポート先をルートプリムに送信するのが一つ。
それからテレポート先を記憶できるような仕組みがあったほうがいいでしょう。
どんな方法でもいいのですが、前回やった「一定時間タッチしつづけたとき」にテレポート先を記録できるようにしようと思います。
「この場所を記録しておきたい」と思ったときにはHUDのボタンをしばらく押しておけばよいだけになりますので、非常に便利なはずです。
それほど複雑ではありませんので、コードから入ります。
string teleport_pos = ""; integer counter=0; default { touch_start(integer detected){ if (llDetectedKey(0) == llGetOwner()){ counter=0; } } touch(integer detected){ if (llDetectedKey(0) == llGetOwner()){ if (counter < 50){ counter++; }else if (counter == 50){ string sim_name = llGetRegionName(); vector pos = llGetPos(); teleport_pos = sim_name + "/" + (string)pos.x + "/" + (string)pos.y + "/" + (string)pos.z + "/"; llOwnerSay("Memorized a teleport position."); counter ++; } } } touch_end(integer detected){ if (llDetectedKey(0) == llGetOwner()){ if (counter < 50){ if (teleport_pos != ""){ llMessageLinked(LINK_ROOT, 0, teleport_pos, NULL_KEY); }else{ llOwnerSay("Not memorized a teleport position yet."); } } } } }
文字列型変数teleport_posはテレポート先を管理するための変数です。
SLのランドマーク表記でお馴染みの形式「SIM名/X/Y/Z/」でテレポート先を格納しておきます。
初期状態ではテレポート先の登録がないので空文字です。
SLのランドマーク表記でお馴染みの形式「SIM名/X/Y/Z/」でテレポート先を格納しておきます。
初期状態ではテレポート先の登録がないので空文字です。
counterはタッチし続けている間増加するカウンターです。
このカウンターが50に達すると、テレポート先の記録を行います。
このカウンターが50に達すると、テレポート先の記録を行います。
llGetRegionName()関数は初登場ですが、現在居るSIMの名前を返す関数です。
llGetPos()は同様に現在の位置(X,Y,Z)を返します。
この二つの関数で取得した位置情報を文字列型変数teleport_posに格納するわけですが、そこで見慣れない表現が出てきてますので説明します。
llGetPos()は同様に現在の位置(X,Y,Z)を返します。
この二つの関数で取得した位置情報を文字列型変数teleport_posに格納するわけですが、そこで見慣れない表現が出てきてますので説明します。
teleport_pos = sim_name + "/" + (string)pos.x + "/" + (string)pos.y + "/" + (string)pos.z + "/";
vector型変数posに、.xとか.yとか付けているのは、vector型データに含まれるxのデータ、yのデータを取り出しているのです。
例えば、<1.0, 2.0, 3.0>というvector型変数pがあったとすると、
例えば、<1.0, 2.0, 3.0>というvector型変数pがあったとすると、
p.x は 1.0 p.y は 2.0 p.z は 3.0
です。
つまり上記のスクリプトは、HogehogeというSIMの<10, 45, 20>で動かしたとすると、
"Hogehoge/10/45/20/"
という文字列を作ってteleport_posに格納することになります。
touch_end?イベントが発生したとき(マウスボタンを離したとき)にカウンターが50に達していなかったら、ルートプリムにテレポート先データを送信します。
ただしteleport_posにまだテレポート先データが格納されていない場合は「まだテレポート先を記憶してないよ」というメッセージの表示のみ行います。
ただしteleport_posにまだテレポート先データが格納されていない場合は「まだテレポート先を記憶してないよ」というメッセージの表示のみ行います。
ルートプリムへのテレポート先データ送信には、前回から扱っているllMessageLinked()関数を使います。
llMessageLinked(LINK_ROOT, 0, teleport_pos, NULL_KEY);
LINK_ROOTはルートプリムにのみメッセージを送信するということです。
送信したいのは文字列型データのteleport_posのみです。
数値型及びキー型のデータは使用しないので、0とNULL_KEYに固定しています。
送信したいのは文字列型データのteleport_posのみです。
数値型及びキー型のデータは使用しないので、0とNULL_KEYに固定しています。
ルートのスクリプト
続いてルートプリムに仕込むスクリプトです。
こちらも非常に簡単です。
こちらも非常に簡単です。
default { link_message(integer sender, integer num, string str, key id){ list telepos = llParseString2List(str, ["/"], []); string sim_name = llList2String(telepos ,0); vector pos = < llList2Float(telepos ,1), llList2Float(telepos ,2), llList2Float(telepos ,3) >; llMapDestination(sim_name, pos, ZERO_VECTOR); } }
ルートのスクリプトは、ボタンから送られてくるメッセージを受け取り、マップを表示するだけです。
llParseString2List()関数は前に登場しました。
文字列を指定した区切り文字で分割してリストに変える関数です。
今回のテレポートデータは"/"で区切られていますので、
llParseString2List()関数は前に登場しました。
文字列を指定した区切り文字で分割してリストに変える関数です。
今回のテレポートデータは"/"で区切られていますので、
list telepos = llParseString2List(str, ["/"], []);
こうですね。
文字列strが例えば、
文字列strが例えば、
"Hogehoge/10/45/20/"
だとしたら、
リストteleposは、
リストteleposは、
["Hogehoge", "10", "45", "20"]
のようになります。
このリストteleposからSIM名(先頭の文字列)と位置(後ろ3つの数値)を取り出し、llMapDestination()関数に渡しています。
llMapDestination()関数も看板を作ったときに登場しました。
指定したSIMの指定した位置をマップ上に表示する関数です。
llMapDestination()関数も看板を作ったときに登場しました。
指定したSIMの指定した位置をマップ上に表示する関数です。
今回のポイント
- SIM名の取得:
llGetRegionName()
- 現在位置の取得:
llGetPos()
新しく登場する要素がどんどん少なくなってきました(^^;
それだけいろいろなことを覚えてきているということですね。
それだけいろいろなことを覚えてきているということですね。
HUDは使い方次第でいろいろと便利なものを作れると思います。
今回の例は非常に簡単なものでしたが、応用して面白いものを作ってみてください。
今回の例は非常に簡単なものでしたが、応用して面白いものを作ってみてください。
さて、次回は何をしましょうw
書くべきことがほとんど無くなってきましたが・・・音でも鳴らしてみましょうかね。
ではまた。
書くべきことがほとんど無くなってきましたが・・・音でも鳴らしてみましょうかね。
ではまた。
- HUDの仕組みについて調べていたらここにたどり着きました。大変参考になりました。有難う御座いますm(__)m もしHUD関係のネタをまたやるようでしたら、HUDで他のプリムを制御する方法(例えば回転させたり、移動させたり)を取り扱っていただけませんか? -- daisuke (2007-09-23 17:09:41)
- 他のオブジェクトを制御する場合はllListenを使います。
以上のような流れになります。-- Miz
- 上記の説明とリンク先でHUDでの操作方法がなんとなく解った気がします。まだ半分くらいしか理解出来ていないですが、自分のさせたいような動作が出来るようになりました。非常に簡素で解りやすい説明有難う御座います。m(__)m -- daisuke (2007-09-25 22:42:02)
- こんにちは、daisukeです。先日教えて頂いたllListenを使ってprimを移動させるスクリプトは出来たのですが、そのスクリプトをHUDで動作させるところで躓いています…。移動させるprimに仕込んだスクリプトはオーナー権限で動作するようにしています。HUDでllSayを使って動かそうとすると、そのコマンドがオブジェクトからのコマンドになってしまい(オーナーの発言ではないと言う事になってしまい)移動させたいprimが全く反応しません。オーナーが装着したHUDで操作したllSayコマンドをオーナーの発言のように認識させるにはどうしたら良いのでしょうか? -- daisuke (2007-09-27 16:41:27)
- llSay(0,"message"+(string)llDetectedKey(0)); -- Eastsea (2008-02-13 04:16:45)
- HUD側は、llSay(1,"message"+(string)llDetectedKey(0)); で発言内容にプラス、タッチした人(つまりオーナー)のキーIDをつなげて発言し、受信側の処理はif(message == "message"+(string)llGetOwner())、とこちらは(ある意味こちらも?)発言内容プラス、オーナーキーとつなげて処理してみてはいかがでしょうか? これなら同じHUDと同じオブジェを持った人が近くにいても、混信を防げますし。但し、チャットチャンネル0以外がいいでしょね^^; llListen(199,"","","")llListen(1,"","","");と、チャンネル以外は空でも、おそらく問題はないかと。 -- Eastsea (2008-02-13 04:32:59)
- listen(integer channel,string name,key id,string message)で、HUDのオブジェクトUUidを取得した後に、if(llGetOwnerKey(id) == llGetOwner()) でHUDのオーナーIDと制御を受けるオブジェのオーナーIDを比較し実行とすれば、オーナー発言のみに反応するHUDとオブジェになりますね。 -- Eastsea (2008-02-14 02:34:15)