乗物制作のチュートリアル

== 概要 ==
Second Life には物理エンジンがあり、LSL にはオブジェクトの物理的な挙動を制御する関数が数多くありますが、乗り物を制御するための独立した API もあります。
このページでは、乗物が動作する仕組みや乗物の説明で使う用語などを、利用可能な API を挙げつつ説明します。
この機能は、滑走、ホバー、飛行、水面を漂うといった動作を充分実現できるほど柔軟性があります。実現できる動作の例をいくつか挙げると、
* 動作志向軸に対する移動/角度偏向速度
* 個別に設定できる移動/回転摩擦
* 地面/水面/絶対高度を基準としたホバー
* バンクによる方向転換
* それぞれ移動と方向転換に用いる、線形モーターと角度モーター
スクリプトを仕込んだオブジェクトは一つの乗物ビヘイビア (vehicle behavior) を持つことができます。乗物ビヘイビアは , llSetVehicleFloatParam, llSetVehicleVectorParam, llSetVehicleRotationParam, llSetVehicleFlags, そして llRemoveVehicleFlags 関数によって設定されます。
これらの関数については後述します。さしあたり重要なのは、乗物ビヘイビアにはいくつかのパラメタがあり、それらは乗物の動作を調整するために変更できるという事です。それらの値の選択に応じて、乗物は水上を旋回する船のように動いたり、レール上を滑走するソリのように動いたりします。
乗物フラグを使うと、標準の振舞いに関していくつかの例外を設定できます。いくつかのフラグは、別の特定の振舞いにおいてのみ有効になります。例えば を設定すると乗物は地表高度を無視するようになりますが、これは乗物がホバーしている時だけ意味を持ちます。
== 注意 ==
Second Life の物理現象は、現実世界の物理現象ではありません。Second Life の乗り物は現実世界の乗り物の完全なエミュレーションではありません。
動作を安定させ、ユーザを安全にするために、その細かい挙動のいくつかは今後変更されるかもしれません。特に、付記されている多くの制限値や標準値はおそらく変更されるでしょうから、長い目で見るとそれをあてにすべきでありません。
乗物ビヘイビアと、オブジェクトに (物理的な) 衝撃や力を及ぼす他の関数を、併用しないほうが良いでしょう。特に , , , があてはまります。
, , , の使用も注意が必要です。これらは動作を不安定にはしないでしょうが、乗物ビヘイビアと相反し、本来の意図とは違う、あるいは一貫性の無い結果をもたらすかもしれません。自己責任で使ってください。
乗物の動作に関するバグを見つけた場合の、報告方法を一つ紹介します。乗物オブジェクトとスクリプトのコピーに、症状を説明したノートカードを沿えて Andrew Linden へ送ってください。送付物には "Bugged Vehicle XX" という名前をつけてください。XX にはあなたのアバター名のイニシャルが入ります。都合がつき次第、その乗物とスクリプトは調査される事でしょう。また、未処理のバグレポートが Jira に載っていないか検索してください。
== 定義 ==
""、""、"" という語は、飛行機や船の回転方向を表現するのによく使われます。これらはそれぞれ、ローカルの X, Y, Z 軸に関する回転に対応します。
というものを、物理学の授業のはじめに教わるかもしれません。これは任意の軸の正 (プラス) の回転方向を定義するのに使います。右手法則の使い方の例として、ロール軸の正方向回転を考えてみましょう。その回転が飛行機をどう動かすか視覚化しやすいよう、あなたの右手の親指を飛行機のロール軸と平行に置き、親指が X 軸の正方向を向くようにしてください。そして残り四本の指を掌方向へ曲げてください。それら四本指の向きが、飛行機がスピンする向きです。
乗物の振舞いを制御するパラメタの多くが以下のような形式をとります:
VEHICLE_BEHAVIOR_TIMESCALE (乗物_振舞い_時間スケール)
振舞いの "時間スケール" (timescale) とは押したり、捻ったり、何にせよ乗物に作用する振舞いにかかる時間であると一般に理解されます。その際、現在と次の瞬間の振舞いの差が、前の瞬間と現在の振舞いの差の 1/e になるよう減衰します。ここで言う "e" とは (約 2.71828182) です。言い換えると、指定した状態に達するまで指数関数的減衰を伴う時間スケールという事です。乗物の反応性を良くしたいならば、1 秒かそれ以下の短い時間スケールを設定してください。逆に無効にしたいならば、時間スケールを非常に長く、例えば 300 秒 (5 分) かそれ以上に設定してください。安定性を実現するため、設定可能な時間スケールには通常下限があり、通常それは 0.1 秒程度です。(時間スケールを極力短くしたいという場合は) 時間スケールをゼロに設定するのが安全で、常にその最小値と等しくなるよう設定されます。
== 乗物タイプの設定 ==
乗物のパラメタを設定する前に、まず最初に乗物ビヘイビアを有効にしなければなりません。乗物ビヘイビアは 関数に引数 VEHICLE_TYPE_* を与えて呼び出すと有効になります。ただし は例外で、乗物ビヘイビアを解除します。現在利用できる乗物タイプはの定数の節を見てください。乗物タイプはいずれ追加されるでしょう。
乗物タイプの指定は、乗物ビヘイビアを有効にする際に必須であり、またその時に全てのパラメタが (その乗物タイプの) 標準値に設定されます。各乗物タイプについて、同等に設定するための長いスクリプトが公開されています。これらの標準設定はどの乗物タイプににおいても (まだ) 最適化させていない事、またその値は将来確実に変更されるであろうという事に注意してください。値が確定するまでは、それらが不変であるかのようにあてにはしないでください。
何か風変わりな、あるいは実験的な乗物を制作したいという場合でもやはり、まずは標準乗物タイプの内どれか一つを選んで乗物ビヘイビアを有効にしなければなりません。その上で、あらゆるパラメタやフラグを、その指定可能範囲内で変更できるようになります。
乗物タイプを設定しても、(それだけでは) 乗物を動かすコントロール (キーボード/マウス入力) その他を自動的に取得できるわけではありません。いくら乗物ビヘイビアを有効にしても、それだけではまだオブジェクトは勝手に動き回る状態であり、高台の上に置いたら下へ滑り落ち始めるでしょう。
我々は新しく、またより良い標準乗物タイプを探しています。車、船、その他の標準乗物タイプを今より良くするパラメタの組み合わせを見つけたら、それをスクリプト、ノートカードに記すか、JIRA を通じて に渡してください。
== 線形/角度偏向 ==
現実の乗物に共通する特質として、それらが "動作志向軸" (preferred axes of motion) に沿って動くというものがあります。車輪、翼、(自身の) 形状、推進手段によって、その乗物はそのローカル座標の一定の軸方向へ押されたり、向き直ったりします。この一般的な特徴が乗物というカテゴリを定義しているのであり、一般的なダーツの矢も "乗物" としてこのカテゴリに含まれます。すなわち、矢の尾部には羽根がついているため、空中へ適当に放り投げられても、最終的に矢はその進行方向へ向き直ります。この姿勢制御の効果を角度偏向と呼びます。
車輪のついた乗物には違った効果があります。すなわち、スケートボードがなんらかの方向へと押された場合、それは (常に) 自由に転がって行ける方向への動作になります。この効果を線形偏向と呼びます。
従って Second Life での典型的な乗物とは、動作志向軸に沿った線形偏向や角度偏向を持つオブジェクトの事です。標準の動作志向軸は、乗物のルートプリムのローカル座標系における X (前), Y (左), Z (上) 軸です。以下のように、偏向は X 軸 (前方向) に関して行なわれます: X 軸が進行方向を向くよう、角度偏向 (の力) は乗物の姿勢を変えようとします。そうして乗物がローカルの正の X 軸方向に向き直るまでの間、線形偏向 (の力) による速度は一定しません。その他の軸も後述する乗物の振舞いに関係します。例えば、乗物の Z 軸を世界の Z 軸 (上方向) へ向け続けようとする垂直アトラクターがそうです。乗物の軸は パラメタによって、オブジェクトの実際のローカル軸とは別に設定できます。しかしそれは上級向けの機能であり、この文書の後の段で詳述します。
乗物の種類によって、線形偏向や角度偏向の値を大きくとるのが良い場合も、悪い場合もあります。偏向の速度は 関数にそれぞれをパラメタとして設定することで変更できます。
線形偏向と角度偏向には "時間スケール" パラメタがあり、それは偏向がどのくらい素早く発生するかを定義します。
基本的にこの時間スケールは偏向が完了するまでの指数関数的減衰の時間係数です。従って、素早く偏向する乗物は時間スケールを小さくすべきです。例えば普通のダーツの矢は角度偏向の時間スケールが 2 秒程度でしょうが、線形偏向に関しては数秒でしょう。従って、移動方向の修正より前に、姿勢の修正が行なわれるでしょう。ダーツの矢の偏向の時間スケールを設定するには、以下のようにします:
<lsl>
llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 2.0);
llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 6.0);
</lsl>
移動/角度偏向には "効力" パラメタがあり、それには 0.0～1.0 の間の任意の値を設定できます。乗物の振舞いを設定するその他の効力パラメタとは異なり、偏向の効力は "活発" と "不活発" の間を意味するのではなく、"全く偏向無し" から "偏向最大" の間を意味します。すなわちこれらは偏向時間スケールとかなり似通った効果を持ちますが、その範囲は 0.0～1.0 の間に正規化されます。
== 乗物を移動させる ==
いったん乗物ビヘイビアが有効になると、乗物は外からの力や 関数といった関数によって押されたり回転させられたりします。しかし乗物の動作をより簡単かつ滑らかにするために、移動/角度モーターという物があらかじめ用意されています。
その方向は 関数によって設定できます。例えば乗物をそのローカル X 軸 (標準の進行方向) に沿って秒速 5m で動かしたいとするならば、以下のような行をスクリプトに含めます:
<lsl>
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <5, 0, 0>);
</lsl>
乗物を速く走りすぎないよう設定にするには、線形モーターの速度を約 30m/s 以下に制限します。これは物理エンジンによる制限であり、将来可能になった時には引き上げられるかもしれません。
モーター速度を設定するだけでは全ての面白い乗物を有効にするには不充分です。例えば、ある人は望む速度まですぐに加速する車を望むでしょうし、別のある人は最大速度までゆっくり加速してゆく船を望むでしょう。この効果を制御するために パラメタを使えます。
基本的に、モーターの "時間スケール" とは乗物がその最大速度まで指数関数的に加速してゆく際のです。
乗物の移動速度を可能な限り最大にし、そのまま進ませたらどうなるでしょうか。走り続けて止まらないという事は必ずしも無く、自動的な "モーター減衰" という仕組みがあるため、最大速度に達した後、全てのモーターは次第にその出力を減らしてゆきます。
線形モーターのベクトルが設定される度、その "グリップ" (地面を掴む力) は、 が定義する時間スケールに応じて、即座に指数関数的な減衰を始めます。そうして充分な時間の後、モーターは完全に停止します。この減衰の時間スケールには二つの意図があります。まず一つ目として、減衰の時間スケールは 120 秒以上に設定できず、かつ常に有効になっているため、何らかの積極的制御 (キーボード入力や、スクリプトでの何らかの論理的ループ) 無しに乗物がいつまでも勝手に動き続けることは無い、という事が保証されます。二つ目として、単純な衝撃モデルを使うことで、ある種の乗物を動き回らせるのに減衰の時間スケールを使えます。すなわち、特定のキーが "押し下げられている/離されている" に応じてモータを "稼動している/休止している" よう設定するのでなく、減衰の時間スケールを小さくしてキーが "押し込まれた" 瞬間だけモーターを稼動させ、あとは自動的に減衰させるという設定が可能です。
モーター出力は、モーターの (速度) ベクトルが設定される度にリセットされます。従って速度をゼロ ベクトルにするのと、乗物が減衰して完全に止まった状態になるのとは別物です。前者では、乗物は速度がゼロになるよう (積極的に) 努めます。後者は、モーターを無力化したまま放置した状態です。
二つのモーター時間スケールはとても名前が似ていますが、その効果は異なりますので、混同しないようにしてください。
:: はモーターが最大速度まで達するのにかかる時間です。
:: はモーターがゼロ速度まで減衰する時間、いわば "減衰効果" です。一方をもう一方と取り違えて設定すると、なんとも不満足な結果になります。また、モーターの減衰の時間スケールを通常よりも小さく設定すると、モーターの実際の出力は減じられます。
== 乗物を回転させる ==
線形モーターと同様、常に稼動しており方向と出力を変更可能な角度モーターがあります。例えばある乗物を毎秒 5 度の速度でローカルの Z 軸 (上下軸) に関して回転させる場合、スクリプトに以下の行を追加します:
<lsl>
vector angular_velocity = <0, 0, 5 * PI / 180>;
llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, angular_velocity);
</lsl>
角度モーターの速度は最大で毎秒 2 回転 (4 * ラジアン/秒) です。(それ以上に設定しても、規定の最大値まで落とされます。)
線形モーター同様、角度モーターにも効力を設定するパラメタ 、およびモーターの減衰の時間スケールを設定するパラメタ があり、後者は標準で最大 120 秒まで設定可能です。
乗物を操縦する際、極端に大きく、あるいは長く、方向転換したくないかもしれません。角度モーターでそれを実現する方法の一つは、減衰の時間スケールを長く、(モーターを停止した時に乗物が素早く減速するよう) 回転摩擦を充分大きく設定することです。それにより、キーを押したとき角度モーターに大きな作用が加わり、逆にキーを離すとすぐ作用がゼロになります。もう一つの方法は、 に小さな値を設定し、キーを押し下げたときモーターを加速する (また場合によってはキーを離したときモーターを停止する) ような、より推進力のある手段で乗物を動かすことです。これはモーター出力の自動的な指数関数的減衰によるものであり、常に一定な回転摩擦によるものではありません。
角度モーターの出力をゼロにする事と、それを減衰させる事とは違います。角度モーターは完全に減衰 (して停止) すると、もはや乗物の動作に影響しなくなります。しかし出力をゼロにすると、それは乗物の "グリップ" をリセットし、回転速度がゼロになるよう (積極的に) 努めます。
ある種の乗物では、方向転換するために "バンク機能" を使えます。"バンク" とは飛行機やバイクが方向転換する時に行なうものです。バンク機能を持つ乗物がそのロール軸に関して姿勢をひねると、結果的にそのヨー軸に関して回転することになります。バンク機能は後述する "垂直アトラクター" を使っている時のみ有効になります。
== 垂直アトラクター ==
船のようないくつかの乗物は、その上面を常に上に向けているべきです。これは "垂直アトラクター" (vertical attractor) を有効にすることで実現できます。垂直アトラクターとは、乗物のローカルな Z 軸を世界の Z 軸 (いわゆる "上") に向けてバネのように跳ね返します。この特質を有効にするには でよろめきの時間スケールを、 でバネの強度を設定します。後者を 0.0 に設定すると、バネはその平衡状態からよろめきます。逆に 1.0 に設定すると、バネは指数関数的な速度減衰を伴いながら平衡状態へ落ち着きます。
<lsl>
llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 4.0);
llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.5);
</lsl>
垂直アトラクターは時間スケールを 300 秒以上に設定することで無効にできます。
標準では、垂直アトラクターは乗物が駆け上がる/駆け下りる動作の妨げになります。従って飛行機を作成する場合は ビットを設定し、ピッチ軸に関する垂直アトラクターだけ解除するのが良いでしょう。以下のようにします:
<lsl>
llSetVehicleFlags(VEHICLE_FLAG_LIMIT_ROLL_ONLY);
</lsl>
== バンク ==
バンクを機能させるためには、垂直アトラクターが有効になっていなければなりません。バンクは以下のように動作します: 乗物のロール軸に関する回転が、ヨー軸に関する回転速度を生み出し、ひいては乗物が方向転換します。ヨー効果の大きさは 、ロール回転角度に比例し、場合によっては動作志向軸に沿った速度もこれに加わります。
 は -1 ～ +1 の範囲で設定できます。プラスに設定した場合、ロール軸に関する (右手法則で言う) プラス方向への回転は、ヨー軸に関するマイナス方向のトルクを発生させ、ひいては右へ方向転換します。これは曲がろうとする方向へ乗物が傾く、実際の飛行機やバイクの動作そのものです。マイナスに設定した場合、バンクの効果も反対になり、曲がる方向の逆に乗物を傾ける事になります。(なんとも非 "物理的" ですが、面白い乗物が作れるならば良いでしょう。)
 は操作性だけを目的とした (つまり物理法則を無視した) パラメタです。それはバンクしている乗物を、物理法則でなくあなたの意図に従わせる為に使えます。例えば現実のバイクは、前進しながらでないとバンクしながら方向転換できません。しかしビデオゲームのバイクは、停止状態でもその場で方向転換できるよう、しばしば設定されます。なぜならキーボードやゲーム コントローラといった限られた入力では、その方が操作しやすいからです。 を使うと、バンクの現実度/非現実度の割合を調整できます。具体的には、完全に静的 (0.0) ～ 完全に動的 (1.0) の範囲で値を設定します。"静的" (非現実的) の場合、乗物のロール軸回転にのみ、バンクの効果は依存します。一方 "動的" (現実的) の場合、バンクの効果はロール軸に沿った移動速度に比例します。範囲内のどの値を設定すれば良いかを見つけるには、試行錯誤が必要かもしれません。
