忍者ブログ
This is an official blog of Orange Forest who is the aircraft developer in the Secondlife virtual world.

「何でこんなに空きプリム少ないの?」な人向けスクリプト

ごぶさたしております。ミューズです。
みなさまお元気ですか?

photo01
::: Ahaaaaa... Where is my object? :::

オレンジフォレストは最近プリム不足病で、いつの間にか空きプリムが無くなっていて「何でこんなに空きプリム少ないのー?」な叫びが頻繁にコダマシテマスw
そんなこんなで今日は区画内に存在するオブジェクトをリストアップするスクリプトを作成したので、同じ悩みを抱える方々にそっと公開します。

使用方法は下記で紹介するスクリプトを入れたプリムを体の適当な場所にアタッチします。するとプリムを着用した場所と同じ土地区画の中のオブジェクトの検索とカウントアップを開始してチャットメッセージで表示します。

LSLのセンサーの仕様上、探知範囲と、1回で検出できるオブジェクトの総数に限りがあるため、このスクリプトでは探知範囲を半径40mにしています。このカウンター着用後、自分の土地の中をくまなく飛び回ってオブジェクトをセンサーに引っ掛けて行きます。


::: List objects in the current parcel :::

チャットメッセージのフォーマットは、
検出番号, プリム数, オブジェクト名, 位置, トータルプリム数
です。

ここで、"show 検出番号" とチャットすると、その検出番号のオブジェクトに向けて矢印パーティクルが一定時間飛んで場所を特定できます。


::: Point the detected object :::

ここからはスクリプトです。
いつも通り御利用は改変自由&自己責任でお願い致します。
バグ指摘、もっと楽な方法ありましたらやんわり教えて下さいね~w


//==================================================================
// Parcel prim counter
// $Id: primcount.lsl 701 2010-08-09 06:33:29Z myuse $
//==================================================================
list gObjIds;
integer gTotalPrims;
key gTgtParcelId;
integer gTimerCount;
float gTimerInterval = 0.97;
float gBeamTimeout = 30.0;
float gBeamRest;
integer gListenHndl = -1;

// 位置情報の簡略表示
string makePosString (vector pos) {
return "<" + (string)((integer)pos.x) + ", " +
(string)((integer)pos.y) + ", " +
(string)((integer)pos.z) + ">";
}

