2007年06月21日
エレベーター2
ただ上下に箱が動くだけでした~ 箱~
なのでここはエレベーターらしく
・ 中に行きたい階のボタンパネル
・ 各階のエレベーター外にボタン
つけてみましょか まずは頭ん中で構想~~
.
..
...
....
...(5分経過)...
うぃ!
中のボタンパネルはなんか普通にListでも作ってできそ~
でも各階のボタンからどうやってエレベーター本体と通信しよぅ????
.
..
...
....
...(10分経過)...
え~と エレベーター本体と各階のボタンをリンクさせて…

エレベーターを動かすと…

( ゚д゚)ポカーン
ダメじゃん 一緒に動いてるよ
.
..
...
....
...(5分経過)...
あぃ じゃリンクせずに本体と各階ボタンを別オブジェにして
各階ボタンにしゃべらすか…

う~ん…これってエレベーター本体はlistenイベントが
常にぐるぐる回ってないと無理じゃない??
さすがに24時間回りっぱなしのlistenイベントはいかがかと…
サーバーさんに怒られそうだな うん却下
.
..
...
....
...(20分経過)...
あはw ボタンから物投げてみるか!
で本体にぶつかったらllDetectedNameかなんかで
投げた奴見つける! (゚Д゚)ゴルァ!! って

