一人テニスゲーム、完成!!

エディタ(導入必須)

繰り返しになりますが、xyzzy、Emacsなどのエディタの導入をお願いします。

エディタの導入方法はこちらです。

特に、Windows の方は xyzzy を必ず使って下さい。
プログラミングが苦手で、上手くいかず質問してくる人ほど xyzzy を使っていないケースが多く、非常に困ります
自力でできず、TA・SAに質問しようという人は、必ず xyzzy を使うようにして下さい。

完成へ向けて

まず、本日の講義資料をダウンロードしてください。
第6回資料(Windows用)
第6回資料(Mac用)

ボールの出てくる位置をバラバラにしてみる。

前回の授業では、画面にボールを複数出すことを学びました。
それを元に、ボールの出てくる場所をバラバラにしてみましょう。

乱数

乱数とは、ランダムな数値(バラバラな数字)のことです。
要するに、サイコロと同じだと考えて下さい。

サイコロを振ると、1 〜 6 の数字のどれかを返します。
(つまり、サイコロは 1 〜 6 の乱数を生成しています。)
GameCanvas では、gc.rand(1, 6); と書くと、サイコロを一回振ったのと同じことになります。

以下のような感じで使用します。

// 受け取る変数を定義
int random_num;

(中略)

// gc.rand(1, 6); は 1 〜 6 のうちいずれかの値を返し、random_num はその値を保持する
random_num = gc.rand(1, 6);
	

実際に使ってみましょう。
ダウンロードした資料を解凍して、Sample01フォルダ直下にあるgame.javaを開いて下さい。
Sample01 は、前回の講義の Sample03 を元に変更を加えたものです。見比べながら考えてみましょう。

/********* 初期化の手順はこちらに *********/
public void initGame()
{
    for(int i=0; i<10; i=i+1)
    {
        // ボールの座標を初期化する
        // X座標を、0 〜 600 の範囲の乱数で初期化する
        ball_x[i] = gc.rand(0, 600);
        ball_y[i] = 0;

        (中略)
    }

   (以下略)
}
	

前回の講義の Sample03 と比べて、変更した箇所は 1 ヶ所だけです。
ball_x[i] = gc.rand(0, 600); で、ボールのX座標を 0 〜 600 の範囲のいずれかの数字で初期化しています。

コンパイルして実行してみましょう。
ボールの現れる場所がバラバラになっているのが分かりますね。

スコア

スコア(点数)をつけてみましょう。
Sample02フォルダ直下にあるgame.javaを開いて下さい。

このプログラムでは、弾をラケットで打ち返す度に点数を10点追加しています。
現在の点数は、画面の左上に描画しています。

それでは、順に見ていきましょう。 まずは変数定義と初期化処理です。
変数定義には、点数を保持する score を追加しています。
初期化部分では、score を 0 で初期化しています。

/********* 変数定義はこちらに *********/
// 型 変数名; の順に書いて定義する

(中略)

// 点数
int score;

/********* 初期化の手順はこちらに *********/
public void initGame()
{
    (中略)

    // 点数を初期化
    score = 0;
}
	

続いては、更新処理です。
ボールが当たった際に、点数を10点足しています。
score = score + 10; の部分です。

/********* 物体の移動等の更新処理はこちらに *********/
public void updateGame()
{
    (中略)

    // ボールと、ラケットが当たった場合に真
    if(gc.checkHitRect(racket_x, racket_y, racket_w, racket_h, ball_x, ball_y, 24, 24))
    {
        // ボールのY方向の速度を反転させる
        ball_speed_y = -ball_speed_y;

        // 点数を10点加算する
        score = score + 10;
    }
}
	

最後は描画処理です。
ここでは、画面上に点数を書く処理が追加されています。

/********* 画像の描画はこちらに *********/
public void drawGame()
{
    (中略)

    // 点数を描画します
    gc.drawString("Score " + score, 0, 0);
}
	

gc.drawString("Score " + score, 0, 0); で、画面の左上に点数を描画しています。

文字列の後に + 変数名(あるいは数字)と書くと、文字列に数字をつなげることができます。
"Score " + score で、"Score scoreの中の数字" という文字列になります。

ハイスコア

今までゲームをプレイした中で一番良かった点数(スコア)のことを、ハイスコアといいます。
ハイスコアを画面に表示してみましょう。
Sample03フォルダ直下にあるgame.javaを開いて下さい。
Sample03 は、Sample02 を元に変更を加えたものです。見比べながら考えてみましょう。

まずは変数定義と初期化処理です。
変数定義には、ハイスコアを保持する変数 high_score を追加しています。
初期化部分では、high_score を 0 で初期化しています。