default {
// ユーザーがプリムカウンターを脱着した
attach (key id) {
list parcelParams;

// システム初期化
llParticleSystem([]);
llSetTimerEvent(0.0);
if (gListenHndl >= 0) {
llListenRemove(gListenHndl);
}

if (id == llGetOwner()) {
   // ユーザーがプリムカウンターを着用
// 計測開始
gObjIds = [];
gTotalPrims = 0;
gTimerCount = 0;
parcelParams = llGetParcelDetails(llGetPos(),
[PARCEL_DETAILS_ID, PARCEL_DETAILS_NAME]);
gTgtParcelId = llList2Key(parcelParams, 0);
llOwnerSay("Start counting object for this parcel (" +
llList2String(parcelParams, 1) + ").");
llListen(0, "", llGetOwner(), "");
llSetTimerEvent(gTimerInterval);
}
else {
// 計測終了
llSetTimerEvent(0.0);
}
}

// センサーでオブジェクトを探索
// 1イベント毎に scripted, non-scripted を交互に検索
timer() {
if (gBeamRest > 0.0) {
gBeamRest -= gTimerInterval;
if (gBeamRest <= 0.0) {
llParticleSystem([]);
}
}

gTimerCount = 1 - gTimerCount;
if (gTimerCount) {
llSensor("", NULL_KEY, PASSIVE, 40.0, PI);
}
else {
llSensor("", NULL_KEY, PASSIVE | SCRIPTED, 40.0, PI);
}
}

// ユーザーが指定したオブジェクトに矢印パーティクルを指し示す
listen (integer ch, string name, key id, string mes) {
list words = llParseString2List(mes, [" "], []);
key tgt;

if ((llList2String(words, 0) == "show") && (llGetListLength(words) == 2)) {
tgt = llList2Key(gObjIds, (integer) llList2String(words, 1));

if (tgt == NULL_KEY) {
llOwnerSay("ERROR : Unknown object number.");
return;
}

gBeamRest = gBeamTimeout;
llParticleSystem([PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_DROP,
PSYS_SRC_TARGET_KEY, tgt,
PSYS_PART_START_COLOR, <1.0, 1.0, 1.0>,
PSYS_PART_START_ALPHA, 1.0,
PSYS_PART_START_SCALE, <0.5, 1.0, 0.0>,
PSYS_PART_MAX_AGE, 9.0,
PSYS_SRC_BURST_RATE, 0.6,
PSYS_SRC_TEXTURE, "a990f9a1-ab01-5071-444e-552b0f3f4983",
PSYS_SRC_BURST_PART_COUNT, 1,
PSYS_PART_FLAGS,
PSYS_PART_TARGET_POS_MASK |
PSYS_PART_EMISSIVE_MASK |
PSYS_PART_FOLLOW_VELOCITY_MASK ]);
}
}

// センサー、検出したオブジェクトの判定
// 一度検出したオブジェクトはスキップ
sensor (integer numDetected) {
integer n;
key id;
vector pos;
string name;
integer prim;

for (n=0; n<numDetected; n+=1) {
id = llDetectedKey(n);
pos = llDetectedPos(n);
if (llList2Key(llGetParcelDetails(pos, [PARCEL_DETAILS_ID]), 0)==gTgtParcelId) {
if (llListFindList(gObjIds, [id]) < 0) {
gObjIds += [id];
prim = llGetObjectPrimCount(id);
name = llDetectedName(n);
gTotalPrims += prim;
if (llGetOwnerKey(id) == llGetOwner()) {
// Your object
llOwnerSay("@ No." + (string) llGetListLength(gObjIds) +
", " + (string) prim + "prim, " +
name + " " + makePosString(pos) +
", total " + (string) gTotalPrims + "prims");
}
else {
// Not your's
llOwnerSay(" No." + (string) llGetListLength(gObjIds) +
", " + (string) prim + "prim, " +
name + " " + makePosString(pos) +
", total " + (string) gTotalPrims + "prims");
}
}
}
}
}
}

で、結局、空きプリムが少ない原因は未知の巨大オブジェクトじゃなくてミューズのプリム浪費癖が悪かっただけってことが判明しましたとさ
orz 超ガックシ

ではでは
おそまつでした~
PR

LSL 実践講座 - HUD編

こんにちわ、ミューズです。
みなさん Viewer2.x は使ってますでしょうか? 巷ではさんざんな評判のようですがw ウチは営業&サポートの都合上、なるべく標準準拠させなくてはなので、Viewer2.x を使うようにしています。 今日は Viewer2.x でちょっと欲しい情報を表示するHUDを作成したので、それを久しぶりなLSL講座のネタにします。


::: Addtional Informaton HUD for Vewer2.x :::

今回作成するのはコレ↑
現在位置と対地高度(地面からの高さ)とかを表示します。
それではこのHUDの作成方法とスクリプトの解説を始めます。

2010/06/13 Myuse 追記
現在位置のみであれば、 メニューバーから World > Show > Coordinates のチェックを入れることで上のURLバーみたいなエリアの表示でも取得が可能です。また、土地の属性は World > Show > Parcel Properties でアイコンみたいなのが出ます。 本記事はあくまでもLSLの題材として参照下さい。


- 注意 -
お決まりの話しですが、ココに記載の情報利用に伴う一切の不利益についてOrange Forestは責任を負いません。本記事の内容は自己責任で参照下さい。バグ指摘、質問は歓迎です。


