東京工芸大学 工学部  電子機械学科 2年 前期 

基礎プログラミング 第13回
 2017.7.4 
藤木 文彦
http://fujiki.tv/t-kougei/kisoprog/
fujiki.kougei@gmail.com  



 今日は、簡単なシューティングゲームを作成します。


今日も、プログラム実行画面をプリンタに出して紙で提出してもらいます。
同時にメールにも貼り付けて送ってもらいます。

簡単なゲーム


 始めに、画面上に縦横20のドットを表示し、指定の座標に "#" を表示するプログラムを作成します。

  1.ゲーム座標画面の描画
 
適切な値が入力されるまで繰り返す while( ) break;

while( 条件 ) という制御文は、条件が満たされている間、次の処理を繰り返す、というものです。
 条件が満たされなくなると終わります。
 また、特別な働きをする break 文を実行すると、そのbreak文の含まれている while 文は、強制的に終了します。

 このような処理が何故必要なのかというと、条件が満たされたら、繰り返し回数になる前でも終了する、というような場合に使います。
 実際に使われる場合としては、入力値が、正しい値の範囲にあれば終了し、そうでない場合は、再度入力を求める、と言うような場合に使います。

 なお、以下の例では、

while( ++c < 10 )

 と言う条件を示して居ますので、cが1つずつ増加して、10回繰り返されたら強制的に終了します。(このとき、正しくない値が入ってエラー終了となりますが、今回は、そこまでは考えません。)

 場合に寄ると、正しい値が入力されるまで無制限に繰り返すこともあり、そのような場合には、次のように書きます。これは、必ず正しいので、正しい範囲の値が入力されて、break文が実行されるまで、いつまでも終わりません。
while( 1 == 1 )
while( TRUE )

画面表示と標的位置の表示

  (例題1) 

Project1301


#include "stdafx.h"
#include "iostream"
#include "fstream"
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{

#define MAX 20

	int n = 0, x, y, ix, iy;
	char a[MAX + 1][MAX + 1];
	for (ix = 0; ix <= MAX; ix++){
		for (iy = 0; iy <= MAX; iy++){
			a[ix][iy] = '.';
		}
	}

	for (iy = MAX; iy >= 0; iy--){
		printf("%2d ", iy);
		for (ix = 0; ix <= MAX; ix++){
			printf("%c ", a[ix][iy]);
		}
		printf("\n");
	}

	while (++n <= 10){
		printf("Input x y   ( x:0-%d y:0-%d)\n", MAX, MAX);

		cin >> x >> y;
		if (x >= 0 && x <= MAX && y >= 0 && y <= MAX){
			break;
		}
		else{
			printf("Invalid input! \n");
			return 1;
		}
	}

	a[x][y] = '#';


	for (iy = MAX; iy >= 0; iy--){
		printf("%2d ", iy);
		for (ix = 0; ix <= MAX; ix++){
			printf("%c ", a[ix][iy]);
		}
		printf("\n");
	}

	printf(" ");
	for (ix = 0; ix <= MAX; ix++){
		printf("%2d", ix);
	}
	printf("\n");

	getchar();
	getchar();
        return 0;
}




 【演習問題1】 


 上記プログラムの一部を書き換えて、

縦20個、よこ30個表示するようにしなさい。
(MAX を XMAX   YMAX  の2つに変え、数箇所書き直す。)

 以上の変更点を加えて動かしたら、
  実行した 画面
  プログラムの主要部分
 を、コピーしてメールに貼り付け、  "Project1301" として送りなさい。
 また、実行画面をプリントして提出しなさい。


  2.目標に当てる


 次に、画面上に目標 ”#” を表示し、それに向かって弾を撃つと、弾が飛んでいき、当たると ”$”印に変わる、というプログラムを作ります。
 目標の位置は、範囲内(0−20)の座標で指定し、打ち上げ角度(右から上に向かって、度数で表す)を指定します。

 範囲内の数値を入れないと再入力を求められます。
 当たり外れの判定が出た後、繰り返すかどうか求められます。


 キャラクタベース(文字だけ表示)では、画面を素早く書き直すことが出来ませんので毎回、空白表示後に1ステップ進んだ画面を表示します。1画面表示後に、Enter キーが押されるまで待ちます。(変数 tmp に、空データを入れるまで待つ。)

         for(i=0; i<=30; i++) printf("\n");   //  これは、画面をカラ送りするためのもの。

 その後、新しい画面を表示するために、1ページ分から送りするために、改行を30回して居ますが、実は、コンソール画面で画面をクリアするコマンド ”cls”(clear screen)を、プログラムから実行することが出来ます。
 次のようにします。

  std::system("cls");
    :: のように、コロン(:)を2つ並べて書く記号を、「スコープ解決演算子」と言いますが、詳細は省略します。
 これは、省略して、

  system("cls");

 とも書けます。
 これを実行するために、プログラムのはじめの部分に、こうしたシステム関数を定義した名前空間を使う、という意味で、
  using namespace std;
 という行が入っています。

  (例題2) 

