【ゲーム開発のためのC#入門講座・基礎拡張編】色んなループを使ってみよう【#4】

2.0_C#基礎拡張編

whileの注意点覚えてますか?

以前whileというループ処理を学習しました。その時、ふたつ注意点がありましたね。ひとつは無限ループに気を付けて、ということ。もうひとつは……覚えてますか?

そう、最初から条件を満たさない場合は一度も実行されないというものでした。

実はこの問題を解消できるループ処理があります。

まずはそこから学んでいきましょう。

do whileループ

do whileループはwhileループの派生形です。

条件がtrueの間、「{}」で囲まれた処理を繰り返すこと、条件を満たし続けると無限ループになってしまうことなど、その特徴はwhileとほとんど変わりません。

唯一違うのは、条件を満たしているかどうかの判定が最後に行われるということ。

実際に確認してみましょう。

public class Hello{
    public static void Main(){
        int playerHP = 0;
        do
        {
            playerHP -= 1;
            System.Console.WriteLine(playerHP);
        }
        while (playerHP > 0);
        System.Console.WriteLine("ループ処理おわったよ!");
    }
}
出力エリア

-1
ループ処理おわったよ!

条件である「playerHPが0より大きかったら」を最初から満たしていないにもかかわらず、1回だけループ処理内の命令が実行されていますね。

これは下記の順番でコンピュータが命令を実行しているからです。

  1. playerHPに0を設定
  2. ループ処理に入るが、do whileの場合はこの時点では条件を見ずに実行
  3. playerHPから1引いた値をplayerHPに上書き
  4. 現在のplayerHPの値を出力エリアに表示
  5. ループ処理終了後、条件を満たしているか確認。条件を満たしている場合は3に戻る。満たしていない場合は次の命令へ

このように、do whileは最低1回は実行して欲しいループを実装することができます

いちおうwhileループでも下記のように実装すれば同様のことはできます。

public class Hello{
    public static void Main(){
        bool isFirst = true;
        int playerHP = 0;
        while (isFirst || playerHP > 0)
        {
            playerHP -= 1;
            System.Console.WriteLine(playerHP);
            isFirst = false;
        }
        System.Console.WriteLine("ループ処理おわったよ!");
    }
}
出力エリア

-1
ループ処理おわったよ!

ただ、余計な変数や命令が必要になってしまうので、do whileループを使った方がすっきり書けてよいですね。

do whileループの構文は下記の通りです。

<構文>
do
{
    ……繰り返したい処理……
}
while (条件);

whileループと違い、「while (条件)」のあとに「;(セミコロン)」がないとエラーになってしまうので注意してください。

forループ

今度はまったく新しいループ処理を学びましょう。それがforループです。

forループは、指定した回数分処理を繰り返したい時によく使うループ処理です。

まずはforループの実例を見てみましょう。

public class Hello{
    public static void Main(){
        for (int i = 1; i <= 3; i += 1)
        {
            System.Console.WriteLine(i);
        }
        System.Console.WriteLine("ループ処理おわったよ!");
    }
}
出力エリア

1
2
3
ループ処理おわったよ!

確かに繰り返し処理は実行されていますが、書き方がだいぶ謎いですね。

どうしてこのような結果になるのか、まずは構文を確認してみましょう。

<構文>
for (変数の初期値設定; ループ条件; 変数の増減処理)
{
    ……繰り返したい処理……
}

このforループを命令されたコンピュータは下記のように処理を実行します。

  1. 「変数の初期値設定」の部分の処理を実行
  2. 「ループ条件」を満たしているか判定。満たしている場合は繰り返したい処理を実行。満たしていない場合はループ処理を終えて次の命令へ
  3. 繰り返したい処理を実行
  4. 「変数の増減処理」を実行し、2へ戻る

これを先程の実例にあてはめて考えてみると、下記のようになります。

public class Hello{
    public static void Main(){
        for (int i = 1; i <= 3; i += 1)
        {
            System.Console.WriteLine(i);
        }
        System.Console.WriteLine("ループ処理おわったよ!");
    }
}
  1. int型の変数「i」に1を設定
  2. 変数「i」が3以下かどうか判定。3以下だったら繰り返したい処理を実行。3より大きかったらループ処理を終えて次の命令へ
  3. 繰り返したい処理を実行
  4. 変数「i」に+1して2へ戻る

変数の増減処理に+1を設定すると、ループの度に1ずつカウントアップしながら処理を繰り返してくれます。そのため、初期値に1を設定し、条件に「変数 <= 繰り返したい回数」を設定すると、あたかも条件で指定した回数(今回は3)分だけ処理を繰り返すループ処理のように動作するという訳です。

ループ回数と変数「i」の関係

1回目のループ :i = 1
2回目のループ :i = 2
3回目のループ :i = 3

あくまでこういう使い方をする機会が多いというだけで、初期値や増減処理を変更すると色んな使い方ができます。他の人のコードなどを参考に使い方を覚えていくとよいでしょう。

なお、こちらも同じことはwhileループでもできます。

public class Hello{
    public static void Main(){
        int count = 1;
        while (count <= 3)
        {
            System.Console.WriteLine(count);
            count += 1;
        }
        System.Console.WriteLine("ループ処理おわったよ!");
    }
}

ただ、やはりdo whileと同様、余計な変数や命令が必要になってしまいます。結果、forループの方がすっきり書けるんですね。

ちなみにforループで使う特殊な変数名は慣例として「i」が使われます。理由はぐぐると出てきますが、ゲーム開発には関係ないので、そういうものなんだなーで大丈夫です。