::: Create a new box prim :::

 まずはHUDになるプリムを準備します。
Ctrl+B でBuild用のダイアログを出し、図のようにしてBoxプリムを1個、地面に生成します。


::: Edit the created prim and resize :::

続いてそのプリムを編集してサイズを小さくします。
2cm~5cmの立方体にするとよさそうです。


::: Create a new Script in HUD prim's content :::

次にプリムのコンテンツTabに移動して空っぽのスクリプトファイルを生成します。
あ、いい忘れましたが作業する土地はオブジェクト生成OK&スクリプト実行OKな所でお願いします。


::: Paste script code from the below of this blog article :::

スクリプトの中身をこのブログ記事の下にあるコードと入れ替えます。
スクリプトの機能はスクリプトのコメントを参照下さい。
書き換えたら保存して下さい。スクリプトのコンパイル処理が始まります。


::: Attach the object as HUD :::

HUDを"Right Click">"Take"してInventoryに取り込みます。
取り込んだHUDを図のようにHUDとしてアバターに装着します。


::: Locate HUD :::

装着したHUDは、"Right Click">"Edit" で位置やサイズを調整可能です。


::: Detach HUD if you want :::

HUDが不要になった場合は図のようにインベントリから選択して"Detach"することが出来ます。

それでは中身のスクリプトです。
改変、再販はユーザーの責任において御自由にどうぞ。

//---------------------------------------------------------------------------------
// showcurpos - Show current position
// $Id: showcurpos.lsl 682 2010-06-12 15:54:53Z myuse $
//---------------------------------------------------------------------------------

vector gPrevPos = ZERO_VECTOR;

default {
  state_entry () {
    // Show object after build
    llSetAlpha(1.0, ALL_SIDES);
    llSetText("", ZERO_VECTOR, 0.0);

    // HUDを装着したままスクリプトを編集した場合の初期化処理
    if (llGetAttached()) {
      llSetAlpha(0.3, ALL_SIDES);
      llSetTimerEvent(1.0);
    }
  }

  // HUDを装着した場合、on_rez → attach の順番でイベントが発生する。
  // HUDを地面に置いた場合は on_rez のみ発生
  // HUDを不透明にして動作停止
  on_rez (integer rezParam) {
    // Show object after rez
    llSetAlpha(1.0, ALL_SIDES);
    llSetText("", ZERO_VECTOR, 0.0);
    llSetTimerEvent(0.0);
  }

  attach (key id) {
    if (id == llGetOwner()) {
      // HUD をオーナーが装着した
      // → 半透明にして1秒毎に動作開始
      llSetAlpha(0.3, ALL_SIDES);
      llSetTimerEvent(1.0);
      gPrevPos = ZERO_VECTOR;
    }
    else {
      // HUDを取り外した
      // → 不透明にして動作停止
      llSetAlpha(1.0, ALL_SIDES);
      llSetText("", ZERO_VECTOR, 0.0);
      llSetTimerEvent(0.0);
    }
  }

 // 1秒毎に呼び出されるHUD表示更新処理
  timer () {
    vector  pos    = llGetPos();
    integer ground = (integer)llGround(ZERO_VECTOR);
    integer water  = (integer)llWater(ZERO_VECTOR);
    integer parcel = llGetParcelFlags(pos);
    string  out;
    integer abs;
    vector  col    = <0.0, 0.5, 1.0>;  // blue

    // Check if moved ?
    if (gPrevPos == pos) {
      // アバターが移動していない場合は何もしないで終了
      return;
    }
    else {
      gPrevPos = pos;
    }

    // Get absolute altitude
    if (ground > water) {
      abs = ground;
    }
    else {
      abs = water;
    }
    abs = ((integer)pos.z) - abs;

    out = llGetRegionName() + "/" +
          (string)((integer)pos.x) + "/" +
          (string)((integer)pos.y) + "/" +
          (string)((integer)pos.z) + "\nAbsAlt: " + (string)abs + "m";

    // HUDの文字の色の設定
    // 赤 : 進入禁止区域
    // 橙 : Rez不可
    // 青 : Rez, 進入OK
    // イマイチ正しく機能しない場合あり

    if (parcel & (PARCEL_FLAG_USE_ACCESS_GROUP |
                  PARCEL_FLAG_USE_ACCESS_LIST |
                  PARCEL_FLAG_USE_LAND_PASS_LIST)) {
      col = <1.0, 0.0, 0.0>;  // red
    }
    else if ((parcel & PARCEL_FLAG_ALLOW_CREATE_OBJECTS) == 0) {
      col = <1.0, 0.5, 0.0>;  // orange
    }

    // HUD表示
    llSetText(out, col, 1.0);
  }
}


