MSBOOKS

プログラミングとか深層学習とか日常の出来事とか

【C言語入門】part8.5 : よりゲームっぽく見せるには…

前回はRPGゲームみたなものを作りましたが,マップの移動などではマップがどんどん下に表示されて,リアルタイムで動いている感じがあまりしませんでしたよね.キーボード入力を受け付けたら,今までの画面を消してから,新たに表示すれば綺麗に見れそうですね.そんなよりゲームっぽく見せる方法についてみていきます.

画面をクリアする

まずはだらだら画面表示していたのをクリアして綺麗にする方法をお教えします.
f:id:msteacher:20190127002440p:plain
前回のtest8-7.cを改良するので参照してください.はじめに#include <stdio.h>の次に#include <stdlib.h>を追加します.これで画面をクリアする関数が使えるようになります.実際に画面をクリアするのはsystem("cls");を記述します.これを記述することで今までの表示はすべて消すことができます.test8-7.cを改良した結果を以下に載せます.
・test8-8.c

#include <stdio.h>
#include <stdlib.h>
int main(void){
   int map[7][7];  //マップの定義
   int i,j;
   int choose;
   int hero[2] = {1, 1};  //主人公の初期位置
   int goal[2] = {3, 3};  //ゴールの位置
   //マップの初期化(0:移動可能,1:壁(移動不可))
   for(i = 0 ;i < 7 ;i++){
      for(j = 0 ;j < 7 ;j++){
         if(i == 0 || i == 6 || j == 0 || j == 6)
            map[i][j] = 1;
         else
            map[i][j] = 0;
      }
   }
   system("cls"); //画面をいったん綺麗にする
   /*RPGゲームメインループ処理*/
   while(1){
      for(i = 0 ;i < 7 ;i++){
         for(j = 0 ;j < 7 ;j++){
            //主人公の居る位置は0でなく@を描画
            if(hero[0] == i && hero[1] == j)
               printf("@");
            //ゴールの場所は0ではなくGを描画
            else if(goal[0] == i && goal[1] == j)
               printf("G");
            else
               printf("%d",map[i][j]);
         }
         printf("\n");
      }
      //終了判定
      if(goal[0] == hero[0] && goal[1] == hero[1])
         break;
      printf("(↑:1,→:2,↓:3,←:4)>");
      scanf("%d",&choose);
      system("cls"); //画面を綺麗にする
      switch(choose){
         case 1:
            //次移動するところが壁でなければ移動する
            if(map[hero[0]-1][hero[1]] != 1)
               hero[0] -= 1;
            break;
         case 2:
            if(map[hero[0]][hero[1]+1] != 1)
               hero[1] += 1;
            break;
         case 3:
            if(map[hero[0]+1][hero[1]] != 1)
               hero[0] += 1;
            break;
         case 4:
            if(map[hero[0]][hero[1]-1] != 1)
               hero[1] -= 1;
            break;
         default:
            printf("不正な入力です\n");
      }
   }
   printf("GOAL!\n");
   return 0;
}

・実行結果

1111111
1@00001
1000001
100G001
1000001
1000001
1111111
(↑:1,→:2,↓:3,←:4)>

実際に操作をしてみてください.一個前の動作がでることなく,動作後のマップのみがでるので綺麗に表示されているかと思います.

エンターキーを省略する

今までのプログラムではキーボードから入力した後,エンターキーを押さないと次の動作にいきませんでした.ゲームをプレイするときにいちいちエンターキーを押していては時間がかかって仕方ありませんよね.そこで,キーボードから入力されたら即実行する_getch関数というものを使ってみましょう.
この関数を使うには最初に,#include を追加します.この関数でキーボードから受け取ったものをchooseに代入すればすぐに使えますが,1,2,3,4だとすこし使いづらいので,ネットゲームなどでよく使われるWASDキーの入力で動けるようにしてみましょう.Wキーが上,Aキーが左,Sキーが下,Dキーが右に進むようにプログラムを改良してみましょう.
ただし今回はchooseは整数型なので文字型(char)に直してから使ってみましょう.文字型(1文字)は'a'のようにシングルクォーテーションで囲って使います.プログラムの改良例を以下に載せます.
・test8-9.c

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
int main(void){
   int map[7][7];  //マップの定義
   int i,j;
   char choose;
   int hero[2] = {1, 1};  //主人公の初期位置
   int goal[2] = {3, 3};  //ゴールの位置
   //マップの初期化(0:移動可能,1:壁(移動不可))
   for(i = 0 ;i < 7 ;i++){
      for(j = 0 ;j < 7 ;j++){
         if(i == 0 || i == 6 || j == 0 || j == 6)
            map[i][j] = 1;
         else
            map[i][j] = 0;
      }
   }
   system("cls"); //画面をいったん綺麗にする
   /*RPGゲームメインループ処理*/
   while(1){
      printf("******操作方法******\n↑:w →:d ↓:s ←:a\n");
      printf("********************\n");
      for(i = 0 ;i < 7 ;i++){
         for(j = 0 ;j < 7 ;j++){
            //主人公の居る位置は0でなく@を描画
            if(hero[0] == i && hero[1] == j)
               printf("@");
            //ゴールの場所は0ではなくGを描画
            else if(goal[0] == i && goal[1] == j)
               printf("G");
            else
               printf("%d",map[i][j]);
         }
         printf("\n");
      }
      //終了判定
      if(goal[0] == hero[0] && goal[1] == hero[1])
         break;
      choose = _getch(); //キーボードから入力を受け付ける
      system("cls"); //画面を綺麗にする
      switch(choose){
         case 'w':
            //次移動するところが壁でなければ移動する
            if(map[hero[0]-1][hero[1]] != 1)
               hero[0] -= 1;
            break;
         case 'd':
            if(map[hero[0]][hero[1]+1] != 1)
               hero[1] += 1;
            break;
         case 's':
            if(map[hero[0]+1][hero[1]] != 1)
               hero[0] += 1;
            break;
         case 'a':
            if(map[hero[0]][hero[1]-1] != 1)
               hero[1] -= 1;
            break;
         default:
            printf("不正な入力です\n");
      }
   }
   printf("GOAL!\n");
   return 0;
}

