Hatena::Groupfatalemployeetraining

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

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

2010-10-31

RSEの仕様について調べてみた

set_pos, get_pos, set_rot, get_rot, rel_rotまわりの動きがよくわからなかったので、これらの関数トライアルするだけのミッションをくんでいろいろ試してみた。得られた知見を以下にまとめておく。


  • 世界座標系は、x軸が東西方向東向き正、y軸が鉛直方向上向き正、z軸が南北方向北向き正の左手座標系になっている。内部がDirectXで書かれているので自然な流れだけど、よく数学で出てくる右手座標系と鏡像反転なので注意が要る。
    • マップの中心座標はx=2500,z=2500(ひょっとするとx=2499,z=2499?)。
    • マップエリアはx=0~5000、z=0~5000。そのうちコースアウト領域はx<=1000、x>=4000、z<=1000、z>=4000(情報ありがとうございます、JoJoさん)。
  • 機体に割り付けられた座標系は進行方向(機体の軸方向)がz軸、機体上方がy軸、ピッチ回転軸(左から右に向かう方向が正)がx軸。
  • どのユニットの子ユニットでもないユニットは、child_flagに何を設定しようが、child_flag=FALSE(=0)の場合の振る舞いになるようだ。
  • 姿勢回転角は以下のようになっている。
    • x軸回転角は、機体に割りつけられた座標系におけるx軸(=すなわちピッチ軸)周りの回転で、ピッチアップ(機首上げ)する方向が正。単位は[deg](度)。
    • y軸回転角は、機体に割りつけられた座標系ではなく、機体座標系を通る鉛直軸(上方を正)とする軸周りの回転。すなわち方位角になる。機体を上空から見て、時計回りが正(機体後ろから見て右に機体が回るのが正)。単位は[deg](度)。
    • z軸回転角は、機体に割りつけられたz軸(すなわち機体軸、ロール軸)周りの回転。すなわちロール角になる。機体後ろから見て反時計回りに回るのが正。単位は[deg](度)。
    • 物体の姿勢を3軸の回転で表記すると、一般にその回転をかける順番で結果が異なる。RaidersSphereの場合、ピッチ軸回転→方位角回転→ロール軸回転の順で行われているようだ。すなわち、"set_rot(LABEL, rx, ry, rz, FALSE);"と、"set_rot(LABEL, 0, 0, 0, FALSE); rel_rot(LABEL, rx, 0, 0, FALSE); rel_rot(LABEL, 0, ry, 0, FALSE); rel_rot(LABEL, 0, 0, rz, FALSE);"は同じ。
    • 絶対座標系の軸で回転させて同じことを実現するためには、まずz軸周りにあらかじめロールさせておき、次にx軸周りにピッチアップ、最後にy軸周りに回して方位設定となる。
    • とすると実は、ピッチアップ90度してからヨー90度しよう、などと考えると回転できない特異状態になっていることがわかる(ロール軸と方位角軸が一致してしまうため)。その場合、望む姿勢にするには別の回し方をしないといけない。つーことで「ヨー軸回転」専用の関数を作ると便利そう。>作ってみた。
  • system.incでシステムコール関数として三角関数が用意されているが、これらの取り扱う角度の単位は[rad](C言語三角関数と同じ)。get_rot, set_rot, rel_rotなどで扱う単位は[deg]なので、以下の換算を意識して使う。
    • 円周率を"const float PI = 3.1415926535897932384626433832795;"などとしておく。(floatはこんなに精度でないが、値を電卓からコピーしている)
    • deg_value[deg]->rad_value[rad]の場合: "rad_value = deg_value * PI / 180.0;"
    • rad_value[rad]->deg_value[deg]の場合: "deg_value = rad_value * 180.0 / PI;"
    • system.incにatan2があるのはめちゃくちゃありがたい。(さっそくあとで使います)
  • テストしていて気がついたことだが、set_sectionでセクション値を200にし、set_speedで速度を0にしていても、ユニットの姿勢を水平じゃない姿勢に設定したまま何もせず放置しておくと、重力の影響が計算されるのか?、徐々に姿勢が変化していってしまう。これはユニットがFIGHTER,PLANE属性ではなくFACILITY属性などでも発生する。そのため、思い通りの姿勢を維持させたい場合は常時set_rotなどで姿勢を固定しておく必要がある。(位置は変化しない。)

