私たちが手作業で同じタスクを何度も繰り返すとき、一般的にはミスが発生しやすく、時間もかかります。 しかし、プログラムにとって繰り返しは得意分野です。同じタスクを一度設定すれば、その後は何度でも高速で正確に処理を行うことができます。
この「繰り返し」をプログラミングで実現するために用意されている制御文の一つが for 文です。
for 文とは
for 文は、指定した一定の回数だけ、同じ処理を繰り返すための構文です。 タスクを一つずつリストから取り出して実行するような感じです。
例えばタスクを「聴きたい曲」とし、それがプレイリストに何曲も入っているとしましょう。 for 文はこのプレイリストに入っている曲を、一曲ずつ順番に再生させるような処理を作ることが出来ます。
for 文の基本的な書き方
for 文の基本的な書き方は以下の通りです。
for (初期化式; 条件式; インクリメントまたはデクリメント) { // 実行する処理 }
このままですと分かりにくいので、実際にサンプルコードを利用して解説していきましょう。
for (int i = 0; i < 10; i++) { Debug.Log(i); }
各処理の説明です。
int i = 0;
これが初期化式です。ループが開始する前に一度だけ実行されます。この例では変数 i を 0 で初期化しています。代入する値にはメンバ変数やローカル変数を指定することも可能です。
i < 10;
これが条件式です。各ループの実行前に評価され、その結果がtrueであればループが続けられます。この例では変数 i が10より小さい場合にループを続けます。実数ではなく、メンバ変数やローカル変数を指定することも可能です。
i++
これがインクリメントです。各ループの最後に実行されます。この例では変数 i を1増やしています。
このfor文によって、Debug.Log(i);の処理が10回繰り返され、0から9までの数字がログに出力されます。 音楽に例えるなら、プレイリストの10曲を順番に再生する音楽プレーヤーのような処理です。
for 文の読み解き方
プログラムの処理は、上の行から下の行に、そして左側に書かれている処理から順番に右側へと処理されていきます。
ただし、for 文の場合は少し違います。
下記に処理の順番について番号を振りました。
for (① int i = 0;② i < 10; ④ i++) {
③Debug.Log(i);
}
いかがでしょうか。通常の処理と違いますね。
for 文内に書かれているインクリメント/デクリメントの部分が、最後に実行されることになっています。 処理の繰り返しがあるため、for 文内が、左から右の順番になっていないことにも注目してください。 ③の処理が終わってから、④の処理が行われます。
この処理をフローチャートにすると、このようになります。
最初だけは① → ② → ③ → ④ と処理されます。 その後、①には戻らず、②から処理を再開し、② → ③ → ④ と処理されます。
②条件の判定が true であれば③ → ④ と処理されます。 そのため、処理を1回行いつつ、ループした回数もカウントされます。 そして、②条件の判定が true であり続けるかぎり、② → ③ → ④ のループ処理を行います。
②条件の判定が false の場合、そこで for 文は終了します。ブロックから抜けて、次の処理に移ります。
このような処理の繰り返しによって、for 文は、ループする回数をカウントしつつ、処理を繰り返す仕組みになっています。
for 文は、処理の順番を理解することが大変重要です。
この①~④の順番を頭に入れて、for 文の処理を読み解いていくようにしてください。
for 文と他の制御構文の組み合わせ
for 文は他の制御構文と組み合わせて使うことで、プログラムを構築していくことが可能です。 特に if 文や switch 文との組み合わせはよく使われます。
以下にfor文を各制御構文に組み合わせたサンプルコードを示します。
if 文との組み合わせ
if 文との組み合わせると、ステージ上にランダムな位置に敵を生成するといった場面で利用できます。
public GameObject enemyPrefab; for (int i = 0; i < 10; i++) { float randomX = Random.Range(-10f, 10f); // X座標をランダムに生成 float randomZ = Random.Range(-10f, 10f); // Z座標をランダムに生成 if (randomX > -2f && randomX < 2f) { // プレイヤー近くに生成された場合 Debug.Log("位置の再計算"); i--; // カウンターを減らして、再生成する continue; } Instantiate(enemyPrefab, new Vector3(randomX, 0, randomZ), Quaternion.identity); }
このスクリプトは10体の敵をステージ上に生成します。 ランダムにX座標とZ座標を生成して敵の位置を決定しますが、もし生成された位置がプレイヤーの近くだった場合(この例ではX座標が-2から2の間)、 その敵の生成をスキップし、再度位置を生成するようにしています。
ここでは新しく continue キーワードが出てきていますね。 このキーワードは、その回の処理を1回分スキップして次のループに移る命令です。
今回は continue キーワードが if 文内にありますので、その後のInstantiateの処理を実行せず、直接次のループに進みます。 そのため、先ほどの説明にあるように、敵の生成位置がプレイヤーの近くになったとき、その生成をキャンセルします。 そして再度、新たな位置での生成を試みるというロジックを実現し、プレイヤーの近くに敵が生成されるのを避けています。
switch 文との組み合わせ
switch文を組み合わせると、敵が倒された時にドロップするアイテムをランダムに決定する処理などを実装することができます。
for (int i = 0; i < 5; i++) { int dropItemID = Random.Range(1, 4); // 1から3までのランダムな数値を生成 switch (dropItemID) { case 1: Debug.Log("Drop a Potion!"); // アイテム(ポーション)の生成処理 break; case 2: Debug.Log("Drop a Sword!"); // アイテム(剣)の生成処理 break; case 3: Debug.Log("Drop a Shield!"); // アイテム(盾)の生成処理 break; } }
このサンプルコードでは、敵が倒された時に5個のアイテムをドロップします。 ドロップするアイテムのIDをランダムに決定し(この例では1から3)、それぞれのIDに応じて異なるアイテム(ポーション、剣、盾)をドロップします。
配列を学習している場合には、各アイテムの生成処理の部分に活用することができますね。
このように、一見難しそうに見えるプログラムのロジックも、すべては基礎的な制御文で構成されています。
基礎の処理をしっかりと覚えていき、それをどのように組み合わせいけば自分の作りたい処理が作れるのか、 そういったイメージを持って学習していくと、実装力が身に着くとともに、プログラムへの理解が深まります。
まとめ
まとめとして、for 文を利用する際の注意点について、解説しておきます。
for 文を使用する際は、条件式がいつfalseになるのか確認するようにしましょう。 なぜなら、条件式が常にtrueのままだと、ループ処理が終了しません。
このような状態を無限ループとよび、無限ループが発生してしまうと、プログラムが停止してしまいます。 (Unity エディタ―の場合、再起動しなければならなくなりますし、実際のゲームであればゲームが停止します。)
if 文や switch 文を使用する際には、if 文や switch 文の中で重要な処理を行わないようにしましょう。 これらの制御構文は、処理の流れを制御するものであり、処理そのものを記述する場所ではありません。
そのため、メソッドの呼び出し命令などを書くように留めることで、処理の内容を明確にできます。 また、処理の把握が容易になることで、予期しないエラーを避けることができます。