・実行結果

******操作方法******
↑:w →:d ↓:s ←:a
********************
1111111
1000001
1000001
100@001
1000001
1000001
1111111
GOAL!

実際に動かせましたでしょうか.こんな感じに修正することでC言語コマンドプロンプト上でもゲームっぽく見せることができます.普通のC言語の入門ではやらない内容なので詳しい仕組みなどは理解しなくて大丈夫なので綺麗になったー!ぐらいの感覚で使っていきましょう.
お疲れさまでした.

練習問題

・問題
 part7のtest7-9.cの指スマゲームを改良して,画面をクリアしてくれるように修正してみましょう.クリアする位置に注意して,色々試してみよう.
・実行結果例

*****バトル開始!*****
自分の立てた指の数:2
PC立てた指の数:1
2人の合計:3
pcの予測した数:2
pcの予測は間違えていた!
**********************
---------------------------------
|自分の残りの数:2 PCの残りの数:2|
---------------------------------
あなたのターン!
立つ親指の数を予測してね:

練習問題の解答例

・問題
 ・test8-10.c

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main(void){
   //①変数定義
   int num;
   int myTh;
   int myReTh = 2;
   int pcTh;
   int pcReTh = 2;
   int turn; 
   srand((unsigned)time(NULL));
   system("cls");
   printf("*****指スマゲームを開始します*****\n");
   //②先行を決める
   turn = rand() % 2;
   if(turn == 0){
      printf("先行はあなたです\n");
   }
   else{
      printf("先行はPCです\n");
   }
   //③自分のターンとPCのターンの動きを定義
   while(myReTh != 0 && pcReTh != 0){
      printf("---------------------------------\n");
      printf("|自分の残りの数:%d PCの残りの数:%d|\n",myReTh,pcReTh);
      printf("---------------------------------\n");
      //自分のターン
      if(turn == 0){
         printf("あなたのターン!\n");
         do{
            printf("立つ親指の数を予測してね:");
            scanf("%d",&num);
         }while(num < 0 || num > (myReTh + pcReTh));
         do{
            printf("自分の立てる親指の数を入力してね:");
            scanf("%d",&myTh);
         }while(myTh < 0 || myTh > myReTh);
         pcTh = rand() % (pcReTh + 1);
         system("cls");
         printf("*****バトル開始!*****\n");
         printf("自分の立てた指の数:%d\nPC立てた指の数:%d\n",myTh,pcTh);
         printf("2人の合計:%d\n",myTh+pcTh);
         printf("あなたの予測した数:%d\n",num);
         if((myTh + pcTh) == num){
            printf("あなた予測はあってます!\n");
            myReTh = myReTh - 1;
         }
         else{
            printf("あなたの予測は間違えていた!\n");
         }
         printf("**********************\n");
         turn = 1;
      }
      //PCのターン
      else{
         printf("pcのターン!\n");
         do{
            printf("自分の立てる親指の数を入力してね:");
            scanf("%d",&myTh);
         }while(myTh < 0 || myTh > myReTh);
         pcTh = rand() % (pcReTh + 1);
         //pcが立てた指の数を足すことで,pcが指を立ててるのに0を予測しないようにする
         num = rand() % (myReTh + 1) + pcTh;
         system("cls");
         printf("*****バトル開始!*****\n");
         printf("自分の立てた指の数:%d\nPC立てた指の数:%d\n",myTh,pcTh);
         printf("2人の合計:%d\n",myTh+pcTh);
         printf("pcの予測した数:%d\n",num);
         if((myTh + pcTh) == num){
            printf("pcの予測はあってます!\n");
            pcReTh = pcReTh - 1;
         }
         else{
            printf("pcの予測は間違えていた!\n");
         }
         printf("**********************\n");
         turn = 0;
      }
   }
   //④どちらが勝ったか表示
   if(myReTh == 0){
      printf("あなたの勝ちです!\n");
   }
   else if(pcReTh == 0){
      printf("PCの勝ちです!\n");
   }
   return 0;
}