スクリプト講座題材の物理ブランコ復旧しました。

一部お問い合わせのあった、スクリプト講座題材の物理ブランコ復旧しました

以前の記事はコチラ↓です。
気まぐれスクリプト講座 Vol.1 - 物理なブランコ

移設先はコチラ
Ikebukuro/239/145/722


::: 池袋 Sky Yard 上の物理なブランコ :::

このスカイヤードは、 池袋 Orange Forest Skytard and Airship Shop の上空に浮いており、どなたでもゆったりとしたひと時をお楽しみ頂けます。時々、ミューズがここで飛行機作りをしていたりなんかします。
池袋 Orange Forest Skytard and Airship Shop は、飛行船の船室ショップ内にランディングポイントを設けています(観光地のバスツアーみたいだw)。なので、この記事では上空のスカイヤードに行くためのクールな!方法を御紹介しましょう

まず、ランディングポイントに着きましたら、すぐ側にあるこの椅子に座ってみます。

::: 謎の椅子? :::

ほどなくカウントダウン!!!が始まり、レーザーの色が変わります。

そのまま座って様子を見て見ましょう!

カウント0でその椅子は突然ロケット噴射を始め、大空に向かって飛び立ちます。

::: 発進 :::

ところが、このロケット椅子 (実は O.F Freefall M-204 www)、エンジンがおんぼろなんですねー 2回メインエンジンが故障して旋回・墜落してしまいます。2回目にエンジンが故障する天頂部付近で「えいやっ」と飛び降りてみます。

::: ここらへん :::

はいっ、これであなたは無事?にスカイヤードにたどり着けると思われます。スリル満点ですので是非お試しあれ!
お帰りはスカイヤードに備え付けの O.F Warp Port により地上に係留の飛行船までひとっとびです。

::: O.F Warp Port (開発中) :::

誰ですか?「最初っから Warp Port 使えばいいじゃん」とか言っている人は。。。。 この物理ブランコ、以前の記事から安定稼動向けの修正を施してあります。遠からずこのブログのスクリプト題材にしたいなあと考えてます。

今日のところはココまでっ!
おそまつでした。

O.F Warp Port の試作

こんばんわ ミューズです。
今日のエントリはテクニカル寄りな話を織り込んだ最近ホットな製作の様子を紹介です。


::: O.F SKy Yard より (最近ココでガサゴソしていることが多いです):::

今春「エレベータはあるけど、もっと手軽な移動手段はないものかなぁ」と思っていた所、友人のSIMで瞬間移動が出来るワープポートっぽいものを体験して「おっ!」と思い、作ってみたくなりました。
元々、llSitTarget()を使って宛先固定で300m位移動できるのは知ってたのですが、調べてみると↓コレや、類似した方法でSitしたアバターだけ移動させる方法を知りました。

lslwiki - LibraryWarpPos
http://rpgstats.com/wiki/index.php?title=LibraryWarpPos

その時、アバターだけ移動する方式で作成したのが↓コレで、製作の様子は以前、「Myuse easy tool-X」(爆) と言う怪しい名前で紹介頂いています。


