学生向けプログラミング入門

学生向けにプログラミングを解説。Java、C++、Ruby、PHP、データベース

Javaプログラミング入門その31 簡単なゲームを作成

<<前  [TOP]  次>>


以前にスレッドを生成する方法としてRunnableインターフェイスを実装する方法を紹介しましたが、今回はThreadのサブクラスを作成して、スレッドを実現します。
スレッド自体の仕組みは以前と同様で、run()メソッドに記述された処理がスレッドとして実行されます。
そして、スレッドを記述したインスタンスのstart()メソッドを呼ぶことで実行されます。

public class MyThread extends Thread {

    /**コンストラクタ*/
    public MyThread() {
        //コンストラクタの処理
     }

    /**スレッド処理*/
    public void run() {
        //スレッド処理
     }

    public static void main(String[] args) {
        //スレッド生成
         MyThread test = new MyThread();
        //スレッド開始
         test.start();
    }
}



次のThreadを継承したサンプルプログラムを作成してみましょう。


MyTimer.java 直

/**
*Threadを継承したクラス
*/

public class MyTimer extends Thread {
	
	private int count=0;

	/**コンストラクタ*/
	public MyTimer(int sec) {

		//super(); は省略
		count = sec;
	}

	/**ループ処理のスレッド*/
	public void run() {

		while (count > 0) {

			try {
				Thread.sleep(1000);
			}
			catch (InterruptedException ex) {

				System.err.println(ex);
			}
			count--;
		}
	}

	/**残り時間の取得*/
	public int getTime() {

		return count;
	}

	/**mainメソッド*/
	public static void main(String[] args) {

		MyTimer timer = new MyTimer(10);
		timer.start();
		
		while (timer.getTime() > 0) {
			System.out.println("残り" + timer.getTime() + "秒");
		}
	}
}



早速実行してみましょう。
時間が減って0になるプログラムです。
「残り〜秒です」と表示し続けます。
10秒のタイマーです。
これは次のプログラムでも利用しています。





以下のサンプルプログラムを作成してみましょう。


JumpGame.java 直

/**簡単なゲーム
*時間制限があり
*右から来る障害物を[SPACE]でジャンプして回避
*[s]キーで再ゲーム
* ハイスコアの更新があります
*/

import java.awt.*;
import java.awt.event.*;

public class JumpGame extends Canvas implements KeyListener, Runnable {

	private final int MAX_IMAGE = 7;		//キャラのイメージ枚数
	private final int J_STAY = 0;		//ジャンプしていない
	private final int J_UP = 1;			//ジャンプして上昇中
	private final int J_DOWN = 2;		//ジャンプして下降中

	private Graphics offg;			//ダブルバッファリング用
	private Image offImage;
	
	private int mx, my, mw, mh, oldmy, mspace;	//キャラ用
	private int mpx, mpy;			//キャラの中心点
	private int imCount, imNum;			//キャラのイメージカウンターと番号
	private int bx, by, bw, bh, oldbx, bspace;	//障害物用
	private int sx, sy, sc;			//スコア表示用
	private int hsx, hsy, hsc;			//ハイスコア表示用
	private int tx, ty, time;			//残り時間表示用
	private int meterx, metery, meterw, meterh;	// メーター表示用

	private int meterc;					//メーター減少部分 / 秒
	private Image[] im = new Image[MAX_IMAGE];	//キャラのイメージ
	private Image box, back, meter;			//障害物と背景、メーターのイメージ
	private boolean gameFlag;				//ゲーム進行フラグ
	private int jFlag;					//アニメーション(ジャンプ)の状態
	private MediaTracker tracker;			//イメージ通知用
	private int key;					//キーコード取得用
	private MyTimer timer;				//残り時間設定のためのタイマー
	private PlayAudio bgm, se;				//音声の設定

