条件分岐 if文
ゲームを作る際、ある条件下のみでこんな動作を実装したい!といった場面が多く表れます。
例えば、横スクロールアクションゲーム(マリオのようなゲーム)を作る場合、以下のような例が挙げられます。
- プレイヤーが敵とぶつかったら、ダメージを与えたい(残機を減らしたい)
- プレイヤーの残機が0になったら、ゲームオーバーにしたい
- 残り時間が0になったら、ゲームオーバーにしたい
このような処理は、「if文」を用いることで簡単に実装できます!
試しに以下のコードを書いてみましょう。
《コード1》
#include <iostream>
int main() {
int playerLife = 0;// プレイヤーの残機数
if(playerLife == 0){
std::cout << "ゲームオーバー!" << std::endl;
}
return 0;
}
まず簡単に説明すると、ifの隣の()内には条件を、{}内には条件を満たした際に実行する内容を書きます。
上の例で言うと、条件は playerLife == 0 です。
== は「右辺の値と左辺の値が一致しているかどうか」を判定する役割を担っており、例ではplayerLifeの値が0の場合に「ゲームオーバー!」と出力してくれます。
else if, else
playerLifeが0の時に○○、1の時に○○、maxの時に○○...といった具合に、3つ以上に分岐させたい場合もありますよね。
ライフが減るにつれて、注意を促すように赤めのエフェクトが表示されるようになる、といった演出を見たことはありませんか?次はこれをイメージしてみましょう。
《コード2》
#include <iostream>
#include <string>
int main(){
int playerLife;//ライフの数
std::string playerLifeColor;//ライフの色名
std::cin >> playerLife;
if(playerLife >= 3){
playerLifeColor = "Blue";
}else if(playerLife == 2){
playerLifeColor = "Yellow";
}else{
playerLifeColor = "Red";
}
std::cout << playerLifeColor << std::endl;
return 0;
}
新たにelse if()
が出てきました。これは、一つ上のif文がfalseだった場合に実行してくれるイメージです。
《コード2》の場合だと、「playerLifeが3以上ではない」とき、「playerLifeが2かどうか」を判定してくれるわけです。
最後に else です。この後ろには条件を書く必要がありません。else
に続く{}内のコードは、
「else
より前の一連のif()
及びif else()
がすべてfalseだった場合」に実行してくれる感覚で大丈夫です。
《コード2》では「playerLifeが3以上でも2でもない場合」にelse内を実行してくれます。
論理演算子
しかし、ライフが3つは少ないですよね。快適なゲームを求めるのなら10個ぐらいあってもよいかもしれません。次は、
・ライフが6以上のとき、青色。
・ライフが3~5のとき、黄色。
・ライフが1~2のとき、赤色。
となるようにしてみましょう。
↑のようにライフの色を黄色にする場合、「ライフが3以上である」「ライフが5以下である」の二つの条件が必要そうです…
こんなときは&&
を使って以下のように書けます!
《コード3》
#include <iostream>
#include <string>
int main(){
int playerLife;//ライフの数
std::string playerLifeColor;//ライフの色名
std::cin >> playerLife;
if(6 <= playerLife){
playerLifeColor = "Blue";
}else if(3 <= playerLife && playerLife <= 5){
playerLifeColor = "Yellow";
}else{
playerLifeColor = "Red";
}
std::cout << playerLifeColor << std::endl;
return 0;
}
else if(3 <= playerLife && playerLife <= 5)
の部分は、「playerLifeが3以上かつplayerLifeが5以下の場合」と言い換えることができます。この「かつ」こそが&&
の役割です。
3 <= playerLife <= 5
のように、複数の条件を一つにまとめることはできないので注意!
:
しかし、《コード3》は一か所修正できる点があります。
「ライフが3~5のとき、黄色」という条件をそのまま表したものがelse if(3 <= playerLife && playerLife <= 5)
ですが、else if文は前のif文がfalseだったことを踏まえて書くことが多いです。(今回の場合、「ライフが6以上」という条件がfalseである場合)
よって、playerLife <= 5
は省くことができます。
修正後のコード↓↓
#include <iostream>
#include <string>
int main(){
int playerLife;//ライフの数
std::string playerLifeColor;//ライフの色名
std::cin >> playerLife;
if(6 <= playerLife){
playerLifeColor = "Blue";
- }else if(3 <= playerLife && playerLife <= 5){
+ }else if(3 <= playerLife){
playerLifeColor = "Yellow";
}else{
playerLifeColor = "Red";
}
std::cout << playerLifeColor << std::endl;
return 0;
}
もし、ライフが青色である条件を変えたくなった場合、修正前のコードだと、
if(6 <= playerLife){
と }else if(3 <= playerLife && playerLife <= 5){
の2か所の数値を変える必要がありますが、修正後のコードだと、if(6 <= playerLife){
の修正だけで済みます。ちょっとしたテクニックとして覚えておきましょう!
switch-case文
次はライフが10まである中、1~10それぞれで色を変えてみましょう。ここで、《コード2》のようにelse if(playerLife == 1)
を10個繰り返すのはやや大変ですし、コードも長く、見づらくなってしまいます。ここでswitch-case文の出番です!
《コード4》
#include <iostream>
#include <string>
int main(){
int playerLife;//ライフの数
std::string playerLifeColor;//ライフの色名
std::cin >> playerLife;
switch(playerLife){
case 1: playerLifeColor = "Red"; break;
case 2: playerLifeColor = "Orange"; break;
case 3: playerLifeColor = "Magenta"; break;
case 4: playerLifeColor = "Pink"; break;
case 5: playerLifeColor = "Yellow"; break;
case 6: playerLifeColor = "Blue"; break;
case 7: playerLifeColor = "SkyBlue"; break;
case 8: playerLifeColor = "LightBlue"; break;
case 9: playerLifeColor = "Green"; break;
case 10: playerLifeColor = "YellowGreen"; break;
default: playerLifeColor = "Black"; break;
}
std::cout << playerLifeColor << std::endl;
return 0;
}
switchの隣の()内には変数や条件を書きます。今回はplayerLife
を入れてますね。そして次の行からはcaseがずらっと並んでいます。仕組みをざっくり言うと、()内の値とcaseの横の値が一致するとき、そのcaseの部分からbreakまでのコードを実行します。上の例で言えば、playerLifeが3の時は、case 3:
と break;
の間のplayerLifeColor = "Magenta";
を実行してくれます。
練習問題
以下のテンプレが与えられる。
ダメージ量(0以上の整数)が入力されるので、初期のライフ数からダメージ分を引き、残りライフ数によって以下のように出力が変わるようにしよう。
#include <iostream>
#include <string>
int main(){
int playerLife = 3;//ライフの数
int damage;//ダメージ量
std::string status;//ステータス
std::cout << "HP 3" << std::endl;
std::cout << "damage: ";
std::cin >> damage;
----your code----
-----------------
std::cout << status << std::endl;
return 0;
}
《出力例》
HP 3
damage: 3
gameover
--------
HP 3
damage: 2 //1のときも同じ出力となるようにする。
damaged
--------
HP 3
damage: 0
no damage
--------
HP 3
damage: 72
gameover
--------
if文を用いる場合とswitch文を用いる場合の2パターンを考えてみよう。
解答(if)
#include <iostream>
#include <string>
int main(){
int playerLife = 3;//ライフの数
int damage;//ダメージ量
std::string status;//ステータス
std::cout << "HP 3" << std::endl;
std::cout << "damage: ";
std::cin >> damage;
if(playerLife - damage == 3){
status = "no damage";
}else if(playerLife - damage == 2 || playerLife - damage == 1){//論理演算子を使おう
status = "damaged";
}else{
status = "gameover";
}
std::cout << status << std::endl;
return 0;
}
解答(switch)
switch文の()には、式を入れることもできる。
#include <iostream>
#include <string>
int main(){
int playerLife = 3;//ライフの数
int damage;//ダメージ量
std::string status;//ステータス
std::cout << "HP 3" << std::endl;
std::cout << "damage: ";
std::cin >> damage;
switch(playerLife - damage){
case 3:status = "no damage";break;
case 2:status = "damaged";break;
case 1:status = "damaged";break;
default:status = "gameover";break;
}
std::cout << status << std::endl;
return 0;
}
ループ文
ゲームを作っていると、「100個分の床ブロックを生成したい」だとか、「40個分の弾幕を生成したい」ということがあります。生成するためのコードを100個分書くなんてことは非常に面倒なので、ここでループ文を身につけましょう!
今まで数値や文字を出力してきましたが、今回は_
を床ブロック1つ分と見なして、ステージを作るような感じでやっていきましょう。イメージは↓のような感じです。
_______________________________
while文
まず以下のコードを書いてみましょう。
《コード5》
#include <iostream>
int main(){
int blockNum = 0;//ブロックの数
while(blockNum < 30){
std::cout << "_";
blockNum++;//インクリメント
}
return 0;
}
whileの横の()には、ifと同じように「条件」を書きます。ifと違うのは、()内がtrueである限り、{}内を実行し続けるという点です。
1行づつ見ていきましょう。また、「プログラムは原則main関数内を上から1行ずつ実行する」ということも念頭に置いておいてください。
まず最初にwhile(blockNum < 30)
に到達時、blockNumは0でblockNum < 30
を満たすため{}内を実行し、_
を1つ出力してblockNumの値に1を足します。(1ループ目)
次にwhile(blockNum < 30)
に到達時、blockNumは1でblockNum < 30
を満たすため{}内を実行し、_
を1つ出力してblockNumの値に1を足します。(2ループ目) これを繰り返します。
ざっと飛ばして30ループ目、blockNumは29でblockNum < 30
を満たすため、_
を1つ出力してblockNumの値に1を足します。
そして31ループ目、blockNumは30であるため、blockNum < 30
を満たしません。よって、{}内を実行せずにループが終了します。
for文
ループ文にはもう一つ、for文というものがあります。↑と同じ出力結果を
こちらのループ文でも書いてみましょう。
《コード6》
#include <iostream>
int main(){
for(int i = 0;i < 30;i++){
std::cout << "_";
}
return 0;
}
for文の()には、for(「初期化式」;「条件式」;「更新式」)
というように記述します。 「条件式」がtrueである限り、{}内を実行し続けるというのはwhile文と同じです。「初期化式」と「更新式」は↓で説明します。
また1行ずつ見ていきましょう。
まず最初に「初期化式」により、i = 0
と初期化します。1ループ目のみに実行される箇所だと思ってくれればよいです。この時点でiは0でi < 30
を満たすため{}内を実行し、_
を1つ出力します。その後に更新式の内容を反映させます。例の場合、iに1を加えています。
2ループ目に入った時点で、iは1でi < 30
を満たすため{}内を実行し、_
を1つ出力します。その後更新式により、iに1を加えます。
ざっと飛ばして30ループ目、iは29でi < 30
を満たすため{}内を実行し、_
を1つ出力します。その後更新式により、iに1を加えます。
最後に31ループ目、iは30でi < 30
を満たしません。よって{}内を実行せずにループが終了します。
continue と break
次に、ステージに穴を作る感覚でやってみましょう。左から数えて4の倍数の場所には穴(空白)を作ってみます。完成図は↓の感じ。
___ ___ ___ ___ ___ ___ ___ ___ ___ ___
《コード7》
#include <iostream>
int main(){
int i = 0;
while(true){
if(i % 4 == 3){
std::cout << " ";
i++;
continue;
}
std::cout << "_";
i++;
if(i >= 40)break;
}
}
while文についておさらいしましょう。while文の()内の条件が真である限り、{}内を実行し続けるんでしたね。しかし、ここでは()内はtrue、すなわち真であり、このままでは永遠に{}内を実行し続けてしまいます...
ここでbreakの出番です!
次はcontinueを見ていきましょう。
二重ループ
ゲーム内では、巨大なブロックの塊もよく見られると思います。次は5 x 5のブロックを作ってみましょう!
イメージはこんな感じ↓
#####
#####
#####
#####
#####
《コード8》
#include <iostream>
int main(){
for(int i = 0;i < 5;i++){
for(int j = 0;j < 5;j++){
std::cout << "#";
}
std::cout << std::endl;//改行
}
return 0;
}
この例では、for文の中にfor文を書いています。このような書き方を二重ループと呼びます。
まず、《コード8》の↓部分は、一列分の#####
を出力します。《コード6》とほとんど同じですね。
for(int j = 0;j < 5;j++){//#####を出力
std::cout << "#";
}
std::cout << std::endl;//改行
外側のfor文(i
を用いているもの)は↑を{}で覆っています。つまり、#####
を出力する処理を5回ループしているということです。
for(int i = 0;i < 5;i++){// ↓を5回ループ
for(int j = 0;j < 5;j++){//#####を出力
std::cout << "#";
}
std::cout << std::endl;//改行
}
練習問題
(1)以下の形を出力してみよう。
#####
####
###
##
#
ヒント
- for文の「条件式」を変えてみよう
- 《コード8》を1か所変えるだけでよい
解答
#include <iostream>
int main(){
for(int i = 0;i < 5;i++){
for(int j = 0;j < 5 - i;j++){//コード8と違う点
std::cout << "#";
}
std::cout << std::endl;//改行
}
return 0;
}
(2)マリオのゴール前によく配置されている、以下の形を出力してみよう。
#
##
###
####
#####
######
#######
########
解答
#include <iostream>
int main(){
for(int i = 0;i < 8;i++){
for(int j = 0;j < 8 - (i + 1);j++){
std::cout << " ";
}
for(int j = 0;j < (i + 1);j++){
std::cout << "#";
}
std::cout << std::endl;//改行
}
return 0;
}
Extra
数字あてゲームを作ってみよう!
以下の三行で乱数を生成できます。
std::random_device seed_gen;
std::mt19937 random(seed_gen());
int number = random() % 100;
100の部分を変えると乱数の範囲を変えられます
以下のインクルードをファイルの先頭に追加する必要があるので注意してください。
#include <random>
EX問題の入出力例
数字を入力してね: 50
もっと小さいよ!
数字を入力してね: 25
もっと小さいよ!
数字を入力してね: 10
もっと大きいよ!
数字を入力してね: 17
もっと小さいよ!
数字を入力してね: 13
正解!!
ヒント
- ループ文とbreakを使おう
答え
#include <iostream>
#include <random>
int main(){
// 数あてゲーム
std::random_device seed_gen;
std::mt19937 random(seed_gen());
int number = random() % 100;
// この3行で0~99の乱数を生成できます
// while文の中身を各自考えてみましょう
while (true) {
int guess;
std::cout << "数字を入力してね: ";
std::cin >> guess;
if (guess == number) {
std::cout << "正解!!" << std::endl;
break;
} else if (guess < number) {
std::cout << "もっと大きいよ!" << std::endl;
} else if (guess > number){
std::cout << "もっと小さいよ!" << std::endl;
}
}
return 0;
}