【Java:的当てゲーム】基本クラスの定義と画面表示までの実装

2018年8月16日

今回からはプログラムの分野です。

前の記事 的当てゲーム制作Top 次の記事

まず最初は作成するクラスを簡単に定義してしまいます。

ゲームプログラムのベースとなるウィンドウ表示や画像読み込み、効果音、BGM再生、マウス入力に関しては、すべてライブラリの機能を利用して実装することとします。

このライブラリはこれまでこのサイトで記事にしてきた内容を詰め込んでいます。これらの基本機能はライブラリ内のGTKManagerクラスを中核とし、GameViewクラスを利用することでゲームの処理を実装できるようにしています。

今回はこのライブラリのSwing系APIを利用して実装していきます。




利用するライブラリクラス

利用する主なライブラリクラスと簡単な説明をしておきます。

・GTKManager
画像読み込み、BGM再生、効果音再生、ウィンドウ表示、画面遷移等のゲーム制作に必要とされるであろう基本機能が利用できます。

・GameView
ゲームの1画面を表すクラスとして利用できます。マウス入力の検出メソッドも利用できます。

・Circle
円を表すクラス。的のクラスで利用する。

・GraphicsUtil
描画の透明設定、アンチエイリアシングなど描画機能の補助クラス。

・Animationクラス群
フレームカウンターベースの簡易アニメーションクラス群。

・GTKFileUtil
ファイル入出力機能を簡単に利用できるようにしたメソッド群

クラス定義

今回は規模がかなり小さいので、初期段階である程度クラスを定義できそうです。

まずは、画面クラスです。今回はタイトルランキングゲーム画面の3種類が想定されます。

次に考えやすいのはゲーム画面のオブジェクトです。ブロックと的を出現させる仕様ですので、これら2種類はクラス化しておきます。

次にゲームのリソース管理クラスです。まあ呼び出しが面倒そうな機能はすべてこのクラスに集約し、どこからでも呼び出せるようにしておきましょうか。今回は画像、効果音、BGM、画面遷移の機能を集約してしまいます。リソースはプログラムのルート階層にそれぞれse、img、bgm等のフォルダを作成してそこを呼び出すようにします。

リソースごとに管理クラスを分けてもいいのですが、使用するリソースも少ないですし規模も小さいので全部一個にまとめちゃいます。

ただ、ファイル入出力処理はランキングのセーブ機能がある分少々規模が大きくなる可能性があるので、これだけ別クラスに分けておくことにします。ファイルの出力先はdataフォルダにしておきましょうか。

画面遷移はわかりやすいようにEnum型の引数のみで遷移できるようにしておきます。

これらの管理クラスは全てstaticメソッドでアクセスできるようにしておきます。

あとはmainメソッドようにクラスを一つ作っておきます。

基本管理クラスと画面表示の実装まで

今回は初めてなのでゲームの起動から基本管理クラスと画面表示の実装までをやってみたいと思います。

まずは各種画面クラスを作成します。

タイトル画面をTitleModeクラス
ゲーム画面をGameModeクラス
ランキング画面をRankingModeクラス
とします。

各クラスは1画面を表すクラスであるため、ライブラリのGameViewクラスを継承させます。

このクラスを継承すると、特定のメソッドをオーバーライドするだけで、ゲームループの処理や、マウスの入力機能を実装できるようになっています。

今回はdrawメソッドをオーバーライドしてゲームループの処理を記述していきます。

それでは各画面を遷移する最小プログラムをそれぞれ作成してみます。

配置パッケージはすべてルート階層に配置するものとします。

TitleMode.java
import java.awt.Graphics;

import com.nompor.gtk.GameView;

public class TitleMode extends GameView{
	public void draw(Graphics g) {
		g.drawString("タイトルダゾ", 100, 100);
	}
}
GameMode.java
import java.awt.Graphics;

import com.nompor.gtk.GameView;

public class GameMode extends GameView{

	public void draw(Graphics g) {
		g.drawString("ゲーム画面ダゾ", 100, 100);
	}
}
RankingMode.java
import java.awt.Graphics;

import com.nompor.gtk.GameView;

public class RankingMode extends GameView{

	public void draw(Graphics g) {
		g.drawString("ランキングダゾ", 100, 100);
	}
}

これでゲーム画面は定義できました。

さらに各種画面を表すEnumクラスも作成しておきましょう。

ViewType.java
public enum ViewType {
	GAME,TITLE,RANKING;
}

次に基本機能管理クラスとなるAppManagerクラスを作成し、画像の取得、効果音の再生、BGMのループ再生、画面遷移を行う機能を実装します。

