Hatena::Groupfatalemployeetraining

資材部の懲りない面々・あれこれブログ

「間違った社員教育」製品版委託頒布中!

2010-11-05

先日の関数使うと

こんなことができるわけです(これ自体はめずらしくもないかもですが)。


これのカメラ位置設定が

	// LABEL1:中央のR-44のラベル, LABEL2:カメラとして使うNull Objのラベル
	camera_angle_y = 180.0; // R-44を中心にした円筒座標系におけるカメラ設置位置の方位角設定
	camera_pos_y   = 0.0;   // R-44を中心にした円筒座標系におけるカメラ設置位置の鉛直軸座標設定
	set_rel_pos_cyl_abs(LABEL2,LABEL1,4.0,camera_angle_y,camera_pos_y); 
					// カメラ位置設定(円筒座標系での半径方向座標は4.0にしている)
	view_mode("SHORT"); // カメラモードをSHORTに
	set_cam_shake_flag(FALSE); // カメラ手ぶれOFF
	cam_focus(LABEL2, LABEL1); // LABEL2からLABEL1を見る視点にする
	
	while(1){
		system();
		padroll  = get_pad_analog('ROLL'); // パッドのロール入力取得(-1.0~1.0)
		padpitch = get_pad_analog('PITCH');// パッドのピッチ入力取得(-1.0~1.0)
		camera_angle_y +=padroll;      // R-44を中心にした円筒座標系におけるカメラ設置位置の方位角設定
		camera_pos_y   +=padpitch*0.2; // R-44を中心にした円筒座標系におけるカメラ設置位置の鉛直軸座標設定
		set_rel_pos_cyl_abs(LABEL2,LABEL1,4.0,camera_angle_y,camera_pos_y); // カメラ位置更新
	}

だいたいこんな感じで済んでしまうので、らくらくなのがポイント。

(実際は窓の文字更新のコードも含むんですが省略してます)

で、このサンプルを改造していろいろ試してるわけですが、

いくつか分かった関数仕様をまとめておきます。

[catapult()]

航空機を発進させると、カタパルト元にしたユニットの座標よりy軸方向に+0.4ずれた位置に配置される。

この「+0.4ずらして配置」の話は、実は着陸車輪まわりの関数の説明に書かれている。

指定した座標から自動で+0.4ずらす動作は、初期のplcファイルに基づくユニットの配置と、このcatapult()関数で確認されている。

座標を指定して移動させるset_pos()関数では、この処理は差し挟まれ無い。

[set_parent()]

ユニットに、親ユニットを設定・変更する関数。しかし、一度子ユニットになったオブジェクトを、親の存在しない独立したユニットにする方法は残念ながら開示されていない(単にないのかも)。使用されていないラベル値や-1を設定しても何も起きなかった。しかしまあこれについてはrelease_object()してからのcatapult()なりput_group()なりで、0フレームでのユニットすり替えマジックショーをすれば解決するでしょう。

set_parent()を実行すると、その瞬間に、親ユニットと同じ座標、同じ姿勢角(親ユニットに割り付けられた座標系の原点座標、かつ相対姿勢角はすべて0)の状態になる。そして、以後親ユニットの運動にあわせて運動する。

なお、航空機ユニットを(たとえセクションを200,速度を0としても)、子ユニットとすることはできない(したかったのに…)。1フレームごとにsystem()中にエンジン側で計算される座標計算がおかしくなる。想像するに、これは後述するget_pos()の仕様によるものだと思われるのだが…

この残念な点は、当座、先日作成した、親子関係にないユニットを相対座標指定で移動する関数を使って擬似的にくっついていかせるしかなさそう。

[set_pos(), set_rot()]

位置・姿勢角を設定する。

  • 誰の子ユニットでもない(親ユニットが存在しない)独立なユニットの場合:
    • 位置座標は絶対座標系(マップ中心がx=2500,z=2500,y軸が高度、zは北が正、xは東が正、yは鉛直上が正)、姿勢角はxz平面に対するピッチ角、z軸に対する方位角、機体軸に対するバンク角、で計算される。CHILDフラグはTRUEでもFALSEでも動作は同じ。
  • 親ユニットが設定された子ユニットの場合:
    • 位置座標は相対座標系(親の機体軸進行方向がz軸、翼右方向がx軸、機体上方がy軸)、姿勢角は親の翼面に対するピッチ角、機体翼面に垂直な軸に対する方位角、機体軸に対するバンク角、で計算される。CHILDフラグTRUEにしないと、何も動作しない。

つまりCHILDフラグの真意は、スクリプト作成者が、そのユニットが何かの子ユニットであることをしっかり理解して、各種関数で操作する際に明示的にTRUEをつけて操作しないと、その子ユニットを独立に運動させることができない、という安全装置であるといえる。