/********* 変数定義はこちらに *********/
// 型 変数名; の順に書いて定義する

(中略)

// 点数
int score;
// ハイスコア
int high_score;

/********* 初期化の手順はこちらに *********/
public void initGame()
{
    (中略)

    // 点数を初期化
    score = 0;
    // ハイスコアを初期化
    high_score = 0;
}
	

続く更新処理では、以下のように書き換えています。

/********* 物体の移動等の更新処理はこちらに *********/
public void updateGame()
{
    (中略)

    // ボールが画面の下を越えた場合
    if(ball_y > 480)
    {
        // 最初の状態に戻す
        // ボールの座標を初期化する
        ball_x = 0;
        ball_y = 100;

        // ラケットの座標を初期化する
        racket_x = 270;
        racket_y = 450;

        // 点数を初期化する
        score = 0;
    }

    // ボールと、ラケットが当たった場合に真
    if(gc.checkHitRect(racket_x, racket_y, racket_w, racket_h, ball_x, ball_y, 24, 24))
    {
        // ボールのY方向の速度を反転させる
        ball_speed_y = -ball_speed_y;

        // 点数を10点加算する
        score = score + 10;

        // 点数がハイスコアを上回っていたら
        if(score > high_score)
        {
            // ハイスコアを更新
            high_score = score;
        }
    }
}
	

ボールが当たった際に、点数がハイスコアを上回っていたら、ハイスコアの更新をする処理を追加しています。
加えて、ボールが画面下を越えた場合に、必要な変数を初期化する処理を追加しています。

 

最後は描画処理です。
スコアの隣りに、ハイスコアを描画する処理を追加しています。

/********* 画像の描画はこちらに *********/
public void drawGame()
{
        (中略)

    // 点数を描画します
    gc.drawString("Score " + score, 0, 0);
    gc.drawString("HighScore " + high_score, 200, 0);
}
	

ハイスコアのセーブ・ロード

GameCanvas では、データをセーブデータに保存し、後で保存したデータを読み込むことが&できます。

では、実際の使い方を見てみましょう。
Sample04 フォルダ直下にあるgame.javaを開いて下さい。
Sample04 では、ゲームを終了する際にハイスコア情報を保存し、ゲームを起動した時に保存したハイスコア情報を読み込んでいます。

/********* 初期化の手順はこちらに *********/
public void initGame()
{
    (中略)

    // ハイスコアを読み込む
    high_score = gc.load(0);

}

    (中略)

/********* 終了時の処理はこちらに *********/
public void finalGame()
{
    // ハイスコアをセーブ
    gc.save(0, high_score);
}
	

finalGame() は、右上の×ボタンを押してゲームを終了した場合に実行される処理です。
ここでは、終了した時にハイスコアを 0 番にセーブしています。

initGame() では、0 で初期化する代わりに、0 番にセーブしたデータを読み込んでいます。
セーブデータが無い場合には 0 が返されるので、セーブデータが無い場合でもちゃんと 0 で初期化することができます。

関数を定義する

※ 難しい項目ですので、腕に自信のある方以外は読み飛ばしてもらってかまいません。

関数を使うと、複数の命令を1つにまとめることができます。
実は、今までの講義で使ってきた gc.drawImage などの gc. で始まる命令は全て関数です。
GameCanvas 側で用意されたこれらの関数は、複雑な処理をひとつにまとめ、簡単にゲームを作れるように工夫されています。

では、関数の作り方を学んでいきましょう。 関数を使いこなすことで、より良いプログラムが書けるようになります。
関数の自作に挑戦してみましょう。

Sample05 フォルダ直下にあるgame.javaを開いて下さい。
このプログラムは、画面を青く塗りつぶすだけの単純なものです。

    (中略)

/********* 画像の描画はこちらに *********/
public void drawGame()
{
    // 青く塗りつぶす関数を呼ぶ
    clearBlue();
}

/********* 終了時の処理はこちらに *********/
public void finalGame() {}


// 青で画面を塗りつぶす関数を定義
void clearBlue()
{
    // 色を青にセット
    gc.setColor(0, 0, 255);
    // 画面を塗りつぶす
    gc.fillRect(0, 0, 640, 480);
}
	

一番下で、clearBlue という関数を定義しています。
この関数の中には、2 つの命令が記述されています。

定義した関数 clearBlue は、drawGame 内で呼ばれています。
clearBlue が呼ばれると、clearBlue の中身が上から順に実行されます。
(ここでは、色を青にセットし、その色で画面を塗りつぶしている)

