100日チャレンジ『100日で50弾!』第4回、パターン撃ちです。
画面手前に向かって縦3×横3=9発が何回か発射されます。弾は敵機から発射されるのですから、撃ったあとはだんだん広がっていくのが自然です。だから9発は移動の方向はすべて少しずつ異なっています。gifをどうぞ。

これはDay2の直線弾の撃つ方向を9通りに変化させているだけです。この9発は、パターン(縦3、横3)は同じにあっち向きにもこっち向きにも撃ちたいですよね。その際には、例えば真ん中の弾をもともと(0, 0, 1)方向(これはベクトル)に撃っていたとし、それを(1, 2, 3)方向に撃つようにするわけです。すると、それにともなって他の弾も撃つ向きを同じように(パターンが変わらないように)変えなければなりません。こんな感じに。gifをご覧ください。

ここでは「縦3横3」の「横3」は地面と平行になるようにしています。もしこれを単純に、9つの向きを表すベクトルに対し「あるベクトルの周りに回す」のような操作をするとその正方形が傾いてしまいます(人間で言うと首をかしげる動作みたいに)。そういう演出もあり得ますからもちろんそれでもいいんですが、今回の目的には合いません。横3は地面と平行に、を実現しているのが次のコードです。ぼくはUnityでゲームを書いています。Unityでは採用している座標系は左手系(通常、数学で使うのは右手系)なので注意が必要です。
//-------------------
//左右には傾けず(ロールさせない)、
//dirをdir1に向ける動きでvを動かす
//
Vector3 rotVecUp(Vector3 dir, Vector3 dir1, Vector3 v) {
Vector3 dirh, dir1h, dir2, w;
dirh = dir;
dirh.y = 0;
dir1h = dir1;
dir1h.y = 0;
float s = Vector3.SignedAngle(dirh, dir1h, Vector3.up);
w = Vector3.Cross(dir1, dir1h).normalized;
dir2 = rotVec(dir, Vector3.up, s * d2r);
float s1 = Vector3.SignedAngle(dir2, dir1, w);
Vector3 v1 = rotVec(v, Vector3.up, s * d2r);
Vector3 v2 = rotVec(v1, w, s1 * d2r);
return v2;
}
//-------------------
コード中のd2rは角度の度をラジアンに変換するための係数です(d2r = Mathf.Deg2Rad)。同様にr2dはラジアンを度に変換するための係数です(r2d = Mathf.Rad2Deg)。またrotVec()はロドリゲスの回転公式を用いてベクトルを回転させます。この公式についてはブログでも触れたことがあります。
コードは以下。
//-------------------------------
//ロドリゲスの回転公式
//r: 回転させたいベクトル
//n: 単位ベクトル。回転の軸
//k: 回転角
//nが指す方から見て(ドット)右回りにkラジアン回す
//
Vector3 rotVec(Vector3 r, Vector3 n, float k) {//999
float c = (float)Math.Cos(k);
Vector3 s = c * r + (1 - c) * Vector3.Dot(r, n) * n;
s += Vector3.Cross(n, r) * (float)Math.Sin(k);
return s;
}
//-------------------------------
人間は弱いものです。しかし「100日チャレンジ」と銘打って始めると、簡単にはやめられず、続きそうな気がします。書いたコードはあとで使えるように保存していますし、ゲーム製作に役立ちそう! 50種類もあればひと財産です。