世界の Z 軸に関する既存の回転速度をバンク操作が打ち負かすまでにかかる時間は で設定します。従ってバンクによる素早い方向転換を望むならば、このバンク時間スケールをだいたい 1 秒以下にしてください。逆に数秒以上に設定すると、反応の鈍い乗物になります。
== 摩擦の時間スケール ==
 は、乗物がその参照座標系の各ローカル軸に関して完全に停止するまでにかかる時間スケールを定義する vector 型のパラメタです。各軸の時間スケールは、互いに独立しています。例えば地面を滑走している車は X 軸と Z 軸に関してはとても小さな摩擦しか持たず (だから容易に前進したり落下したりする)、逆に Y 軸に関してはとても大きな摩擦を通常持ちます:
<lsl>
llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <1000, 3, 1000>);
</lsl>
この時間スケールが長くなるほど、摩擦はより小さくなります。それゆえ、全ての移動摩擦を事実上無効化するには、全ての時間スケールに大きな値を指定してください。
移動摩擦にはスカラー値 (float 値) を設定する事もできます。その場合、各軸ともその同じ値が設定されます。以下の二例はいずれも等価で、摩擦を事実上無効化します:
<lsl>
// 全ての移動摩擦の時間スケールに 1000 を設定する
llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <1000, 1000, 1000>);
// 上と同義となる短縮形
llSetVehicleFloatParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, 1000);
</lsl>
 は、乗物がその XYZ 軸それぞれについて、その回転が停止するまでにかかる時間スケールを定義する vector 型のパラメタです。この回転摩擦は、移動摩擦と同じ方法で設定し、また無効化できます。