たとえばこんなヨー軸回転

ヨー軸回転を実現するために、rel_rot_yaw(int label, float yaw_angle, int child_flag)って関数を作ってみた。ちょっと仰々しすぎて、なにかもっと楽に計算する手がある気もしますが…

以下スクリプト

const float PI = 3.1415926535897932384626433832795; //円周率

/**
 * valueの平方根を計算します。
 * @param	float value	引数
 * @return	float	valueの平方根
 * @caption	平方根を計算する
 * @see		systemcall float pow(float x, float p);
 */
function sqrt(float value)
{
	return( pow(value,0.5) );
}

/**
 * 与えられたベクトル(x,y,z)を、x軸周りにrx[deg]だけ回転したベクトル(result_x,result_y,result_z)を求めます。
 * @param	float rx	x軸周りに回転する回転角[deg]
 * @param	float x	元のベクトルのx成分
 * @param	float y	元のベクトルのy成分
 * @param	float z	元のベクトルのz成分
 * @param	refer result_x	回転したベクトルのx成分
 * @param	refer result_y	回転したベクトルのy成分
 * @param	refer result_z	回転したベクトルのz成分
 * @return	int	常に0
 * @caption	ベクトルをx軸まわりに回転する
 * @see		systemcall float sin(float x);
 * @see		systemcall float cos(float x);
 */
function _rot_x_axis(float rx, float x, float y, float z, refer result_x, refer result_y, refer result_z)
{
	//ベクトルに回転行列をかける
	result_x = 1.0*x + 0.0*y + 0.0*z;
	result_y = 0.0*x + cos(rx/180.0*PI)*y + sin(rx/180.0*PI)*z;
	result_z = 0.0*x - sin(rx/180.0*PI)*y + cos(rx/180.0*PI)*z;
	return 0;
}

/**
 * 与えられたベクトル(x,y,z)を、y軸周りにry[deg]だけ回転したベクトル(result_x,result_y,result_z)を求めます。
 * @param	float ry	y軸周りに回転する回転角[deg]
 * @param	float x	元のベクトルのx成分
 * @param	float y	元のベクトルのy成分
 * @param	float z	元のベクトルのz成分
 * @param	refer result_x	回転したベクトルのx成分
 * @param	refer result_y	回転したベクトルのy成分
 * @param	refer result_z	回転したベクトルのz成分
 * @return	int	常に0
 * @caption	ベクトルをy軸まわりに回転する
 * @see		systemcall float sin(float x);
 * @see		systemcall float cos(float x);
 */
function _rot_y_axis(float ry, float x, float y, float z, refer result_x, refer result_y, refer result_z)
{
	//ベクトルに回転行列をかける
	result_x = cos(ry/180.0*PI)*x + 0.0*y + sin(ry/180.0*PI)*z;
	result_y = 0.0*x + 1.0*y + 0.0*z;
	result_z = -sin(ry/180.0*PI)*x + 0.0*y + cos(ry/180.0*PI)*z;
	return 0;
}

/**
 * 与えられたベクトル(x,y,z)を、z軸周りにrz[deg]だけ回転したベクトル(result_x,result_y,result_z)を求めます。
 * @param	float rz	z軸周りに回転する回転角[deg]
 * @param	float x	元のベクトルのx成分
 * @param	float y	元のベクトルのy成分
 * @param	float z	元のベクトルのz成分
 * @param	refer result_x	回転したベクトルのx成分
 * @param	refer result_y	回転したベクトルのy成分
 * @param	refer result_z	回転したベクトルのz成分
 * @return	int	常に0
 * @caption	ベクトルをz軸まわりに回転する
 * @see		systemcall float sin(float x);
 * @see		systemcall float cos(float x);
 */
