いよいよ敵キャラクタを動かしてみよう。このゲームでは道に沿って敵が攻めてくるので、まずは道の設定を行おう。

道の設定

タワーディフェンスゲームでは、画面に引かれた道に沿って敵が攻めてくる。道が曲がりくねっていれば、それだけ攻撃機会も多くなり、プレイヤー側が有利になる。

今回のアプリでは、道はあらかじめ背景画像に描き込んである。この道に沿わせるためには、別途道についての情報を定義しておく必要があるだろう。

道のための設定として、道の始点と終点、および曲がるポイントを記述しよう。ポイントの位置は次の画像のようになる。座標は、このアプリが使っているOpenGLの座標系にあわせてある。

これらの点を、配列として保持することにしよう。

List 3.

// 道の曲がるポイント
GLfloat roadPoints[] = {
    0.52f, -1.6f,
    0.52f, -0.75f,
    -0.69f, -0.75f,
    -0.69f, 0.45f,
    -0.09f, 0.45f,
    -0.09f, 0.05f,
    0.71f, 0.05f,
    0.71f, 1.05f,
    -0.49f, 1.05f,
    -0.49f, 1.6f,
};

敵は、配列が持っている最初のポイントに登場し、次のポイントを目指して移動する。目的のポイントに到達したら、次のポイントを目指す。これを繰り返しながら、終点まで移動させていくのだ。これで、敵が移動する道が定義できたことになる。

敵キャラクタを動かす

この道の設定を使って、敵を動かすプログラムを考えてみよう。

道に沿って動かすには、いま現在、敵がどのポイントからどのポイントへ向かって歩いているかを管理する必要がある。このために使うのが、前回インスタンス変数として定義したenemyRoadIndexだ。この値には、最後に通過したポイントを指定する。enemyRoadIndexが0であれば、始点から、次のポイントへと向かっている。1であれば、最初の曲り道のポイントから、次のポイントへと向かっているわけだ。

このenemyRoadIndexの値を使うことで、前のポイントと次のポイントを取得することができる。このポイントの差分を見れば、敵をどちらの方向に動かせばいいか分かるだろう。そこで、アニメーションのステップごとに、次のポイントに向けて敵を少しずつ動かすことになる。これで、敵が道に沿って歩いて行く。

では、ソースコードを紹介しよう。

List 4.

    // ステップを増加する
    enemyStep++;
    if (enemyStep % 8 == 0) {
        // 画像を更新する
        enemyImage = enemyImage == 0 ? 1 : 0;

        // 道のポイントを取得する
        GLfloat roadX, roadY;
        GLfloat roadNextX, roadNextY;
        roadX = roadPoints[enemyRoadIndex * 2];
        roadY = roadPoints[enemyRoadIndex * 2 + 1];
        roadNextX = roadPoints[(enemyRoadIndex + 1) * 2];
        roadNextY = roadPoints[(enemyRoadIndex + 1) * 2 + 1];

        // 座標を更新する
        BOOL    goNextPoint = NO;
        GLfloat speed = 0.02f;
        if (roadNextX > roadX) {
            enemyX += speed;

            if (enemyX >= roadNextX) {
                enemyX = roadNextX;
                goNextPoint = YES;
            }
        }
        else if (roadNextX < roadX) {
            enemyX -= speed;

            if (enemyX <= roadNextX) {
                enemyX = roadNextX;
                goNextPoint = YES;
            }
        }

        if (roadNextY > roadY) {
            enemyY += speed;

            if (enemyY >= roadNextY) {
                enemyY = roadNextY;
                goNextPoint = YES;
            }
        }
        else if (roadNextY < roadY) {
            enemyY -= speed;

            if (enemyY <= roadNextY) {
                enemyY = roadNextY;
                goNextPoint = YES;
            }
        }

        // 次のポイントを決定する
        if (goNextPoint) {
            enemyRoadIndex++;

            if (enemyRoadIndex == sizeof(roadPoints) / sizeof(GLfloat) / 2 - 1) {
                enemyRoadIndex = 0;
                enemyX = roadPoints[0];
                enemyY = roadPoints[1];
            }
        }
    }
}

まず始めに、enemyStepの値を増加している。この値を使い、8ステップごとに敵を1歩動かす処理を行っている。これは、アニメーションの各ステップで敵を動かす処理をやってしまうと、敵の動きがものすごく速くなってしまうためだ。この値を使って、速度の調整を行なっている。

敵を動かす処理では、道のポイントを2つ取得している。前のポイントと、次のポイントだ。これらの値から、敵のx座標とy座標をどの程度動かすか計算している。

動いた結果次のポイントに到達したと判断したら、その次のポイントへと動かすようにする。これは、enemyRoadIndexの値を増やすことで実現できる。

このようなソースコードで、道に沿って敵が歩いて行くだろう。ただし、横に歩いたり、下へ向かって歩くときも、上へ向かって歩いている画像になっている。これは、画像の用意が間に合わなかったためだ。それぞれの方向に向かって歩く画像を用意すれば、さらに完成度が高くなるだろう。

ここまでのソースコード: Defense-2.zip