::: Myuse easy tool-X (本名 WarpPort Ver1.x系) :::

市販品のごとく設置するだけで勝手にポート同士が相互に認証して宛先一覧を形成する機能が入っています。
※このときはクラッシャーオビワンさんの改造により破壊され、
 その原因を究明する事態にもなりましたが。。。。^^);;;

過去記事 - 2008.06.26 Custom MOTHER AIRSHIP
http://orangeforest.blog.shinobi.jp/Entry/72/

SIM内の離れた地点間を一瞬で移動できるコレのおかげで、製作ラボのあるスカイボックスを、混雑している高度300mから2000m近辺に引越す契機になりました。
ところが、Monoサポートに向けたSIM 1.24系へのアップデートで、突然機能しなくなり、調べてみると、まさに↓コレですねぇ

SVC-2931 llSetLinkPrimitiveParams content breakage, only moves agents 54 meters now
https://jira.secondlife.com/browse/SVC-2931

もともとバグ技っぽい感じでしたが、従来軽く 2000m 移動できたものが、最大54mになってしまいました。>< くぅ~
まー、でも何か逃げ手があるんだろって試行錯誤してみると、先述のLibraryWarpPos は大丈夫そうだし、かえって安定動作することが判明
従来方式だとたまに SIM の <0, 0, 0> に吹き飛ばされてたのが、今のとこ無し
そうして出来たのがコレ↓


::: O.F Warp Port V2.x系 :::

Ver1.0 と同様、ポート間の相互自動認証は健在です。
ポートごと移動するのでポートは使い捨てで別途ポートのrezzerを追加です。
Airship caravan SIM 内の Orange Forest の敷地各所と、SIM中央のモールにある Orenge Forest のスペースを結びます。O.F Sky Yard にお越し頂いた際にちょっとお試し下さいませ~


::: O.F Sky Yard (うーん、だんだん完成してきた♪):::

エレベータといい、コレといい、
SIMの挙動に泣かされ続けるきらいがあるので商品化は気分次第ってことで。。。。

次回のテクニカル系なエントリはブランコの続きです。たぶん

気まぐれスクリプト講座 Vol.1 - 物理なブランコ

こんにちわ。ミューズです。
今日のエントリは、先日オビワンさんが紹介して下さった ブランコ のスクリプトについて、たまたま題材にしやすそうだったので、ちょっと気まぐれで、LSL初心者+α位な方を想定して御紹介したいと思います。
これであなたも物理スクリプトデビュー?!

ブランコ
::: ブランコ :::

物理で動くブランコです。
設置位置について物言いがついていますwwww
置き場所はコチラ → Airship Caravan/32/83/255
ランディングポイントは飛行船ショップ店内なので、飛行船付属のエレベータで下に下りて頂くとすぐ行き着きます。

ブランコは複数名が好きな着座位置にsitでき、sit後に座席をタッチすると加速が掛かかってブラーンブランします。ブランコは2個のオブジェクトから構成されています。個々の構造については現物を御覧下さいネ

■物理なブランコスクリプトの説明

ここで説明するスクリプトの使用についてオレンジフォレストは一切の損害や、サポートに対する責任を負いません。あくまで自己責任で参照下さい。
スクリプトのCopyrightを保持した上で改変、再販は御自由に。



LSLの基本的な文法や使用方法についてはすぐれた解説書が出回っているのでここでは割愛します。

■ブランコスクリプトの構造

LSLはその言語仕様にステートの概念が入っているちょっと変わった言語ですよね。個人的にはあとはロボット用の動作記述言語でしか見たことが無いです。今回はそのステートを多用したので構造を説明するのに状態遷移図を使ってみます。
ブランコスクリプトは4つのステートから構成されています。

  • default : 初期状態
  • waiting : 誰も乗っていない待機状態
  • active : 誰かが乗ってくれた状態
  • accel : ブランコ加速中‥連続的な加速防止