	/**コンストラクタ*/
	public JumpGame() {

		setSize(500, 250);
		setBackground(Color.black);
		addKeyListener(this);

		bgm = new PlayAudio("merrygo1.mid");	//音声の設定
		se = new PlayAudio("bom01.wav");

		Toolkit tk = Toolkit.getDefaultToolkit();
		tracker = new MediaTracker(this);		//メディアトラッカーにイメージを登録

		box = tk.getImage("box.gif");
		tracker.addImage(box, 1);
		
		for (int i=0; i<MAX_IMAGE; i++) {

			im[i] = tk.getImage("char" + i + ".gif");
			tracker.addImage(im[i], 0);
		}

		//背景イメージを生成と登録
		back = tk.getImage("back.gif");
		tracker.addImage(back, 2);

		//背景イメージを生成と登録
		meter = tk.getImage("meter.gif");
		tracker.addImage(meter, 2);

		//全イメージを読み込むまで待つ
		try {
			tracker.waitForAll();
		}
		catch (InterruptedException ex) {
			System.err.println(ex);
		}

		//キャラ、障害物、メーターのイメージの大きさを取得
		mw = im[0].getWidth(this);
		mh = im[0].getHeight(this);
		bw = box.getWidth(this);
		bh = box.getHeight(this);

		meterw = meter.getWidth(this);
		meterh = meter.getHeight(this);

		//キャラの中心点を計算
		mpx = mw/2;
		mpy = mh/2;

		//ハイスコアの初期設定はここでのみ行う
		hsc = 0;
		hsx = 200;
		hsy = 10;		//ハイスコアの表示座標
	}

	/**ゲーム開始処理*/
	public void gameStart() {

		gameInit();			//変数の初期化
		timer = new MyTimer(30);	//タイマーの設定(30秒)
		bgm.loop();			//音声ループ
		new Thread(this).start();	//ゲーム開始
		timer.start();			//秒タイマー開始
	}

	/**ゲームに使用する変数の初期化*/
	public void gameInit() {

		//キャラのx,y座標、ジャンプの間隔
		mx = 100;
		my = 200;
		mspace = 5;

		//障害物のx,y座標、移動間隔
		bx = 450;
		by = 200;
		bspace = 5;

		jFlag = J_STAY;		//ジャンプしていない=0(上昇=1、下降=2)
		gameFlag = true;		//ゲームオーバーならfalse

		sx = 10;			//スコアの表示座標
		sy = 10;
		sc = 0;			//スコアは0点から

		tx = 400;			//残り時間の表示座標
		ty = 10;
		time = 30;		//表示用に残り時間を初期化

		meterx = 400;		//残り時間メーターの表示座標
		metery = 20;
		meterc = meterw/30;	//メータ減少部分 / 秒

		imCount = 0;		//キャライメージの増減カウンター
		imNum = 0;		//キャライメージ1枚目からスタート
	}

	/**メインの処理(スレッド処理)*/
	public void run() {

		while (gameFlag == true) {			//gameFlagがfalesになるまで繰り返し

			try {
				Thread.sleep(20);		//20ミリ秒スリープ
			}
			catch ( InterruptedException ex) {
				System.err.println(ex);
			}
			repaint();
		}
	}