Σ(・ω・;|||
下に投げるか上に投げるか誰が知ってんの?
ボタンからはエレベーターの位置はわかんないしな~~
とりあえず下に投げてみて地面にあたったら上に跳ね返る?
って地面に当たったかエレベーターに当たったかなんて区別付くの??
なんか無謀だしカコわるいので却下
.
..
...
....
延々とLSLの関数眺める…
...(20分経過)...
llEmailってオブジェクトにもメール投げれるじゃん!
ヽ( ゚∀゚)/ やたー これなら本体がどこにいようが関係ない~

あぅ…メールが送信されるまでだいたい20秒…
受け取りに最大3秒…
はい却下 待ちきれません!
.
..
...
....
.....
......
.......
........
...(1時間経過)...
(*゚∀゚)ゞ すんごいこと思いつきました!
ボタンが押されると…
1.押されたボタン自身がlisten起動
2.llRemoteLoadScriptPinで空っぽのスクリプトを
エレベーターに投げて実行しちゃう
空っぽのスクリプトを受け取ったエレベーターは
3.timerイベントで空っぽのスクリプトが稼働中かを
llGetScriptStateで調べておいて 稼動してたら
自分自身のlisten起動
4.でもどの階のボタンからスクリプトを投げられたかは
わからないので『誰が呼んだの?』って全部のボタンに
聞いてみる
1の工程で呼び出し元の階のボタンはlistenが起動しているので…
5.ひとまずエレベーターから『誰が呼んだの?』を聞いて
自分自身のlistenを停止させる
他の階のボタンはlistenが元から動いてないので
エレベーターの声は聞こえず~
6.で呼び出し元の階のボタンがエレベーターに向けて
『おいらが呼んだ~』って叫んでみると…
7.3の工程でlistenを起動していたのでエレベーターは
ちゃんと6の声は聞こえる!
声から場所を判断して目的高度をセット
listenはこのとき止めちゃう
8.で移動開始~~
いやぁ~ 相手のlistenを起動させるのにllRemoteLoadScriptPinを
使ってキックさせるとはw 使い方思いっきり間違ってるような~~
でもSIMにも周りの人にも迷惑を掛けなさそうなので環境にやさしぃ うん
というわけで実際のオブジェにスクリプトを φ(..)カキカキ


実際には上みたいに動かすためにはかなりの下準備が必要だった…
エラいことになったので説明ははしょって… (ぇ
要はあらかじめ各階のボタンたちが自分のKeyと場所を
エレベーター本体に渡しておかないと エレベーター内の
行き先ボタンが作れないのね…
その辺はlistenとllShoutとtimerを駆使してやり取りしました~
はい そんなこんなで…
↓これを親プリム(ルートプリムへ)
float _MOVE_HEIGHT;
integer _MOVE_STATE = FALSE;
float _MOVE_SECOND;
float _MOVE_VELOCITY = 5.0;
float _TIME_INTERVAL = 0.1;
vector _TARGET_POS;
integer _TARGET_ID;
float _TARGET_MARGIN = 0.3;
list _TMP_LIST;
list _FLOOR_NAME;
list _FLOOR_KEY;
list _FLOOR_HEIGHT;
string _DEST_FLOOR_NAME;
float _SETTING_TIMEOUT = 20.0;
float _DIALOG_TIMEOUT = 15.0;
float _REPLY_TIMEOUT = 5.0;
integer _CURRENT_FLOOR;
integer _HANDLE;
integer _PIN = 7699;
integer _CHANNEL = -33;
key _TOUCH_KEY;
string _GRANDMA_SEARCH = "where are my grandchildren?";
string _GRANDMA_CONFIRM = "who is calling?";
string _GRANDCHILD_REPLY = "hi, grandma. im calling!";
string _BUTTON_SELECT = "Please select the floor where you want to go.";
string _SETTING_START = "start setting... please wait about 15 sec.";
string _SETTING_END = "end of setting";
string _DUMMY_SCRIPT = "dummy";
move()
{
vector pos = llGetPos();
_MOVE_HEIGHT = llList2Float(
_FLOOR_HEIGHT
, llListFindList(_FLOOR_NAME, [_DEST_FLOOR_NAME]));
_MOVE_SECOND = llFabs((_MOVE_HEIGHT - pos.z) / _MOVE_VELOCITY);
_TARGET_POS = < pos.x, pos.y, _MOVE_HEIGHT>;
_MOVE_STATE = TRUE;
_TARGET_ID = llTarget(_TARGET_POS, _TARGET_MARGIN);
llSetPrimitiveParams([PRIM_PHYSICS, TRUE]);
llMoveToTarget(_TARGET_POS, _MOVE_SECOND);
}
keepHorizon()
{
vector cur_rot = llRot2Euler(llGetRot());
float z_rot = cur_rot.z;
llRotLookAt(llEuler2Rot(< 0.0, 0.0, z_rot>), 0.5, 0.5);
}
default
{
on_rez(integer param)
{
llResetScript();
}
state_entry()
{
llSetPrimitiveParams([PRIM_PHYSICS, FALSE]);
llSetRemoteScriptAccessPin(_PIN);
llSetScriptState(_DUMMY_SCRIPT, FALSE);
state setting;
}
}
state setting
{
touch_start(integer num_detected)
{
llOwnerSay(_SETTING_START);
_HANDLE = llListen(_CHANNEL, "", NULL_KEY, "");
llShout(_CHANNEL, _GRANDMA_SEARCH);
llSetTimerEvent(_SETTING_TIMEOUT);
}
listen(integer channel, string name, key id, string message)
{
vector pos = (vector)message;
float z = pos.z;
_TMP_LIST += [z, id];
}
timer()
{
integer i;
float diff;
vector pos = llGetPos();
float z = pos.z;
float min;
integer grand_floor;
llSetTimerEvent(FALSE);
llListenRemove(_HANDLE);
_TMP_LIST = llListSort(_TMP_LIST, 2, TRUE);
for (i = 0; i < llGetListLength(_TMP_LIST); i += 2) {
_FLOOR_HEIGHT += [llList2Float(_TMP_LIST, i)];
_FLOOR_KEY += [llList2Key(_TMP_LIST, i + 1)];
if (i == 0 || diff > llFabs(llList2Float(_TMP_LIST, i) - z)) {
diff = llFabs(llList2Float(_TMP_LIST, i) - z);
min = llList2Float(_TMP_LIST, i);
}
}
grand_floor = llListFindList(_FLOOR_HEIGHT, [min]);
for (i = 0 ; i < llGetListLength(_FLOOR_HEIGHT); i++) {
if (i < grand_floor) {
_FLOOR_NAME += ["B" + (string)(grand_floor - i)];
} else {
_FLOOR_NAME += [(string)(i - grand_floor + 1) + "F"];
}
}
llOwnerSay(_SETTING_END);
state waiting;
}
}
state waiting
{
state_entry()
{
llSetTimerEvent(_TIME_INTERVAL);
}
link_message(integer sender_num, integer num, string str, key id)
{
_TOUCH_KEY = id;
state child_calling;
}
timer()
{
if (llGetScriptState(_DUMMY_SCRIPT)) {
state grandchild_calling;
}
}
}
state child_calling
{
state_entry()
{
_HANDLE = llListen(_CHANNEL, "", NULL_KEY, "");
llSetTimerEvent(_DIALOG_TIMEOUT);
llDialog(_TOUCH_KEY, _BUTTON_SELECT, _FLOOR_NAME, _CHANNEL);
}
listen(integer channel, string name, key id, string message)
{
_DEST_FLOOR_NAME = message;
llListenRemove(_HANDLE);
state moving;
}
timer()
{
llListenRemove(_HANDLE);
state waiting;
}
}
state grandchild_calling
{
state_entry()
{
_HANDLE = llListen(_CHANNEL, "", NULL_KEY, "");
llSetTimerEvent(_REPLY_TIMEOUT);
llShout(_CHANNEL, _GRANDMA_CONFIRM);
}
listen(integer channel, string name, key id, string message)
{
_DEST_FLOOR_NAME = llList2String( _FLOOR_NAME
, llListFindList(_FLOOR_KEY, [id]));
llListenRemove(_HANDLE);
state moving;
}
timer()
{
llListenRemove(_HANDLE);
state waiting;
}
}
state moving
{
state_entry()
{
llSetTimerEvent(_TIME_INTERVAL);
move();
}
at_target(integer number, vector targetpos, vector ourpos)
{
llTargetRemove(_TARGET_ID);
llStopMoveToTarget();
llSetTimerEvent(FALSE);
keepHorizon();
llSetPrimitiveParams([PRIM_PHYSICS, FALSE]);
_MOVE_STATE = FALSE;
llSetPos(_TARGET_POS);
llSetScriptState(_DUMMY_SCRIPT, FALSE);
state waiting;
}
not_at_target()
{
}
timer()
{
llSetPrimitiveParams([PRIM_PHYSICS, TRUE]);
keepHorizon();
}
}
↓これを子プリム(行き先ボタンのやつ:2つプリムあるけど同じスクリプト入れてます~)
default
{
touch_start(integer total_number)
{
llMessageLinked(LINK_ROOT, FALSE, "", llDetectedKey(0));
}
}
↓んでこれを孫プリム(親子とはリンクされているわけでないです~:ちなみに孫も全部同じスクリプト
integer _PIN = 7699;
integer _CHANNEL = -33;
string _GRANDMA_SEARCH = "where are my grandchildren?";
string _GRANDMA_CONFIRM = "who is calling?";
string _GRANDCHILD_REPLY = "hi, grandma. im calling!";
integer _HANDLE;
key _GRANDMA;
vector _MY_POS;
string _DUMMY_SCRIPT = "dummy";
float _TIMEOUT_LOST = 600.0;
float _TIMEOUT_WAIT = 10.0;
vector _FLOAT_TEXT_COLOR = < 1.0, 1.0, 1.0>;
float _FLOAT_TEXT_ALPHA = 1.0;
string _FLOAT_TEXT_DEFAULT = "Please touch me if you use the elevator.";
string _FLOAT_TEXT_CALLING = "Just a moment please... I'm calling the Elevator.";
string _FLOAT_TEXT_COMING = "OK! Elevator is coming soon!";
default
{
on_rez(integer param)
{
llResetScript();
}
state_entry()
{
state lost;
}
}
state lost
{
state_entry()
{
_HANDLE = llListen(_CHANNEL, "", NULL_KEY, _GRANDMA_SEARCH);
llSetTimerEvent(_TIMEOUT_LOST);
}
listen(integer channel, string name, key id, string message)
{
_GRANDMA = id;
_MY_POS = llGetPos();
llListenRemove(_HANDLE);
llShout(_CHANNEL, (string)_MY_POS);
llSetTimerEvent(FALSE);
state wait;
}
timer()
{
llListenRemove(_HANDLE);
}
}
state wait
{
state_entry()
{
llSetText(_FLOAT_TEXT_DEFAULT, _FLOAT_TEXT_COLOR, _FLOAT_TEXT_ALPHA);
}
touch_start(integer num_detected)
{
llSetText(_FLOAT_TEXT_CALLING, _FLOAT_TEXT_COLOR, _FLOAT_TEXT_ALPHA);
llRemoteLoadScriptPin(_GRANDMA, _DUMMY_SCRIPT, _PIN, TRUE, FALSE);
_HANDLE = llListen(_CHANNEL, "", NULL_KEY, _GRANDMA_CONFIRM);
llSetTimerEvent(_TIMEOUT_WAIT);
}
listen(integer channel, string name, key id, string message)
{
llSetText(_FLOAT_TEXT_COMING, _FLOAT_TEXT_COLOR, _FLOAT_TEXT_ALPHA);
llListenRemove(_HANDLE);
llSetTimerEvent(_TIMEOUT_WAIT);
llShout(_CHANNEL, _GRANDCHILD_REPLY);
}
timer()
{
llSetTimerEvent(FALSE);
llSetText(_FLOAT_TEXT_DEFAULT, _FLOAT_TEXT_COLOR, _FLOAT_TEXT_ALPHA);
llListenRemove(_HANDLE);
}
}
↓最後にこれを親と孫のContentsの中に"dummy"という名前で放り込んでおく
default
{
state_entry()
{
}
}
それぞれのプリムを適当に位置調整して親プリムをクリックすれば
初期設定が始まります~ その後はご自由に動き回れる~ わーーーい
これはエレベーター内のボタンパネル~(ちゃんと地下階も自動計算されるw)

外のボタンを押してエレベーターが来るのを待つかっぱ

んでもって やっぱり…

あはw はさまれるのは直せないw
これ以上はむり~~ エレベーターはこれでしゅうりょ~
Zzz ( ̄~ ̄) ムニャムニャ
ゆかりちゃんと会ったんだけどさ、あの子工口すぎ( ;゜Д゜)
アイスとかチョコレートとかを俺の珍珍に塗りたくっていっぱいペロペロしてくれたよヽ(´ー`)ノ
アイス塗られた時...
こんな仕組みになっていたとは・・・しかも図入りで分かりやすく解説されてますねー。
それにしても物作りって試行錯誤の連続ですね。
PS.河童さんのブログもお気に入りに追加させて頂きましたー。
とうとうエレベーターが終了しましたか・・・。
どうもです〜!
もの作り飽きないです〜
完璧に自己満足の世界ですがw
こちらのサイトを参考に作成中のダンスクラブに上に移動する「お立ち台」を」作成中です♪ 衝突関数を追加して移動距離の制限をかけようとなんとか頑張ってます!(アバタ対象は面白いので放置・・・)
これからも参考にさせて頂きますので頑張ってください!
あわわ 参考にされてるw
ヾ( ~▽~)ツ ワーイ♪ うれしいですー
お立ち台がんばってくださいねー^^
衝突で停止処理かぁ〜 そかー
そっちの方がスマートだったかも〜
d(>_< )ぐー
見てくださってる方もいるので
も少しスクリプト関係を充実させよ〜と
さっそく引きこもるかなぁ(T-T)
やっぱり引きこもり〜w
ウチでもエレベーター設置したくて情報求めて彷徨っていたところ、こちらの記事に行き当たりました。
判りやすくて非常に為になる記事なので、参考(パクリ?)にさせて頂きました。m(_ _)m
ゴンドラを呼び出すための方法論ですが、「LSL流し込んでListen起動」のフロー読んでて気が付いた事があります。
記事では手順2にて空のスクリプトをリモートロードして、それを監視してリスナ起動し、呼出機と通信して呼出元を特定してますよね。
ですが、リモートロードで投げるスクリプトの中に、「2階から呼出だぜ」などと呼出元をリンクメッセージで伝えるコードを書いておけば、リスナ起動するまでもなくゴンドラは呼出元を知る事が出来ると思います。
アバターが挟まれてしまうのを防ぐ方法として、ウチでは現実と同じようにフロアにも扉を作りました。
ついでにゴンドラが居る位置を表示する表示器も付けてみました。
これらの機能を各フロアに付けると、ゴンドラから全フロアに逐一情報を送らなくてはなりません。
当初はリスナを使わないようにと全てリモートロードで情報伝達しようとしましたが、リモートロードだと一度に複数の対象へ同報することが出来ないのと、1回1回のディレイが長いのでリアルタイム性が失われて役に立ちませんでした。
仕方なく各フロアに1個ずつ常時リスナを使ってしまいました。
見た目的にはぐれいとなエレベータが完成したのですが、SIMへの負担という面で考えるとやっぱ不味いですかねぇ。
w(゜ー゜;)wワオッ!!
すごーい。このスパゲッチコードを解読されちゃってるw。
リモートロードの中にリンクメッセージ!
そうか、その手がありましたね〜!全然思いつかなかった…。
新たな組み合わせを知れて幸せ〜w
常時listenを稼働させててそれが原因で
SIMが落ちるようなことはあり得ないと思いますが、
やっぱり精神衛生上よろしくないですよね〜。
でもkundaliさんみたいに『知ってて最小限で使う』のと
『知らずに無駄に使う』のは大きな違いだと思います。
SIMのことを考えて、「ごめんなさい、使っちゃいますね」の姿勢は
個人的には許される範囲だと思いますよ〜。個人的にはですが。

ここまで複雑になるとはw
参考にさせていただきたいと思います。(というかカスタマイズ?)m(_ _)m
どうぞどうぞ〜^^
なんかいろいろ変な記述してますが
結局はllMoveToTargetさえ使えれば
エレベーターは動かせますので
頑張ってくださ〜い。

わかりやすくって参考にさせていただきました。ありがとうございます。
ところで、このエレベーターをREZ-fauxに建物と一緒に入れておこうと考えていますけど、REZする度にスクリプトのリセットが必要になるんでしょうか?
エレベーターとは関係のない話かもしれませんが、ご教授お願いできませんか?