== 浮力 ==
乗物には浮力 (buoyancy) という性質が組み込まれており、それは 関数の呼び出しからは独立しています。この二種類の浮力を混同しないでください。乗物を浮き上がらせるには、 パラメタを 0.0 (浮力なし) ～ 1.0 (最大の反重力) の範囲に設定してください。
この浮力による振舞いは、 フラグが設定された場合のホバーと無関係ではありません。浮力、ホバー、VEHICLE_FLAG_HOVER_UP_ONLY が一緒に使われると、乗物がそのホバー高度を越えた時に、浮力効果が消滅します。ホバー高度以上に乗物が浮上しそうに思うかもしれませんが、そうなりません。これは仕様でなくバグなのですが、長く放置しすぎた為もはや修正できません。修正すると、(バグと知らずに) この振舞いに依存した多くの乗物を台無しにしてしまうからです。
乗物の浮力設定を、それとは独立した特質である 関数の呼び出しと一緒に行なう事は可能です。ある状況では、単独よりも大きな最終浮力効果が得られる場合もあり、オブジェクトが際限なく加速上昇し続けることになります。自己責任で試してください。
== ホバー ==
ホバー (hover) という振舞いは を 300 秒未満に設定することで可能になります。それ以上の時間スケールでは、全く無効になります。殆どの乗物では、2、3 秒数未満の短いホバー時間スケールが上手くゆきます。時間スケールが短くなるほど、乗物はその指定高度に素早く戻ろうとします。なお の値が、その際のホバー速度に影響するかもしれません。
ホバーは浮力とは独立していますが、 を 1.0 に設定すべきです。さもないと、重力加速度に打ち勝つほど が充分に大きくなるまで乗物は地面から離陸できず、(当然) 指定高度で漂うすべもありません。
 は弾力的 (0.0) ～ 滑らか (1.0) の範囲で設定できます。