	/**再描写が必要な場合の処理*/
	public void update(Graphics g) {

		oldmy = my;
		oldbx = bx;

		//キャラクタ、障害物の消去(全画面消去)
		offg.setColor(Color.white);
		offg.fillRect(0, 0, getSize().width, getSize().height);

		//口を空けるアニメか、閉じるアニメかを設定
		if (imNum >= (MAX_IMAGE-1)) {
	
			imCount = -1;
		}
		else if (imNum <= 0) {

			imCount = 1;
		}
		imNum+=imCount;

		//スペースキーが押されたときの処理
		if (jFlag == J_UP) {

			my -= mspace;

			if (my <= 100) {		//この位置まで来たらフラグ変更

				jFlag = J_DOWN;
			}
		}

		else if (jFlag == J_DOWN) {	//下降中

			my += mspace;

			if ( my >= 200 ) {	//この位置まで来たらフラグ変更

				jFlag = J_STAY;
			}
		}

		//障害物を左へ移動
		bx -= bspace;

		if (bx < 0) {

			bx = 450;
			sc++;
		}

		//残り時間の取得
		time = timer.getTime();

		//キャラと障害物が重なった場合(キャラの中心点で判定か、
		//残り時間が無くなったときはゲームオーバー
		if ( (mx+mpx >= bx && mx+mpx < bx+bw && my+mpy >= by && my+mpy < by+bh) || time == 0) {

			gameFlag = false;
			se.play();		//当たった音声再生
			bgm.stop();		//ループしていた音声を停止

			//ハイスコアの更新チェック
			if (sc > hsc) {

				hsc = sc;
			}
		}

		//オフスクリーン・バッファへの描き込み。実際にはまだ見えない
		offg.drawImage(back, 0, 0, this);		//背景

		//残り時間メーター
		//第2〜5引数(表示範囲)、第6〜9引数(元イメージの範囲)
		offg.drawImage(meter, meterx, metery, meterx+meterc*time, metery+meterh, 0, 0, meterc*time, meterh, this);

		offg.drawImage(im[imNum], mx, my, this);
		offg.drawImage(box, bx, by, this);

		//スコアの表示
		offg.setColor(Color.red);
		offg.drawString("Score:" + sc, sx, sy);

		//ハイスコアの表示
		offg.setColor(Color.yellow);
		offg.drawString("Hi-Score:" + hsc, hsx, hsy);

		//残りタイムの表示
		offg.setColor(Color.blue);
		offg.drawString("Time:" + time, tx, ty);

		if (gameFlag == false) {

			offg.setColor(Color.red);
			offg.drawString("Game Over!!", 100, 100);
			offg.drawString("Start [S] Key", 100, 150);
		}

		//オフスクリーン・バッファに描かれたものを画面に表示
		g.drawImage(offImage, 0, 0, this);
	}

	/**キャンバスで描画の必要があるときに呼ばれるメソッド*/
	public void paint(Graphics g) {

		//オフスクリーン・バッファの領域がない場合は作成
		if (offg == null) {

			offImage = createImage(getSize().width, getSize().height);
			offg = offImage.getGraphics();
		}

		//offImageを描画
		g.drawImage(offImage, 0, 0, this);
	}

	/**キーが押されたときの処理*/
	public void keyPressed(KeyEvent evt) {
		
		key = evt.getKeyCode();

		//ゲームオーバー時に[S]キーでゲーム開始
		if (gameFlag == false && key == evt.VK_S) {

			gameStart();	//ゲーム開始
		}

		//スペースキーが押されて、
		//なおかつジャンプしてないときのみ処理を行う
		if (key == evt.VK_SPACE && jFlag == J_STAY) {

			jFlag = J_UP;
		}
	}

	/**その他のキー処理*/
	public void keyReleased(KeyEvent evt) { }
	public void keyTyped(KeyEvent evt) { }


	/**main()*/
	public static void main(String[] args) {

		JumpGame sample = new JumpGame();

		//クローズボタンで終了するように自作したクラス
		MyFrame frame = new MyFrame("JumpGame");
		frame.add(sample);
		frame.pack();
		frame.setVisible(true);

		sample.gameStart();
	}
}



なお、このプログラムを実行するには以下のイメージファイル(box.gif、back.gif、meter.gif)、画像ファイル(char0.gif〜char6.gif)、音楽ファイル(merrygo1.mid、bom01.wav)が必要になりますのでプログラムを作成するフォルダに保存してください。
back.gif 直
box.gif 直
meter.gif 直
char0.gif 直
char1.gif 直
char2.gif 直
char3.gif 直
char4.gif 直
char5.gif 直
char6.gif 直
merrygo1.mid 直
bom01.wav 直


では早速実行してみましょう。
前に作成したMyFrame.javaを利用していますのでクローズボタンを使って終了できます。
上で作成したMyTimer.javaを利用しています。


時間制限があります。
右から来る障害物を[SPACE]でジャンプして回避します。
[s]キーで再ゲーム開始です。
ただし、ゲームが始まったら一度、マウスで画面のどこかをクリックしなければキーボードの入力を受け付けてくれません。
ハイスコアの更新があります。








<<前  [TOP]  次>>