クラス化 - Javaでシューティングゲームを作ろう
目次
- Javaでシューティングゲームを作ろう
- 自機の左右移動
- 弾発射・はみ出しチェック
- 敵登場・仮想画面
- 当たり判定(敵と弾)・スコア
- 当たり判定(自機と敵)・シーン切り替え
- クラス化
- 継承・Vectorによるキャラ管理・サウンド
- パワーアップ・複数種類の敵
- ペンギンシューティング
今回の題材
- クラス化
一式ダウンロード
ソースの解説
前回まではメインクラスのみで全ての処理を行っていましたが、それではプログラムの見通しが悪くなり、オブジェクト指向の恩恵にも与れないので、今回クラスを分割してオブジェクト指向的なプログラム作りを行いました。その代わり、当たり判定など、前回のプログラムよりバージョンダウンしている部分もあります。
今回は以下のクラス分けを行いました。
-Jiki … 自機クラス
-Teki … 敵クラス
-Tama … 弾クラス
-Key … キー入力管理クラス
-Shooting6 … メインクラス
Jikiクラス フィールド
private Shooting6 app;
メインクラスのオブジェクトへの参照を持つ変数です。メインクラスのメソッド(getImageやgetDocumentBaseなど)を呼び出すために使います。private修飾子をフィールドの前に付けるとこのクラス内でしか使えない変数になります。これによってこの変数が他のクラスから参照・改変されないことが保証されます。
private Image img;
自機画像が入るImageオブジェクトです。敵・弾もまったく同じ名前(img)の変数を使っていますが、オブジェクトが違うのでそれぞれ違う変数だと認識できます。
private int x, y;
自機の座標が入る変数です。これも敵・弾で同じ名前の変数を持っていますが、オブジェクトの違いで判別できるので、jx,tx,exなどと変数を分けなくても大丈夫なのです。
private Tama tama;
自機が発射する弾オブジェクトを保持します。「自機から発射される弾は自機の持ち物」というイメージで自機クラスに含めています。Jikiクラス コンストラクタ
コンストラクタとは、クラスからオブジェクトが生成されたと同時に呼び出されるメソッドのことです。オブジェクトが作られたときに必要な初期化処理などをここで行います。
Jiki(Shooting6 applet) {
app = applet;
コンストラクタのメソッド名はクラス名と同名にしなければなりません。なおかつコンストラクタは戻り値を持つことができません。ここでは、コンストラクタの引数としてアプレット自身を渡すことによって、Jikiオブジェクト内部にアプレットの参照を持つようにしています。
x = app.getw() / 2 - 16;
y = app.geth() - 48;
自機の初期座標を設定しています。アプレットのgetw,gethメソッドはそれぞれアプレットの幅、高さを返すメソッドです。上記の計算によって、自機のx座標をアプレットの中心に、y座標をアプレットの下端から48ドット上に設定しています。前回以前のプログラムのように、アプレットのサイズを決め打ちしないことによって、アプレットのサイズを変更しても自機の位置を同じ場所にすることができるのです。
tama = new Tama(app);
自機オブジェクトが保持する弾オブジェクトを生成しています。Jikiクラス moveメソッド
自機を移動する処理を行います。敵・弾クラスも同名の同じ役割のメソッドを持ちます。
tama.move();
自機が保持している弾の移動メソッドを最初に呼び出します。
if (Key.space) tama.shoot(x + 12, y, 0, -8);
スペースキーが押されたとき、弾オブジェクトのメソッドshootを呼び出し、弾発射を行っています。Jikiクラス drawメソッド
自機を描画する処理を行います。敵・弾クラスも同名の同じ役割のメソッドを持ちます。まず自機が持つ弾オブジェクトのdrawメソッドを呼び出し弾を描画してから、自機を描画しています。
Tekiクラス コンストラクタ
x = (int)(Math.random() * (app.getw() - 32));
y = -32;
敵の座標を画面上端・横位置はランダムに設定します。Tekiクラス moveメソッド
敵の移動を行います。常に下に移動し、一番下に着いたら一番上に移動します。Tamaクラス フィールド
private int vx, vy;
弾のx,y方向の速度です。今回は速度固定ですが、拡張のためにこの変数を用意しておきます。
private boolean appear;
弾が画面上に現れているときtrue、現れていないときにfalseになります。前回のtflagと同じ役割です。Tamaクラス コンストラクタ
appear = false;
ゲーム開始時は弾は発射されていないので出現フラグをクリアします。Tamaクラス shootメソッド
弾を発射するメソッドで、自機オブジェクトから呼ばれます。
if (!appear) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
appear = true;
}
画面上に弾がないとき(!appear)、弾発射を行います。this.xとはこのクラス(Tamaクラス)のフィールドxのことです。フィールドと引数とで同じ名前があったときは引数が優先されるので、thisを付けてフィールドだということを明示しています。ここでは引数で受け取った座標、速度をフィールドに代入しています。Tamaクラス moveメソッド
弾の移動メソッドです。弾が画面上にあるときのみ(appear)、移動を行います。
x += vx;
y += vy;
弾の座標に速度を加えて移動を行います。
if (x < -8 || x > app.getw() || y < -8 || y > app.geth())
appear = false;
弾が画面外に出たときに出現フラグをクリアして弾を消します。Tamaクラス drawメソッド
画面上に弾があるときのみ(appear)、弾を描画します。Keyクラス
キー入力を管理するクラスです。キーリスナーとなるクラスでは、keyPressed,keyReleased,keyTypedメソッドを実装しなければいけませんが、KeyAdapterクラスにはこれらメソッドが既に実装されています(中身は空)。なので、これを継承したKeyクラスでは必要なメソッドのみ定義すれば良いわけです。Keyクラス フィールド
static boolean left, right, space, enter;
←・→・スペース・エンター各キーの状態が入ったクラス変数です。キー入力を取得するオブジェクトではKey.left,Key.rightなどとクラス名で変数にアクセスすることができます。Shooting6クラス フィールド
private Jiki jiki;
private Teki teki;
自機オブジェクトと敵オブジェクトです。弾オブジェクトは自機オブジェクトが生成します。
private volatile Thread gameThread;
volatileで宣言された変数は、他のスレッドからアクセスされても値が保持されます。Thread変数には付けた方が良いらしいです。
private final int SCENE_TITLE = 0;
private final int SCENE_MAIN = 1;
private final int SCENE_OVER = 2;
final修飾子が付いたフィールドは定数です。値の変更はできません。今回はシーン変数に代入する値として、数字では分かりにくいのでそれぞれ定数を定義しています。Shooting6クラス initメソッド
width = getSize().width;
height = getSize().height;
フィールドwidthとheightにアプレットの幅と高さを代入しています。getSizeメソッドはアプレットのサイズを得るメソッドです。
gv = offImage.getGraphics();
前回までは仮想画面のGraphicsオブジェクトを毎フレーム取得していましたが、それでは無駄があるので今回は最初に取得してフィールドとして保存しておきます。
addKeyListener(new Key());
Keyオブジェクトを生成してキーリスナーとして登録しています。
jiki = new Jiki(this);
teki = new Teki(this);
自機オブジェクト、敵オブジェクトを生成し、引数として自分自身、つまりアプレットを渡しています。Shooting6クラス gameMainメソッド
自機と敵オブジェクト自身が、移動・描画でやるべきことを知っているので、ここではそれらを呼び出しているだけです。前回のgameMainメソッドに比べてシンプルで分かりやすくなっていると思います。
jiki.move();
teki.move();
自機・敵の移動を行います。実際の移動は自機・敵オブジェクトに任せています。
jiki.draw(gv);
teki.draw(gv);
自機・敵の描画を行います。仮想画面のGraphicsオブジェクトを引数として渡してそこに描画してもらいます。実際の描画は自機・敵オブジェクトに任せています。Shooting6クラス getw,gethメソッド
アプレットの幅、高さを得るメソッドです。変数width,heightに直接アクセスするのではなく、変数の方はprivate修飾しておき、外部からはこういったアクセスメソッドでアクセスすることによって、他のクラスから値を書き換えられることを防ぎます。Shooting6クラス drawCenterメソッド
Shooting6クラス内部からのみ呼び出されるユーティリティメソッドです。文字列とy座標を引数として渡すと、文字列の横位置が画面の中央になるようにx座標を計算して表示してくれます。
FontMetrics fm = getFontMetrics(gv.getFont());
getFontMetricsメソッドは、引数のフォントの情報を取得するメソッドです。
gv.drawString(str, (width - fm.stringWidth(str)) / 2, y);
FontMetricsオブジェクトのstringWidthメソッドは、引数で渡された文字列のピクセル長を取得します。