function _rot_z_axis(float rz, float x, float y, float z, refer result_x, refer result_y, refer result_z)
{
	//ベクトルに回転行列をかける
	result_x = cos(rz/180.0*PI)*x - sin(rz/180.0*PI)*y + 0.0*z;
	result_y = sin(rz/180.0*PI)*x + cos(rz/180.0*PI)*y + 0.0*z;
	result_z = 0.0*x + 0.0*y + 1.0*z;
	return 0;
}

/**
 * 与えられたベクトル(x,y,z)を、x軸周りにrx[deg]だけ回転します。
 * @param	float rx	x軸周りに回転する回転角[deg]
 * @param	refer x	ベクトルのx成分
 * @param	refer y	ベクトルのy成分
 * @param	refer z	ベクトルのz成分
 * @caption	ベクトルをx軸まわりに回転する(破壊的)
 * @see		function _rot_x_axis(float rx, float x, float y, float z, refer result_x, refer result_y, refer result_z);
 */
function rot_x_axis(float rx, refer x, refer y, refer z)
{
	int rtn;
	float result_x, result_y, result_z;
	rtn = _rot_x_axis(rx, x, y, z, result_x, result_y, result_z);
	x = result_x; y = result_y; z = result_z;
	return rtn;
}

/**
 * 与えられたベクトル(x,y,z)を、y軸周りにry[deg]だけ回転します。
 * @param	float ry	y軸周りに回転する回転角[deg]
 * @param	refer x	ベクトルのx成分
 * @param	refer y	ベクトルのy成分
 * @param	refer z	ベクトルのz成分
 * @caption	ベクトルをy軸まわりに回転する(破壊的)
 * @see		function _rot_y_axis(float ry, float x, float y, float z, refer result_x, refer result_y, refer result_z);
 */
function rot_y_axis(float ry, refer x, refer y, refer z)
{
	int rtn;
	float result_x, result_y, result_z;
	rtn = _rot_y_axis(ry, x, y, z, result_x, result_y, result_z);
	x = result_x; y = result_y; z = result_z;
	return rtn;
}

/**
 * 与えられたベクトル(x,y,z)を、z軸周りにrz[deg]だけ回転します。
 * @param	float rz	z軸周りに回転する回転角[deg]
 * @param	refer x	ベクトルのx成分
 * @param	refer y	ベクトルのy成分
 * @param	refer z	ベクトルのz成分
 * @caption	ベクトルをz軸まわりに回転する(破壊的)
 * @see		function _rot_z_axis(float rz, float x, float y, float z, refer result_x, refer result_y, refer result_z);
 */
function rot_z_axis(float rz, refer x, refer y, refer z)
{
	int rtn;
	float result_x, result_y, result_z;
	rtn = _rot_z_axis(rz, x, y, z, result_x, result_y, result_z);
	x = result_x; y = result_y; z = result_z;
	return rtn;
}

/**
 * 機体座標系のy軸ベクトルj(jx,jy,jz)とz軸ベクトルk(kx,ky,kz)を与え、機体の姿勢(ピッチ角rx,方位角ry,ロール角rz)を求めます。
 * @param	float jx	機体座標系y軸ベクトルのx成分
 * @param	float jy	機体座標系y軸ベクトルのy成分
 * @param	float jz	機体座標系y軸ベクトルのz成分
 * @param	float kx	機体座標系z軸ベクトルのx成分
 * @param	float ky	機体座標系z軸ベクトルのy成分
 * @param	float kz	機体座標系z軸ベクトルのz成分
 * @return	int	常に0
 * 
 * @caption	機体座標軸のベクトル2つを元に機体姿勢角を求める
 * @see		systemcall float atan2(float y, float x);
 * @see		function rot_x_axis(float rx, refer x, refer y, refer z);
 */