弾力的に設定されると、乗物は指定高度より若干低めにホバーしようとし、 が跳ね返りのおおよその振動間隔になります。(実際の間隔は指定値より若干長めになります。)
パフォーマンス上の理由により、Second Life の物理エンジンが今後改善されるまでは、乗物は地面/水面だけをホバーの基準にできます。従って橋や馬など、プリムでできたオブジェクトを基準にホバーすることはできません。標準では、ホバーは地面や水面に対して漂うというものですが、これはいくつかのフラグで変更できます:
船を作成する場合は、 フラグを設定すべきです。ホバータンクを海底で運転したいならば、代わりに フラグを設定しましょう。潜水艦や気球を作成したいならば、 を設定しましょう。
これらのフラグは互いに独立しており、二つの相反するフラグを設定すると予期せぬ動作を起こします。(しかもその振舞いは一定しません。) これらのフラグは 関数を使って設定します。
 は乗物が、地面や水面からどの位の高さでホバーするか、あるいはグローバル高度のどこでホバーするか、を設定します。その最大値は 100 メートルです。なお乗物の "中心" とはその "重心" の事で、慣れていないと見当をつけづらい場合があり、またアバターが搭乗すると重心も変化する、という点に注意してください。
== 参照座標系 (reference frame) ==
乗物は自分がどの方向へ向かおうとしているか、自分の天地方向がどうなっているかを知るために、X (前), Y (左), Z (上) 軸を使います。標準では、これらの軸はオブジェクトのルートプリムのローカル軸と一致しますが、これは乗物そのものの XYZ 軸とそのルートプリムの XYZ 軸が標準で一致しなければならない事も意味します。ただし既に作成済みの乗物で、その進行方向とは別方向にルートプリムが構成されてしまっている場合でも、 パラメタで修正が可能です。このパラメタにより、乗物の軸を任意に再設定できます。
例えばロケットの作成を考えます。円柱で胴体、円錐で頭部を作り、切り込みを入れた直方体で羽根を生やし、円柱をルートプリムとしてそれらを皆リンクします。そのロケットは頭部方向へ動くのが理想的です。しかし円柱の対称軸がそのローカルの Z 軸である一方、乗物の標準の "進行方向軸" (角度偏向のもと線形偏向してゆく軸) はローカルの X 軸であり、それは円柱の丸い側面を通る軸です。以下のスクリプトは乗物の軸を回転し、ローカルの Z 軸を "進行方向軸" に、ローカルの X 軸のマイナス方向を "上方向軸" にします:
<lsl>
// 乗物の座標軸をローカルの Y 軸 (左右軸) に関して -PI/2 だけ回転する
rotation rot =llEuler2Rot(<0, PI/2, 0>);
llSetVehicleRotationParam(VEHICLE_REFERENCE_FRAME, rot);
</lsl>
参照座標系の使い方の例としてもう一つ、飛行中の姿勢安定に垂直アトラクターを用いるけれども VTOL (vertical takeoff and landing, 垂直離着陸) を行ないたいという飛行機を挙げます。この飛行機は、飛行中は背面が上になりますが、着陸時には機首が上になります。この乗物を着陸させるには: 垂直アトラクターを有効にしつつ、現在の を左右軸に関して +PI/2 回転させます。すると乗物はピッチを上げ、機首が天に向きます。あとは (垂直方向の) 摩擦を効かせて着陸台に落下させるか、ホバー効果を弱めながら着陸させます。