前回は、画面上にキャノンをを配置した。キャノンをを配置したからには、砲弾を撃たなくてはいけない。今回は、敵めがけて砲弾を撃ってみよう。
弾道の角度を求める
砲弾を撃つために必要な情報を考えてみよう。まず前提として、砲弾はキャノンをから敵に向かって一直線に撃つものとする。ということは、砲弾を発射するタイミングでの、キャノンをの位置と敵の位置が分かれば、充分なはずだ。砲弾の初期位置はキヤノンの座標になり、そこからステップ毎に敵に近づけていく。
では、1ステップにおける砲弾の位置の増加分はどうやって計算するのだろうか。このときに必要となるのが、キャノンをと敵との間の角度である。角度の計算は、今回の場合は2点の座標が分かっているので、アークタンジェントを使う。角度さえ分かってしまえば、sinおよびcosを使って、増加分を計算することができるはずだ。
この計算式を実装したソースコードを紹介しよう。ここでは、cannonX、cannonYがキャノンをの座標、enemyX、enemyYが敵の座標、shellX、shellYが砲弾の座標、shellThetaが弾道の角度、になっている。
List 1.
// 砲弾の初期位置を設定する
shellX[i] = cannonX[i];
shellY[i] = cannonY[i] - 0.15f;
// 打ち出す角度を計算する
float dx, dy, th;
dx = enemyX - shellX[i];
dy = enemyY - shellY[i];
if (dx > 0) {
th = atan(dy / dx);
}
else if (dx < 0) {
th = M_PI - atan(dy / -dx);
}
else {
if (dy > 0) {
th = M_PI_2;
}
else {
th = -M_PI_2;
}
}
shellTheta[i] = th;
まず、砲弾の初期位置を決定する。キャノンをの少し下あたりの座標を使っている。続いて、砲弾の初期位置と、敵の座標との差分を取る。この差分の値から、アークタンジェントを使い、角度を求めているのだ。注意点としては、アークタンジェントはxが正のときしか定義されないので、それ以外のときは場合分けが必要になる。
これで角度が求まった。
砲弾を撃つ
角度を求めるソースコードを使って、砲弾を撃つためのメソッドを紹介しよう。
「砲弾を撃つ」という処理は、まず初期処理として、先ほどのソースコードを使い初期位置と角度を決定する。その後は、砲弾を描き、座標を更新する。この処理を繰り返すことで、砲弾が飛んでいく。そして、砲弾が画面外に出たら、その砲弾を破棄し、また新しい砲弾を作ることになる。
このような処理を行うメソッドを実装してみた。
List 2.
- (void)drawShell
{
int i;
for (i = 0; i < 3; i++) {
// 砲弾を描く
if (shellX[i] != 1000.0f && shellY[i] != 1000.0f) {
[self drawShellAtX:shellX[i] Y:shellY[i]];
}
// 砲弾を描かない場合
else {
// 砲弾の初期位置を設定する
(List 1.に同じ)
...
}
// 座標を更新する
shellX[i] += 0.05f * cos(shellTheta[i]);
shellY[i] += 0.05f * sin(shellTheta[i]);
// 画面外に出たかどうか確認する
if (shellX[i] < -1.0f || shellX[i] > 1.0f ||
shellY[i] < -1.5f || shellY[i] > 1.5f)
{
// 砲弾の位置を無効化する
shellX[i] = 1000.0f;
shellY[i] = 1000.0f;
}
}
}
ここでは砲弾が3つあるので、その数だけループを回している。
shellXとshellYには砲弾の座標が入っているのだが、無効な座標を表す値として1000.0fを使っている。この値が入っている場合、砲弾の初期一設定と角度の計算を行う。それ以外の場合は、drawShellAtX:Y:という砲弾を描画するためのメソッドを呼び出している。
その次に続くのが、砲弾座標の更新だ。角度を表すshellThetaの値を使って、sin関数とcos関数で増分を計算している。関数の返り値にかけている値で、砲弾の速度を調節することができる。
最後に、砲弾が画面外に出たかどうかの確認を行っている。出た場合は、位置を無効化しておく。次回呼び出されたときに、再び初期値を設定することになるだろう。
これで、砲弾を撃つことになる。
まだ敵の当たり判定を入れていないので砲弾が素通りしてしまうが、きちんと敵を迎え撃つだろう。
次回は、敵の当たり判定を加えて、ゲームとして動作するようにしてみる。