function calc_rxryrz(float jx, float jy, float jz, float kx, float ky, float kz, refer rx, refer ry, refer rz)
{
	// ベクトルkが機体軸方向。ピッチ角と方位角を求められる
	rx = atan2(ky,sqrt(kx*kx+kz*kz)) * 180.0/PI;	// ピッチ角を求める
	ry = atan2(kx,kz) * 180.0/PI;					// 方位角を求める
	
	// 求めた方位角とピッチ角を使い、ベクトルk, ベクトルjを逆に回転する。
	// すなわち、-ryだけ方位角を回転してから、-rxだけピッチ角を回転する。
	// これによりベクトルkがz軸に一致する。
	// 実際に必要な数値はベクトルjの回転結果なので、ベクトルjのみ計算する。
	rot_y_axis(-ry,jx,jy,jz); // ベクトルjを方位角-ry回転
	rot_x_axis(-rx,jx,jy,jz); // ベクトルjをピッチ角-rx回転
	
	// 回転後のベクトルjが、y軸に対しどれだけ傾いているかがロール角。
	rz = atan2(-jx,jy) * 180.0/PI; // ロール角を求める
	
	return 0;
}

/**
 * 指定したヨー角だけ機体を回転させます。
 * @param	int label	ヨー回転させたいユニットのラベル
 * @param	float yaw_angle	ヨー回転角[deg]
 * @param	int child_flag	子ユニットフラグ(1=親ユニットの機体座標系を基準とする 0=絶対座標系を基準とする)
 * 
 * @caption	指定したヨー角だけ機体を回転させる。
 * @see		systemcall int get_rot(int label, refer rx, refer ry, refer rz);
 * @see		function rot_x_axis(float rx, refer x, refer y, refer z);
 * @see		function rot_y_axis(float rx, refer x, refer y, refer z);
 * @see		function rot_z_axis(float rx, refer x, refer y, refer z);
 * @see		function calc_rxryrz(float jx, float jy, float jz, float kx, float ky, float kz, refer rx, refer ry, refer rz);
 * @see		systemcall int set_rot(int label, float rot_x, float rot_y, float rot_z, int child_flag);
 */
function rel_rot_yaw(int label, float yaw_angle, int child_flag)
{
	float rx, ry, rz; // 機体姿勢角
	float jx, jy, jz; // 機体座標系のy軸ベクトルj
	float kx, ky, kz; // 機体座標系のz軸ベクトルk
	
	// まずユニットの元々の姿勢角を得る
	get_rot(label, rx, ry, rz);
	
	// ヨー回転したあとの機体座標系のy軸ベクトルj,z軸ベクトルkを求める。
	// そのために、
	// 1)絶対座標系と同じ向きにベクトルj,kを初期化
	// 2)そのまま方位角を、与えられたヨー角だけ回す
	// 3)絶対座標系z軸まわりにrz回す(さきにロールさせておく)
	// 4)絶対座標系x軸まわりにrx回す(次にピッチアップする)
	// 5)絶対座標系y軸まわりにry回す(最後に方位回す)
	// とする。
	jx = 0.0; jy = 1.0; jz = 0.0;
	rot_y_axis(yaw_angle, jx, jy, jz);
	rot_z_axis(rz, jx, jy, jz);
	rot_x_axis(rx, jx, jy, jz);
	rot_y_axis(ry, jx, jy, jz);
	
	kx = 0.0; ky = 0.0; kz = 1.0;
	rot_y_axis(yaw_angle, kx, ky, kz);
	rot_z_axis(rz, kx, ky, kz);
	rot_x_axis(rx, kx, ky, kz);
	rot_y_axis(ry, kx, ky, kz);
	
	// 求めたベクトルj,kより、新しい機体姿勢角を得る
	calc_rxryrz(jx,jy,jz,kx,ky,kz,rx,ry,rz);
	
	// 新しい機体姿勢角で姿勢を設定する
	return(set_rot(label, rx, ry, rz, child_flag));
}

たとえばこんな座標計算

たとえば「カメラを、撮影対象の機体の後ろ5m、高さ2mに置きたいなー」なんて思うことありますよね。