新しい算術演算子でスタイリッシュに書こう

このforループを使うならぜひ覚えておきたいのがインクリメント・デクリメントです。

インクリメント・デクリメントとは、算術演算子に分類される演算子です。

演算子命令使用例
++現在の値に1を足すnumber++
現在の値から1引くnumber–

インクリメントが「++」、デクリメントが「–」です。

実際に見てみましょう。

public class Hello{
    public static void Main(){
        int number = 0;
        number++;
        System.Console.WriteLine(number);
        number--;
        System.Console.WriteLine(number);
    }
}
出力エリア

1
0

きちんと数値が1ずつ変動していますね。

やっていることは下記と同じです。

public class Hello{
    public static void Main(){
        int number = 0;
        number += 1;
        System.Console.WriteLine(number);
        number -= 1;
        System.Console.WriteLine(number);
    }
}

ただ、インクリメント・デクリメントを使った方が簡潔に書けますね。

forループの増減処理でもこちらを活用した方がすっきりして見えます。

public class Hello{
    public static void Main(){
        // ↓1ずつ増える処理をインクリメントで設定
        for (int i = 0; i < 3; i++)
        {
            System.Console.WriteLine(i);
        }
        System.Console.WriteLine("ループ処理おわったよ!");
    }
}

といっても+2ずつ増えるとか、-3ずつ減るとか、±1以外の増減処理はインクリメント・デクリメントでは設定できません。その場合は元の通り、「+=」か「-=」の複合代入演算子を使うことになります。

public class Hello{
    public static void Main(){
        // ↓±1以外の処理では今まで通りの方法を使う
        for (int i = 0; i > -5; i -= 2)
        {
            System.Console.WriteLine(i);
        }
        System.Console.WriteLine("ループ処理おわったよ!");
    }
}

なので絶対インクリメント・デクリメントを使わなきゃいけないという訳ではないです。

ただ、基本的にforループ使いたい時は「1ずつ加算または減算したい」ことが多いです。そういった時、インクリメント・デクリメントを使った方がさらっと書けてスタイリッシュなんですね。

なのでこちらも、楽して書けるなら活用しよう系の技術になります。

Unityでの活躍ポイント

自分のゲームで例を挙げると、クリア済のレベルに応じて表示する選択肢を変化させるのに使っています(わかりやすくするために少し加工しています)。

for (int i = clearLevel + 1; i > 0; i--)
{
    int level = i;
    Button button = CreateButton();
    button.GetComponentInChildren<Text>().text = level.ToString();
}

クリア済のレベル+1を初期値とし、増減処理にはデクリメントを設定しています。

こうすることで、仮にクリア済のレベルが2だったなら、

  • レベル3
  • レベル2
  • レベル1

という風に、未クリアのレベルを先頭に降順で選択肢が表示されるようにしています。

ループ処理は工夫次第で色んな使い方ができるので、色々試していくとよいでしょう。

実践演習

それでは実際に色んなループを使ってみましょう。

演習①do whileループを使ってみよう!

仕様

下記のループ処理を最低1回は実行されるように修正してください。
(対応前後で実行結果の違いも確認してみてください。こういうことを実現したい時にdo while使うんだなーということも見えてくると思います)

テンプレート
public class Hello{
    public static void Main(){
        System.Console.WriteLine("10までカウントアップ");
        int count = 10;
        // ↓このループ処理を最低1回は実行されるように修正しよう!
        while (count < 10)
        {
            count++;
            System.Console.WriteLine(count);
        }
        if (count > 10)
        {
            System.Console.WriteLine("って最初から超えとるがな!");
        }
        else
        {
            System.Console.WriteLine("カウント終了したよ!");
        }
    }
}
出力エリア(対応前)

10までカウントアップ
カウント終了したよ!

演習②forループを使ってみよう!

仕様

forループを使って1~1000までの合計値を算出し出力エリアに表示してください。
<考え方>
変数「i」にはループの度に1ずつ加算された数値が入っています。
これを合計値集計用の変数「sum」に足していってあげると実現できます。

テンプレート
public class Hello{
    public static void Main(){
        int sum = 0;
        for (int i = 1; i <= 1000; i++) {
            // ↓ここに合計値を算出するための加算処理を書こう!
            
            // ここまで
        }
        System.Console.WriteLine(sum);
    }
}

答え合わせ

演習①の答え

public class Hello{
    public static void Main(){
        System.Console.WriteLine("10までカウントアップ");
        int count = 10;
        // ↓このループ処理を最低1回は実行されるように修正しよう!
        do
        {
            count++;
            System.Console.WriteLine(count);
        }
        while (count < 10);
        if (count > 10)
        {
            System.Console.WriteLine("って最初から超えとるがな!");
        }
        else
        {
            System.Console.WriteLine("カウント終了したよ!");
        }
    }
}
出力エリア

10までカウントアップ
11
って最初から超えとるがな!

演習②の答え

public class Hello{
    public static void Main(){
        int sum = 0;
        for (int i = 1; i <= 1000; i++) {
            // ↓ここに合計値を算出するための加算処理を書こう!
            sum += i;
            // ここまで
        }
        System.Console.WriteLine(sum);
    }
}
出力エリア

500500

まとめ

  • do whileループはwhileループの派生形。条件を満たしているかどうかの判定がループ処理の最後に行われるという特徴がある
  • forループは、指定した回数分処理を繰り返したい時によく使うループ処理
  • インクリメント・デクリメントは現在の値に1足したり引いたりする算術演算子

それでは、今回もお疲れ様でした!

また次の記事でお会いしましょう!

Posted by yuumekou