でも「じゃあ闇雲にTRUEにしておけばいいんじゃん?」とされてしまうと、安全装置として成立しない罠。悩ましい。

なぜ親の存在しない独立したユニットでTRUEでも(FALSEと同じように)動くように作られているのか(この結果安全装置としては片手落ち)。逆に、なぜ、ユニット自身に「自分には親がいる/いない」を判断させて、自動で動作が切り替わるようになっていないのか(安全装置が必要ないなら、現状での動作はこれで実現できる)。(なんというか…そのせいで初期はTRUE/FALSEの切替で参照する座標系が変わるようになっているのかと誤解してしまっていた)。set_pos(),set_rot()はこの仕様のままでも、「自分の親はいない/親のラベルは○○である」を知ることができる関数(残念ながらそういう関数はまだないですがー)が追加されれば、実はこのCHILDフラグ隠蔽したラッピング関数をユーザー側で用意できる状態とも言える。…いっそ、CHILDフラグをFALSE指定すると親ユニットが存在するユニットではset_rotが機能しないのを利用して、(0フレームで瞬間的にある角度に回そうとしてみて、動くか動かないかをget_rotで測定するとかして)親がいるか/いないか調べる関数とか作るといいんだろうか。ちょっとトリッキーすぎるかな。

なお、航空機ユニットを1回だけこの関数で配置して、セクション200、速度0の動かないはずの状態で放置しておくと、数値計算誤差が残るのか、時間経過とともにびみょーに位置・姿勢角が動いていく。これを防ぐには、system()実行前にget_pos()、get_rot()しておき、system()実行後にその値をset_pos()、set_rot()で上書きしてしまう。そうするとsystem()で計算された座標の変化(誤差含む)はキャンセルされ、system()実行前の状態に設定されるのでぴたっと静止する。(もちろん思い通りに動かしたければ、get_pos/get_rot直後に位置・姿勢角を数値で動かせばよい)。まあ誤差が気になる人だけが対象の話ですが。

[move(), rel_rot()]

現在の位置・姿勢角からの相対的な移動・回転を行う。

使用する座標系、CHILDフラグの扱いについてはset_pos(),set_rot()と同じ。

[get_pos(), get_rot()]

現在の位置、姿勢角を得る。

  • 誰の子ユニットでもない(親ユニットが存在しない)独立なユニットの場合:
    • get_pos()の位置座標は絶対座標系(マップ中心がx=2500,z=2500,y軸が高度、zは北が正、xは東が正、yは鉛直上が正)、get_rot()の姿勢角はxz平面に対するピッチ角、z軸に対する方位角、機体軸に対するバンク角、で与えられる。
  • 親ユニットが設定された子ユニットの場合:
    • get_pos()による位置座標は相対座標系でなく絶対座標系(マップ中心がx=2500,z=2500,y軸が高度、zは北が正、xは東が正、yは鉛直上が正)、get_rot()による姿勢角は親の翼面に対するピッチ角、機体軸に対する方位角、機体軸に対するバンク角、で計算される。なぜget_pos()は相対座標系での座標取得ではないのか…。

[get_rot_abs()]

現在の姿勢角を得る。

ユニットが親であるか子であるかにかかわり無く、get_rot_abs()の姿勢角はxz平面に対するピッチ角、z軸に対する方位角、機体軸に対するバンク角、で与えられる。get_rot()と明確に動作が分かれてて分かりやすい。

[move_abs()]

絶対座標で指定した位置座標に移動する。moveと異なり、移動量のベクトルをさすのではなく移動先の位置座標を指定する必要があるので注意。さらにもう一つ。ここで用いる絶対座標は、get_pos()やset_pos()で用いられる絶対座標とは異なるので注意。

「move_abs(LABEL,0.0,0.0,0.0,FALSE);」で移動する先は、「set_pos(LABEL,2500.0,0.0,2500.0,FALSE);」で移動する先に等しい。すなわち、get_pos(),set_pos()で使っている座標系においてx=2500,z=2500,y=0を原点とする位置に平行移動した座標系を使って指定する。

また、CHILDフラグについて。これも注意が必要。

  • 誰の子ユニットでもない(親ユニットが存在しない)独立なユニットの場合:
    • CHILDフラグがTRUE/FALSEいずれの場合でも、move_abs()に用意された座標系における指定された位置座標に移動する。
  • 親ユニットが設定された子ユニットの場合:
    • CHILDフラグがFALSEの場合は動作しない。
    • CHILDフラグがTRUEの場合、座標計算がおかしくなり(たぶん内部での座標移動計算の累積がうまくない)、はるかかなたの座標に飛ぶ。