::: 状態遷移図 :::

それでは、スクリプトです。
間違いとか、改善とかのツッコミは歓迎です。
日本語のコメントは後から書き足しました。普段はココまでやりません><

//================================================================================
// Physical Swing Seat
// All rights reserved, Copyright(C) 2008, Orange Forest
// $Id: seat.lsl 243 2008-09-09 15:04:22Z myuse $
//================================================================================

// グローバル変数 : 'g' は global を示す接頭語
// 変数を識別しやすくするためのローカルルール

integer gDefaultPrims; // 座席のプリム数
vector gDefaultPos; // 座席のホームポジション
rotation gDefaultRot; // 座席の据付時の角度

//---------------------------------------------------
// default state
// 座席のプリム数のカウントと、復帰座標の取得
// アバターが座ったかどうかをプリム数で判定するため
//---------------------------------------------------
default {
state_entry () {
gDefaultPrims = llGetNumberOfPrims();
gDefaultPos = llGetPos();
gDefaultRot = llGetRot();
state waiting;
}
}

//---------------------------------------------------
// waiting state
// 搭乗待ち状態
// SIM負荷軽減のため、人が乗らない場合は非物理
//---------------------------------------------------
state waiting {
state_entry () {
llOwnerSay("Enter waiting");

// 物理off
llSetStatus(STATUS_PHYSICS, FALSE);

// 初期位置に戻る - 後続のステートから戻った時用
llSetPos(gDefaultPos);
llSetRot(gDefaultRot);
}

// リンク状態の変化を検出
// 誰か乗ってくれたかどうかのチェック
changed (integer chgParam) {
if ((chgParam & CHANGED_LINK) && (llGetNumberOfPrims() > gDefaultPrims)) {
llOwnerSay("Enter active");
state active;
}
}

// 再rez 時はリセット
on_rez (integer rezParam) {
llResetScript();
}
}

//---------------------------------------------------
// active state
// 物理でぶらぶらしている状態
//---------------------------------------------------
state active {
state_entry () {
// 物理ON
llSetStatus(STATUS_PHYSICS, TRUE);
}

// リンク状態の変化を検出
// 全員降りちゃったかのチェック
changed (integer chgParam) {
if ((chgParam & CHANGED_LINK) && (llGetNumberOfPrims() == gDefaultPrims)) {
state waiting;
}
}

// 誰かがブランコをタッチして押した?
touch_start (integer numTouches) {
state accel;
}
}


//---------------------------------------------------
// accel state
// 加速状態 : 連続加速で暴走するのを防ぐため、
// 次回加速を受け付けるのは2秒後
//---------------------------------------------------
state accel {
state_entry () {
// 回転力を添加
llApplyRotationalImpulse(<0.0, 0.0, 100.0>, TRUE);
// 2秒毎にタイマーイベント
llSetTimerEvent(2.0);
}

// リンク状態の変化を検出
// 全員降りちゃったかのチェック
changed (integer chgParam) {
if ((chgParam & CHANGED_LINK) && (llGetNumberOfPrims() == gDefaultPrims)) {
state waiting;
}
}

timer () {
// タイマー停止
llSetTimerEvent(0.0);
state active;
}
}



いかがですか?
今回のスクリプトはアニメーション使うつもりが無かったので、着座位置を強制せず、誰かが座ったかどうかの判定をオブジェクトのプリム数で行っています。
そして物理のブランコは誰かが座ってオブジェクトにタッチしたら回転のモーメントを加えるだけです。非物理でブランコを作る場合は連続的な回転を演出しなければならないのでもっと大変ですよね。
また、タッチしたら回転‥というだけなので、応用が効くと思います。ミューズがパッと思いつくのは、鉄棒とか、ボール状のジャングルジムみたいなものかなぁ

それでは第1回スクリプト講座は終了です。
第2回があるかどうかはわっかりませーん
ではでは