Project1302

// Project1302.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include "iostream"
#include "fstream"

#include "math.h"
#include "string.h"
#include "stdlib.h"
#include "windows.h"

#define PI 3.14159265
#define MAX 20

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{

    int i,n=0,c=0,x,y,ix,iy;
    int xmax;
    char tmp;
    float d;
    int deg,hit=0;
    char a[MAX+1][MAX+1];
    char cont;

do {    // この do 文に対応する while は、プログラムの一番最後にある。6行下の while ではない。

        for( ix=0; ix<=MAX; ix++){
            for( iy=0; iy<=MAX; iy++ ){
                a[ix][iy]='.';
            }
        }

    while( ++c <= 10 ){
        for( iy=MAX; iy>=0; iy--){
            printf("%2d ",iy);
            for( ix=0; ix<=MAX; ix++){
                printf("%c ",a[ix][iy]);
            }
            printf("\n");
        }
        printf(" ");
        for( ix=0; ix<=MAX; ix++){
            printf("%2d",ix);
        }
        printf("\n");

        printf("Input target point x y ( x:0-20 y:0-20)\n");
            cin >> x >> y;
            if( x>=0 && x<=MAX && y>=0 && y<=MAX ){
            break;
        }else{
            printf("Invalid input! \n");
        }
    }

    a[x][y]='#';

    while( ++c <= 10 ){
        printf("Input angle( 0-89)");
            cin >> deg;
            getchar();
            if( deg>=0 && deg<90 ){
            break;
         }else{
            printf("Invalid input! \n");
            return 1;
        }
    }

    d=tan(PI*2*deg/360);

// ここから、毎回繰り返し計算して表示する部分
//    xmax=x;
    xmax=MAX;
    for( x=0; x<=xmax; x++){

        for(i=0; i<=30; i++) printf("\n");   //  これは、画面をカラ送りするためのもの。
// system("cls"); // 上の行を コメントアウトし、この行のコメントを外して実行してみなさい
// std::system("cls"); // 同様に、上の2行を コメントアウトし、この行のコメントを外して実行してみなさい

        y=x*d;
        if( y>MAX ){
            break;
        }
        printf("%d\n",y);

        if( a[x][y] == '#' ){
            hit=1;
            a[x][y]='$';
        }else{
            hit=0;
            a[x][y]='*';
        }

        for( iy=MAX; iy>=0; iy--){
            printf("%2d ",iy);
            for( ix=0; ix<=MAX; ix++){
                printf("%c ",a[ix][iy]);
            }
            printf("\n");
        }
        printf(" ");
        for( ix=0; ix<=MAX; ix++){
            printf("%2d",ix);
        }
        printf("\n");

        if( hit==1 ){
             printf("**** You Win ****\n");
             break;
        }

// 1回表示したら、次に進むために、なにかキーボードから入力する。

        cout << "次の画面表示(Enterキー)";
    getchar();
//        cin >> tmp;

    }
    if( hit==0 ){
        printf("---- You Lose ----\n");
    }
    printf("Continue? (y/n) \n");
    cin >> cont;

}while( cont == 'y' || cont =='Y' );
getchar();
return 0;
}









 【演習問題2】 

 上記プログラムの一部を書き換えて、

縦20個、よこ30個表示するようにしなさい。
(MAX を XMAX   YMAX  の2つに変え、数箇所書き直す。)

 以上の変更点を加えて動かしたら、
  実行した 画面
  プログラムの主要部分
 を、コピーしてメールに貼り付け、  "Project1302" として送りなさい。