この間まで自分でエクセルで座標計算してplcファイルに手打ちしてたんですが…

  • function set_rel_pos_xyz_abs(int label, int label_rel_axis, float x, float y, float z);
    • ユニット(label)を、別のユニット(label_rel_axis)の持つ機体座標系(xyz座標系)で表される座標(x,y,z)に移動する。姿勢はユニット(label_rel_axis)の姿勢と同じとなる。
  • function set_rel_pos_cyl_abs(int label, int label_rel_axis, float r, float theta, float y);
    • ユニット(label)を、別のユニット(label_rel_axis)の持つ機体座標系(y軸方向にのびる円筒座標系)で表される座標(r,theta,y)に移動する。姿勢はユニット(label_rel_axis)の姿勢と同じとなる。

こんな関数作ってみました。

互いに親子関係にないのに、相手の機体座標系使って位置設定ができるのがポイント。もちろん親子関係にありませんから、位置設定後ユニット(label_rel_axis)が動いてもユニット(label)が自動でついていくことはありません。ここは注意。

なお、label_rel_axisなるユニットは何かの子ユニットではない前提で関数を組んであります(get_pos関数の仕様の問題。get_abs_posでもあればなー)。

以下スクリプト。先ほどのヨー軸回転関数用に作った定数や関数を併用するので、先ほどのスクリプトに書き加える形で使ってください。

/**
 * 絶対座標系に対して与えられた姿勢角(rx,ry,rz)を向いている相対座標系基準のベクトル(x,y,z)を、
 * 絶対座標系でのベクトル表現(result_x, result_y, result_z)に変換します。
 * @param	float rx	座標系のピッチ角[deg]
 * @param	float ry	座標系の方位角[deg]
 * @param	float rz	座標系のロール角[deg]
 * @param	float x		ベクトルのx成分。相対座標系での値
 * @param	float y		ベクトルのy成分。相対座標系での値
 * @param	float z		ベクトルのz成分。相対座標系での値
 * @param	refer result_x		ベクトルの絶対座標系でのx成分。
 * @param	refer result_y		ベクトルの絶対座標系でのy成分。
 * @param	refer result_z		ベクトルの絶対座標系でのz成分。
 * 
 * @caption	与えられた姿勢角の座標系におけるベクトルを、絶対座標系でのベクトルに変換する
 * @see		function rot_x_axis(float rx, refer x, refer y, refer z);
 * @see		function rot_y_axis(float rx, refer x, refer y, refer z);
 * @see		function rot_z_axis(float rx, refer x, refer y, refer z);
 */
function _calc_rel_pos(float rx, float ry, float rz, float x, float y, float z, refer result_x, refer result_y, refer result_z)
{	
	// 1)z軸まわりにrz回す(さきにロールさせておく)
	// 2)x軸まわりにrx回す(次にピッチアップする)
	// 3)y軸まわりにry回す(最後に方位回す)
	retult_x = x; retult_y = y; retult_z = z;
	rot_z_axis(rz, result_x, result_y, result_z);
	rot_x_axis(rx, result_x, result_y, result_z);
	rot_y_axis(ry, result_x, result_y, result_z);
	
	return 0;
}


/**
 * 絶対座標系に対して与えられた姿勢角(rx,ry,rz)を向いている相対座標系基準のベクトル(x,y,z)を、
 * 絶対座標系でのベクトルに変換します。
 * @param	float rx	座標系のピッチ角[deg]
 * @param	float ry	座標系の方位角[deg]
 * @param	float rz	座標系のロール角[deg]
 * @param	refer x		ベクトルのx成分。相対座標系での値を入れて渡すと絶対座標系での値に換算される
 * @param	refer y		ベクトルのy成分。相対座標系での値を入れて渡すと絶対座標系での値に換算される
 * @param	refer z		ベクトルのz成分。相対座標系での値を入れて渡すと絶対座標系での値に換算される
 * 
 * @caption	与えられた姿勢角の座標系におけるベクトルを、絶対座標系でのベクトルに変換する(破壊的)
 * @see		function _calc_rel_pos(float rx, float ry, float rz, float x, float y, float z, refer result_x, refer result_y, refer result_z);
 */