つまり、現状は子ユニットに対して使ってはいけない、ということ。じゃあset_pos()でいいじゃん!ということになってしまうのですが…過去互換性とかのためにあるのかな?

さて、たとえば親にR-44を指定し、子として何かユニットをset_parent()でつけるテストで。

子にFRAKの本体(砲台上部)を使う場合と、子にR-21を使う場合で気がついたことなんですが、以下の症状が確認できました。

なおR-44、FRAKの本体(砲台上部)、R-21の3つとも、事前にset_section()を使ってセクション値を200(スクリプトがコントロールする)、set_speed()を使って速度ゼロにしてあります。

  • R-21の親をR-44に指定→位置の座標変換に異常、正常な位置に出現せず(座標x,y,zはめちゃくちゃに変わり続ける)
  • FRAK本体の親をR-44に指定→正常に座標変換され、R-44の上に出現
  • FRAK本体の親をR-44に指定→正常に座標変換され、R-44の上に出現→get_pos()でFRAK本体座標を取得、取得した座標(絶対座標値)をそのままset_pos()+CHILDフラグTRUEで代入(相対座標の値として無理矢理指定)→R-21の親をR-44に指定したときとまったく同様の症状(位置の座標変換に異常、正常な位置に出現せず座標x,y,zはめちゃくちゃに変わり続ける)

これから推測されることなんですが、航空機はその運動を、スクリプトがsystem();でエンジン側に処理を返してる間に計算しています。このときに、エンジン内部では「get_pos()で現在座標を取得→速度・角速度を用いて1フレーム後の位置・姿勢を計算→set_pos()で設定」としてるのではないでしょうか。もしこうなっているなら、get_pos()の仕様が「ユニットが親・子どちらであるかに関わらず絶対座標を返す」ようになっている一方、set_pos()がCHILDフラグTRUE時には「移動先を親の相対座標で指定する」になっている結果として、速度ゼロでユニットが動かないにも関わらず問題が発生している、と言えます。

get_pos()の仕様をget_rot()と同等にすれば現状の問題は解決すると思われます。(その際はget_rot_abs()と同様get_pos_abs()が欲しくなると思います。)

ただ、すでにget_pos()を現状の仕様で子ユニットに使ってうまく動くようにしちゃってる既存ミッションがあるかもしれないので…簡単には仕様変更できないかもですね。

あと、たとえ解決したとしても、子ユニットとして登録した航空機を飛行させると、「親ユニットの座標系に対して相対的に飛行運動する(さも親ユニットの座標系が絶対座標系の空であるかのように、です)」ので、親ユニットがロールしたりすればもう即座に、物理法則を無視した運動になってしまいます。なので需要自体はすごく低いとは思うんですが…

じゃあ、なんで欲しいんだよ!というと。

航空機航空機をくっつけたような状態(ジャンボ機の背中にシャトルがついてるような状態)を作りたいわけです。

それだけならば「なんだ、おまけでくっつくほうのユニットを、形だけ飛行機にして航空機扱いじゃなくOBJECTとかFACILITYとか扱いで登録しておけばいいじゃない」となるのですが、さらにやりたいことがあります。

くっついている複数の航空機、両方に航空機としてのターゲットマーカを出して攻撃可能にしたい、んですね。

ターゲットマーカを出すだけなら、航空機扱いじゃなくても出力されます。しかし、航空機じゃないものは空を飛ぶはずがない/高速で移動するはずがない、ことになっているのか、高速移動しても機銃の照準器が未来予測位置に移動しません。

つーことで、えらく細かいことをやろうとして気がついたことだったりします。

で、当座手元では、すでに紹介したとおり、「子ユニットじゃないけど相手ユニットの相対座標系を使って移動できる」関数を自作したので、これで対処することにします。

テストベッドミッションサンプル

上記のテストのベースにしたミッションを、ソース整理してまとめておきました(実際のテストでは上記関数を一通り試すため、関数をいろいろ書き換え、初期条件もいろいろ変えて試しています)。

  • 方向キー→カメラ位置を円筒座標系で移動
  • ターゲット切り替え→メッセージウィンドウ表示内容切り替え
  • 武装切り替え→テスト進行(FLAK本体の親を変えたりユニットの方位角を回したり。詳細は中身見てください)

特にミッション終了条件など設定していません。リトライや終了をするにはポーズメニューから選択してください。

航空機を子ユニットにするとヤバイ件については、FLAKを呼び出してるcatapult関数で、UNIT_IDを104(FLAK本体)から20(R-21)とかにすれば確認できます。