まずゲーム画面の起動はstartメソッドで実行します。ゲーム画面の起動はGTKManager.startメソッドを実行すると起動できます。

次に画像、効果音のロード処理を実行します。

画像、効果音のロード完了後にゲームBGMをループ再生させます。

これらリソースの利用はGTKManagerのgetImage、playSE、loopBGM等で処理できるようになっていますので、それを呼び出すだけにしておきます。これらの画像、効果音のメソッドは一度読み込まれるとロードされ、以降はロード処理をしません。サンプルではload処理を実行していますが、事前ロードはしなくても良い場合は使用されるときに自動ロードされます。ロードデータの解放はunloadメソッドで実行できますが、総リソース量が少ないため今回は利用しません。画像、効果音再生などのメソッドは1リソース1メソッド用意することにします。(量が多くなるとそんなことやってられませんけどね・・・)

画面遷移はchangeメソッドで実行できるようにします。ViewType型を渡すことで遷移できるようにプログラムします。

これが実装結果になります。

AppManager.java
import java.awt.Image;

import com.nompor.gtk.GTKManager;
import com.nompor.gtk.GameView;

//ゲームの画像や音声再生、画面遷移を制御するメソッド群
public class AppManager {

	//ゲームBGM開始
	public static void gameBGMStart() {
		GTKManager.loopBGM("bgm/bgm.wav");
	}

	//ゲームBGMの停止
	public static void gameBGMStop() {
		GTKManager.stopBGM();
	}

	//的破壊効果音を鳴らす
	public static void breakSE() {
		GTKManager.playSE("se/break.wav");
	}

	//メニュー選択効果音を鳴らす
	public static void selectSE() {
		GTKManager.playSE("se/select.wav");
	}

	//カウントダウン効果音を鳴らす
	public static void countSE() {
		GTKManager.playSE("se/count.wav");
	}

	//ゲーム開始効果音を鳴らす
	public static void startSE() {
		GTKManager.playSE("se/start.wav");
	}

	//的画像を取得
	public static Image getTargetImage() {
		return GTKManager.getImage("img/mato.png");
	}

	//ブロック画像を取得
	public static Image getBlockImage() {
		return GTKManager.getImage("img/kabe.png");
	}

	//背景画像を取得
	public static Image getBackImage() {
		return GTKManager.getImage("img/back.png");
	}

	//ゲームプログラムを開始する
	public static void start(ViewType type) {
		//ウィンドウの表示
		GTKManager.start("的当てゲーム","img/icon.png",800, 600, get(type));

		//事前にロードしなくても良いが、使用する時にロードすると遅くなるかもしれないので、事前にロードする

		//初期化時に全画像ファイルをメモリ上に展開する
		GTKManager.loadImage("img/mato.png");
		GTKManager.loadImage("img/kabe.png");
		GTKManager.loadImage("img/back.png");

		//初期化時に全効果音ファイルをメモリ上に展開する
		GTKManager.loadSE("se/break.wav");
		GTKManager.loadSE("se/start.wav");
		GTKManager.loadSE("se/select.wav");
		GTKManager.loadSE("se/count.wav");

		//BGMの再生
		AppManager.gameBGMStart();
	}

	//ゲーム画面を遷移する
	public static void change(ViewType type) {
		GTKManager.changeViewEvent(get(type));
	}

	//ウィンドウ横幅取得
	public static int getW() {
		return GTKManager.getWidth();
	}

	//ウィンドウ縦幅取得
	public static int getH() {
		return GTKManager.getHeight();
	}

	//ゲーム画面を取得する
	private static GameView get(ViewType type) {
		switch(type) {
		case TITLE:
			return new TitleMode();
		case GAME:
			return new GameMode();
		case RANKING:
			return new RankingMode();
		default:
			break;
		}
		return null;
	}

	//アプリケーションを終了する
	public static void end() {
		GTKManager.end();
	}
}

最後にmainメソッドを持つクラスを作成して今回の作業はここで終了としておきましょう。

2018/02/24追記。Mac環境、Linux環境でテストしたところ、致命的な動作不良が起こる場合があるようなので、新たにchangeViewEventというメソッドを追加し、それを呼び出すように修正しました。こちらのメソッドで遷移要求すると、ゲームループスレッドによる画面遷移処理が実行されます。

AppStarter.java
public class AppStarter{
	public static void main(String[] args) {
		//初期化時はタイトルを表示する
		AppManager.start(ViewType.TITLE);
	}
}
実行結果

本稿ではAppStarter.javaとAppManager.java、ViewType.javaのプログラムはここで完成とします。

Java

Posted by nompor