function calc_rel_pos(float rx, float ry, float rz, refer x, refer y, refer z)
{
	// 1)z軸まわりにrz回す(さきにロールさせておく)
	// 2)x軸まわりにrx回す(次にピッチアップする)
	// 3)y軸まわりにry回す(最後に方位回す)
	float temp_x, temp_y, temp_z;
	temp_x = x; temp_y = y; temp_z = z; 
	_calc_rel_pos(rx, ry, rz, temp_x, temp_y, temp_z, x, y, z);
	return 0;
}


/**
 * ラベル番号labelのユニットを、ラベル番号label_rel_axisの持つ機体座標系基準で、座標(x,y,z)の位置に設置します。
 * (向きはラベル番号label_rel_axisの機体にそろえます)
 * @param	int label	移動させたいユニットのラベル
 * @param	int label_rel_axis	基準とする座標系をもつユニット(何かの子ユニットではないことが必要)
 * @param	float x		ベクトルのx成分。相対座標系での値。label_rel_axisの左右方向。右が正。
 * @param	float y		ベクトルのy成分。相対座標系での値。label_rel_axisの上下方向。上方向正。
 * @param	float z		ベクトルのz成分。相対座標系での値。label_rel_axisの機体軸方向。進行方向が正
 * 
 * @caption	ユニットの位置を、ほかのユニットの機体座標系(xyz)基準で設定する
 * 
 * @see		systemcall int get_pos(int label, refer x, refer y, refer z);
 * @see		systemcall int get_rot_abs(int label, refer rx, refer ry, refer rz);
 * @see		function calc_rel_pos(float rx, float ry, float rz, refer x, refer y, refer z);
 */
function set_rel_pos_xyz_abs(int label, int label_rel_axis, float x, float y, float z)
{
	float px, py, pz; // 機体座標(機体座標系原点の絶対座標系での所在)
	float rx, ry, rz; // 機体姿勢角(機体座標系の絶対座標系に対する姿勢)
	
	// 
	get_pos(label_rel_axis, px, py, pz);
	get_rot_abs(label_rel_axis, rx, ry, rz);
	
	calc_rel_pos(rx, ry, rz, x, y, z);
	
	set_pos(label, px, py, pz, FALSE);
	move(label,x,y,z,FALSE);
	
	set_rot(label,rx,ry,rz,FALSE);
	
	return 0;
}

/**
 * ラベル番号labelのユニットを、ラベル番号label_rel_axisの持つ機体座標系(上下方向に伸びる円筒座標系)基準で
 * 座標(r,theta,y)の位置に設置します。
 * (向きはラベル番号label_rel_axisの機体にそろえます)
 * @param	int label	移動させたいユニットのラベル
 * @param	int label_rel_axis	基準とする座標系をもつユニット(何かの子ユニットではないことが必要)
 * @param	float r		相対座標系での(xz平面における)半径方向座標値。原点から離れれば離れるほど大。
 * @param	float theta	相対座標系での(xz平面における)方位角。[deg]
 * @param	float y		相対座標系でのy座標値(円筒軸方向座標値) 。label_rel_axisの上下方向。上方向正。
 * 
 * @caption	ユニットの位置を、ほかのユニットの機体座標系(円筒座標系)基準で設定する
 * 
 * @see		systemcall float sin(float x);
 * @see		systemcall float cos(float x);
 * @see		function set_rel_pos_xyz_abs(int label, int label_rel_axis, float x, float y, float z);
 */
function set_rel_pos_cyl_abs(int label, int label_rel_axis, float r, float theta, float y)
{
	float x, z; // 引数に与えられた座標の、機体xyz座標系におけるx,z座標値
	
	//円筒座標からxyz座標へ変換。y値はそのまま。
	z = r * cos(theta * PI/180.0);
	x = r * sin(theta * PI/180.0);
	
	set_rel_pos_xyz_abs(label, label_rel_axis, x, y, z);
	
	return 0;
}