ponpoko実験室

4.よく使うベクトル用関数、及びベクトル計算

 Quake2は3Dゲームですからプログラミングを行う場合には当然3次元座標の計算が必要となってきます。3次元座標は3つの要素を持つfloat型配列変数vec3_tで扱います。例えばpositionというvec3_t型変数を宣言するならば

vec3_t position;

と宣言すればposition[0]、position[1]、position[2]の各配列へアクセスできます。これはベクトル変数とでも呼びましょう。以下にQuake2のソース内にあらかじめ用意してあるベクトル変数用の基本的な関数及び定義を示します。殆どがたいしたことのない処理なのですが、ソースの記述がスッキリするので使いましょう。


関数群

int VectorCompare (vec3_t v1, vec3_t v2)
 ベクトルv1とv2が等しければtrue(1)、等しくなければfalse(0)を戻り値として得る。

vec_t VectorNormalize (vec3_t v)
 ベクトルvを単位ベクトル(長さ1)へ変換する。

vec_t VectorNormalize2 (vec3_t v, vec3_t out)
 ベクトルvの単位ベクトル(長さ1)をベクトルoutへ得る。

void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc)
 ベクトルvec1とベクトルvec2をscaleでスカラー倍した物の和をベクトルveccへ得る。

vec_t _DotProduct (vec3_t v1, vec3_t v2)
 ベクトルv1とv2のスカラー積(内積)を戻り値vec_tとして得る。

void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out)
 ベクトルvecaとvecbの差をベクトルoutへ得る。

void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out)
 ベクトルvecaとvecbの和をベクトルoutへ得る。

void _VectorCopy (vec3_t in, vec3_t out)
 ベクトルinをoutへコピー。

void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
 ベクトルv1とv2のベクトル積(外積)を戻り値crossとして得る。

vec_t VectorLength(vec3_t v)
 ベクトルvの長さを戻り値vec_tとして得る。

void VectorInverse (vec3_t v)
 ベクトルvを反転させる。

void VectorScale (vec3_t in, vec_t scale, vec3_t out)
 ベクトルinをscaleでスカラー倍した結果をベクトルoutへ得る。


定義群

DotProduct(x,y)
 ベクトルxとyのスカラー積(内積)を戻り値として得る。

VectorSubtract(a,b,c)
 ベクトルaとbの差をベクトルcへ得る。

VectorAdd(a,b,c)
 ベクトルaとbの和をベクトルcへ得る。

VectorCopy(a,b)
 ベクトルaをbへコピー。

VectorClear(a)
 ベクトルaを0ベクトルにする。

VectorNegate(a,b)
 ベクトルaを反転させた結果をベクトルbへ得る。

VectorSet(v,x,y,z)
 ベクトルvの成分にx、y、zを代入。


次はオマケとしてQuake2でよく使うかもしれないベクトル計算の例を示します。高校や大学でやってても今じゃこんな計算忘れちまったっちゅーに!という方は参考にしてください。

●degree値から座標を得る

 playerやmonster、その他動き回るentityは配列anglesによってmodelの向きを 決めています。例えば、あるentityのポインタがentに格納されているとすると 配列anglesへは

ent->s.angles

としてアクセスします。anglesは3つの要素を持つ配列で個々の配列へはangles[PITCH]、 angles[YAW]、angles[ROLL]となっており、PITCHは上下の向き、YAWは左右の向き、ROLLは傾きを表します。 値の範囲は-180〜180のdegree値で、PITCH値が0で丁度playerが真正面を見ている状態であり、上を向かせたければPITCH値を負方向へ変更し、逆に下を向かせたければPITCH値を正方向へ変更します。 同様にYAWの場合、右方向が負で左方向が正です。ROLLについてはめったに使わないので今回調査しませんでしたが、まあ、似たようなものでしょう。
 以下にYAW値及びPITCH値からその単位方向ベクトル(長さが1)を得る例を関数Get_BotsVecで示します。使い道は私の場合、Botの移動方向に何があるのか調査するために使っていました(他の場合は...あんまり使い道ないみたいです)。戻り値としてpositionへ方向ベクトルを得ます。 cosやsinへ渡す値はdegree(-180〜180)ではなくradian(-π〜π)でなければならないので変換している個所があります。

void Get_BotsVec ( edict_t *ent, vec3_t position )
{
	float yaw,pitch;

	yaw = ent->s.angles[YAW];		//entityのYAW値を
	pitch = ent->s.angles[PITCH];		//entityのPITCH値を

	yaw = yaw * M_PI * 2 / 360;		//degreeをradianへ変換
	pitch = pitch * M_PI * 2 / 360;		//degreeをradianへ変換

	position[0] = cos(yaw) * cos(pitch);	//x方向
	position[1] = sin(yaw) * cos(pitch);	//y方向
	position[2] = -sin(pitch);		//z方向
}

水平方向(x,yのみ)のベクトルだけ必要な場合は各ベクトル値の設定部分を
	position[0] = cos(yaw);		//x方向
	position[1] = sin(yaw);		//y方向
	position[2] = 0;		//z方向

とするだけでOKです。実のところ乗算が少ない分処理が軽くなるかな〜等と考えて私は後者を使用し、前者は全く使用していないのでした。
 で、得られた単位ベクトルpositionの使用例ですが、任意の数値distでpositionをスカラー倍してentiyの位置座標ent->s.originへ足すと(直接足すとentityの位置が変わっちゃうので別のベクトル変数にoriginをコピーしてそれに足すのが普通)「entiyの位置座標からentityの向いてる方向へdist離れた場所の座標」が得られる等...。

●ベクトル値からYAWを得る

 逆に、あるベクトル変数からPITCH値やYAW値を得たい場合はどうするのかというとacos(アークコサイン)とasin(アークサイン)を使ってなんとかします。 以下に示す例は与えられたベクトル変数vecの値からYAW値を算出し、結果を戻り値へ得る関数です。

float Get_yaw (vec3_t vec)
{
	vec3_t		out;
	float		yaw;

	VectorNormalize2 (vec, out);			//vecの単位ベクトルをoutへ得る

	yaw = acos((double) out[0]);			//YAW値をradianで得る
	yaw = yaw / M_PI * 180;				//YAW値をdegreeへ変換

	if (asin((double) out[1]) < 0 ) yaw *= -1;	//asinの結果が負ならばYAW値も負でなければならないので
							//符号を反転させる
	return yaw;					//結果を返す
}

PITCH値も同じ要領で得られると思うので、考えてみてくらはい。

e-mail:ponpoko@axcx.com