また、実行画面をプリントして提出しなさい。


 【演習問題3】 


 1画面表示するごとに、キーを押さないとならないのは面倒です。そこで、1秒に1枚ずつ弾の進度の位置に ”*” を書いた画面を表示するように変更します。

 上記プログラムのどこか数行を次の行と入れ替えます。


// 1秒(1000ミリ秒)待つ
        Sleep(1000);

 以上の変更点を加えて動かしたら、
  実行した 画面
  プログラムの主要部分
 を、コピーしてメールに貼り付け、  "Project1303" として送りなさい。

 【演習問題4】 

(やや難しいので、うまくできないときは飛ばして次の課題をやりなさい。)

 なお、このプログラムは(以下も)目標が左上に近い位置にあるときに、あたっているはずなのにすり抜けてしまうことがあるが(下の図面)それについては、横のステップを、 1 単位ではなく、 0.1 単位にするとか、衝突判定の範囲を工夫するとかすればよい。)

 このようにして、今まで、左上に打ったときにすり抜けてしまったものをすり抜けないようにしなさい。

Project1304

メールと印刷物提出も同様に行いなさい。





  5.目標の場所を乱数で発生



 目標の出現場所を、自動的に乱数で発生させて描いた後、角度を指定して弾を飛ばすように変更ます。
 上記プログラムの、目標位置を入力する部分を、次のように、乱数発生プログラムと入れ替えます。

  (例題5) 

Project1305

#include "time.h"

---------------------------

srand( (unsigned int) time(NULL));

x=( rand() % XMAX );
y=( rand() % YMAX );

 【演習問題5】 


 変更点を加えて動かしたら、
  実行した 画面
  プログラムの主要部分
 を、コピーしてメールに貼り付け、  "Project1305" として送りなさい。
また、実行画面をプリントして提出しなさい。

【演習問題6】 


 目標にあたったときに、目標位置だけが、

    $

のように表示されるのは、味気ないので、次のように目標位置の、上下左右に %  が表示されるようにしなさい。

Project1306



(目標位置が、端にあるときにエラーになることがあるが、今のところは構わない。余裕があればエラーにならない方法を考えてみよ。)

 変更点を加えて動かしたら、
  実行した 画面
  プログラムの主要部分
 を、コピーしてメールに貼り付け、  "Project1306" として送りなさい。
また、実行画面をプリントして提出しなさい。


【応用演習問題7】 


Project1307
 このプログラムでは、外れたときに、また次回に別の位置に目標が出てしまう。 Continue で、y を選んだときには、同じ位置に目標が出るようにして、角度を何度も調整して出来るようにしてみなさい。
 Continue で、 n を入力したときには、完全に終わりにはならずに、次の目標を設定して続けるか、完全に終了するかを選べるようにしてみなさい。

 ある程度できたら、 Project1307 として、メールを提出しなさい。
また、実行画面をプリントして提出しなさい。
(やや難しいので、完全に動作しなくても途中までで時間切れの場合は、その状態でメール提出、画面出力提出を行いなさい。)


【応用演習問題8】 


Project1308

このプログラムでは目標が動きませんが、目標物が、2回ごとに、右に、1ドットずつ動くようにしたものを作りなさい。動いた際に、前の点を消すことを忘れないように。

なお、出現範囲があまり右側だと、画面からはみ出してしまうので、出現場所のx座標が XMAXの半分以下になるようにすること。





 実行画面の保存とメールへの添付法


(1) プログラムを作成、実行して、保存したい画面を表示しておきます。

(2) Word を立ち上げます。

(3) 「挿入」 から 「スクリーンショット」を選ぶと次のような画面が出ます。

(4)  ここで、保存したい画面を選びます。すると、Word画面に張り付けられます。

 そのままファイルを、「 Project1302 」などとして保存すると、画像ファイルとしてではなく、Word 文書ファイルとして、「 Project1302.docx 」というようなものが保存されます。(自分の ドキュメント フォルダにあると思いますが、違っていたら探してください。)

 

 これをメールの「添付ファイル」に指定して送ってください。