もう一つ具体例を見てみましょう。
Sample06 フォルダ直下にあるgame.javaを開いて下さい。

Sample06 は、Sample04 を元に変更を加えたものです。
ボールが画面の下を超えた場合の初期化処理を、関数としてまとめています。

一番下に、resetGameValue という関数を定義しています。
この関数の中に、ゲーム中に変化する変数の初期化命令をまとめています。

// ゲームスタート時の状態に初期化する関数を定義
void resetGameValue()
{
    // ボールの座標を初期化する
    ball_x = 0;
    ball_y = 100;

    // ラケットの座標を初期化する
    racket_x = 270;
    racket_y = 450;

    // 点数を初期化する
    score = 0;
}
	

そして、このresetGameValue関数を、必要に応じて適宜呼び出しています。

/********* 初期化の手順はこちらに *********/
public void initGame()
{
   (中略)

    // ゲーム中に変更される変数を初期化
    resetGameValue();
}

/********* 物体の移動等の更新処理はこちらに *********/
public void updateGame()
{
    (中略)

    // ボールが画面の下を越えた場合
    if(ball_y > 480)
    {
        // ゲーム開始の状態に初期化する関数を呼び出す
        resetGameValue();
    }

    (中略)
}
	

resetGameValue 関数は、ゲーム内で変化していく値だけを初期化する関数です。
initGame 内と、updateGame 内でボールが画面の下を超えた場合に呼ばれています。

このように、いくつかの場所に同じような処理がまとまっている場合に、関数を使うと非常に便利です。
同じ内容を何度も書かずにすみますし、変更する場合も、一箇所書き換えるだけですみます。

関数の引数

関数を呼び出す際にカッコの中に書く数字のことを、引数と言います。
例えば、gc.setColor(0, 0, 255); は、3 つの引数を持つ関数です。
Sample05, Sample06 で作った関数は、カッコの中になにもないので、引数無しの関数です。

引数を活用した関数を定義してみましょう。
Sample07 フォルダ直下にあるgame.javaを開いて下さい。
引数に渡した数値を使って、画面を塗りつぶす関数を定義しています。

    (中略)

/********* 画像の描画はこちらに *********/
public void drawGame()
{
    // 画面を塗りつぶす関数を呼ぶ
    // ここでは、赤で塗りつぶしている
    clearWindow(255, 0, 0);
}

/********* 終了時の処理はこちらに *********/
public void finalGame() {}


// 引数に渡した数値を使って、画面を塗りつぶす関数を定義
void clearWindow(int r, int g, int b)
{
    // 色を青にセット
    gc.setColor(r, g, b);
    // 画面を塗りつぶす
    gc.fillRect(0, 0, 640, 480);
}
	

定義された clearWindow 関数には、int 型の3つの引数 r, g, b があります。
そして、drawGame では、255, 0, 0 の引数で drawGame を呼び出しています。

ここで呼び出された clearWindow の引数はそれぞれ、r は 255、g は 0、b は 0 です。
つまり、gc.setColor(r, g, b); は、gc.setColor(255, 0, 0); として実行されます。

関数の戻り値

関数は、値を返すこともできます。この返す値を、戻り値といいます。
例えば、gc.rand は、int 型の値を返す関数です。

それでは、戻り値の例を説明します。
Sample08 フォルダ直下にあるgame.javaを開いて下さい。
このプログラムでは、引数に与えた数字を 2 乗して返す関数を定義しています。

/********* 変数定義はこちらに *********/
// 型 変数名; の順に書いて定義する
int value;

(中略)

/********* 物体の移動等の更新処理はこちらに *********/
public void updateGame()
{
    value = square(7);
}

(中略)

// 引数に渡した数値を2乗して返す関数を定義
int square(int n)
{
    return n * n;
}
	

square という、int 型の引数 1 つをとり、2乗した値を返す関数を定義しています。
return 値; と書くと、その値が返されます。
return された時点で関数は終了するので、return より下に命令を書いても実行されません。注意しましょう。

square の前に書いてある int は、戻り値の型です。
ここでは、int 型の値を返すと定義しています。
なお、ここに void と書くと、値を返さない関数となります。
その場合は、return 文を書かなくてもエラーにはなりません。

updateGame 内で、square() を呼んでいます。
この関数の中では、n * n で引数を二乗して、return でその結果を返しています。

課題

今までの課題と、今回の授業内容を組み合わせて、1人用テニスゲームを完成させてください。
自分なりに工夫して、面白いゲームを作ってみましょう。

また、余裕のある方は、関数にチャレンジしてみましょう。

戻る