[Android] インベーダー・ゲームを作る (2) オブジェクト指向 ================================================================================ [Android] インベーダー・ゲームを作る (1) シンプル http://blog.goo.ne.jp/marunomarunogoo/d/20120405 のものを、クラスを導入して作る。 ゲームの制御をするのは、CanvasView クラス。 ゲームで出てくるアイテムとしては、インベーダー、砲台、弾だが、それぞれ、Invader、 Gun、Bullet クラスとして定義する。 それぞれは、下記のインターフェースのいくつかを実装する形で作る。 IDrawable 描画できる項目のインターフェース IAttacher 攻撃物のインターフェース ITarget 攻撃目標になるアイテムのインターフェース IMoveAutomatic 自動で動くアイテムのインターフェース IMoveManual 手動で動かすアイテムのインターフェース クラスの概要(アクティビティを除く。スーパークラス、インターフェースも記述) --- AbstractItem インベーダー・ゲーム上のアイテムのスーパークラス。 IDrawable Bullet 弾のクラス。 AbstractItem、IAttacher、IMoveAutomatic CanvasView インベーダー・ゲームの制御。 Gun 砲台のクラス。 AbstractItem、IMoveManual Invader インベーダーのクラス AbstractItem、ITarget、IMoveAutomatic --- クラス図 --- IDrawable A |---|> IAttacher | |---- Bullet ---| | | |---|> IMoveAutomatic | | | AbstractItem <|---+---- Invader -----+-|> ITarget | |--- Gun -------|> IMoveManual --- ▲ アクティビティ □ Invader11Activity.java ※ Invader01Activity.java と同じ ▲ ビュー 表示とあわせて、ゲームをコントロールする。 定数 --- int BLOCK_SIZE 描画のときのブロックの大きさ int DRAWING_INTERVAL 描画間隔(ミリ秒) int NOT_VISIBLE 非表示 --- フィールド --- Bullet bullet 弾 Gun gun 砲台 Invader invader インベーダー int score 得点 TextView scoreView 得点を表示するビュー --- メソッド --- public TextView getScoreView() scoreView を取得する。 public boolean onTouchEvent(android.view.MotionEvent event) public void setScoreView(android.widget.TextView scoreView) scoreView を設定する。 protected void onDraw(android.graphics.Canvas canvas) protected void onSizeChanged(int w, int h, int oldw, int oldh) private void clearScreen(android.graphics.Canvas canvas) canvasをクリアする。 private void drawScore(int score) スコアを表示する。 private void init() すべてのコンストラクターで共通の処理。 --- □ CanvasView.java --- package jp.marunomaruno.android.invader; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.ImageView; import android.widget.TextView; /** * インベーダー・ゲーム。 * * @author marunomaruno * @version 1.0, 2012-03-06 */ public class CanvasView extends ImageView { /** * 描画間隔(ミリ秒) */ public static final int DRAWING_INTERVAL = 50; /** * 得点 */ private int score = 0; /** * 得点を表示するビュー */ private TextView scoreView; /** * インベーダー */ private Invader invader; /** * 砲台 */ private Gun gun; /** * 弾 */ private Bullet bullet; public CanvasView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public CanvasView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CanvasView(Context context) { super(context); init(); } /** * すべてのコンストラクターで共通の処理。 */ private void init() { score = 0; // 得点 // インベーダーの生成(画面左上から) invader = new Invader(this); // 弾の生成(まだ非表示) bullet = new Bullet(this); System.out.println(invader); System.out.println(bullet); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); System.out.printf("onSizeChanged: (%d, %d)%n", w, h); // 砲台の設定 gun = new Gun(this); System.out.println(gun); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 画面をクリアする clearScreen(canvas); // インベーダーを表示する invader.draw(canvas); invader.resetColor(); // 弾を表示する bullet.draw(canvas); // 砲台を表示する gun.draw(canvas); // インベーダーを指定方向に移動する invader.move(); // 弾を移動する bullet.move(); // 弾が当たったら得点する if (invader.isHit(bullet)) { score++; invader.setColor(Invader.HIT_COLOR); bullet.setNotVisible(); // 弾の表示を消す // 得点を表示する drawScore(score); } // 指定ミリ秒分ウエイトする try { Thread.sleep(DRAWING_INTERVAL); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 指をタッチした assert true; // 何もしない break; case MotionEvent.ACTION_MOVE: // 指を動かしている gun.move((int) event.getX(), gun.y); // 横に動かす break; case MotionEvent.ACTION_UP: // 指を離した bullet = gun.shot(); break; default: assert true; // 何もしない break; } invalidate(); return true; } /** * scoreView を取得する。 * * @return scoreView オブジェクト */ public TextView getScoreView() { return scoreView; } /** * scoreView を設定する。 * * @param scoreView */ public void setScoreView(TextView scoreView) { this.scoreView = scoreView; } /** * canvasをクリアする。 */ private void clearScreen(Canvas canvas) { invalidate(); } /** * スコアを表示する。 * * @param score * スコア */ private void drawScore(int score) { assert scoreView != null; scoreView.setText(Integer.toString(score)); } } --- ▲ インターフェース アイテムの性質によって、つぎのインターフェースを作った。 インターフェースとそれぞれのメソッド --- IDrawable 描画できる項目のインターフェース void draw(android.graphics.Canvas canvas) int getColor() int getHeight() int[][] getImageTable() int getWidth() int getX() int getY() boolean isVisible() void setNotVisible() IAttacher 攻撃物のインターフェース boolean isVisible() ITarget 攻撃目標になるアイテムのインターフェース boolean isHit(IAttacher attacher) void resetColor() void setColor(int color) IMoveAutomatic 自動で動くアイテムのインターフェース boolean isBorder() void move() IMoveManual 手動で動かすアイテムのインターフェース void move(int x, int y) --- □ IDrawable インターフェース 描画できる項目のインターフェース。 描画に必要なものとして、 ・描画のデータ ・色 ・左上の座標 がある。これらに対するゲッターを用意している。 メソッド --- void draw(android.graphics.Canvas canvas) int getColor() int getHeight() int[][] getImageTable() int getWidth() int getX() int getY() boolean isVisible() void setNotVisible() --- ・IDrawable.java --- package jp.marunomaruno.android.invader; import android.graphics.Canvas; /** * 描画できる項目のインターフェース * * @author maruno * */ public interface IDrawable { /** * 描画のときのブロックの大きさ */ public static final int BLOCK_SIZE = 20; /** * 非表示 */ public static final int NOT_VISIBLE = -1; /** * 図形描画のテーブルを取得する。 * * @return 図形描画のテーブル */ public int[][] getImageTable(); /** * X 座標を取得する。 * * @return X 座標 */ public int getX(); /** * Y 座標を取得する。 * * @return Y 座標 */ public int getY(); /** * 図形の色を取得する。 * * @return 図形の色 */ public int getColor(); /** * 図形の幅を取得する。 * * @return 図形の幅 */ public int getWidth(); /** * 図形の高さを取得する。 * * @return 図形の高さ */ public int getHeight(); /** * 図形を描画する。 * * @param canvas * キャンバス */ public void draw(Canvas canvas); /** * この図形が可視かどうかを返す。 * * @return 可視の場合true */ public boolean isVisible(); /** * 図形を非表示にする。 */ public void setNotVisible(); } --- □ IMoveAutomatic インターフェース 自動で動くアイテムのインターフェース。 自身が描画域の端にいるのかどうかを判断するメソッドを用意する。 メソッド --- boolean isBorder() void move() --- ・ IMoveAutomatic.java --- package jp.marunomaruno.android.invader; /** * 自動で動くアイテムのインターフェース * * @author maruno * */ public interface IMoveAutomatic { /** * アイテムが動く。 */ public void move(); /** * 端かどうかを判断する。 * * @return 端のときは true */ public boolean isBorder(); } --- □ IMoveManual インターフェース 手動で動かすアイテムのインターフェース。 このため、移動先の座標を指定して移動するメソッドを用意する。 メソッド --- void move(int x, int y) --- ・ IMoveManual.java --- package jp.marunomaruno.android.invader; /** * 手動で動かすアイテムのインターフェース * @author maruno * */ public interface IMoveManual { /** * アイテムを指定座標に動かす。 * @param x X 座標 * @param y Y 座標 */ public void move(int x, int y); } --- □ IAttacher インターフェース 相手を攻撃する物体のインターフェース。 可視かどうかを判断することができるようにする。 メソッド --- boolean isVisible() --- ・ IAttacher.java --- package jp.marunomaruno.android.invader; /** * 攻撃物のインターフェース * @author maruno * */ public interface IAttacher { public boolean isVisible(); } --- □ ITarget インターフェース 攻撃目標になるアイテムのインターフェース。 攻撃物が自身に当たったかどうかを判断できる。 また、あたったときに、自身の色を変えることができる。 メソッド --- boolean isHit(IAttacher attacher) void resetColor() void setColor(int color) --- ・ ITarget.java --- package jp.marunomaruno.android.invader; /** * ITarget 攻撃目標になるアイテムのインターフェース * * @author maruno * */ public interface ITarget { public boolean isHit(IAttacher attacher); /** * 図形の色を設定する。 * * @param color * 色 */ public void setColor(int color); /** * 図形の色をデフォルトの色にリセットする。 */ public void resetColor(); } --- ▲ アイテム スーパークラスとして、AbstractItem をつくり、これを継承するクラスとして、イン ベーダー、砲台、弾 をつくる。 □ AbstractItem クラス すべてのアイテムのスーパークラスになるクラス。 図形の形、色、描画するときの左上の座標を持つ。 とくに、図形の左上の座標は public にしている。 図形の形、色は、このクラスのサブクラスで設定する。 フィールドの概要 --- public int x 図形の x 座標 public int y 図形の y 座標 protected int color 図形の色 protected int height 図形の高さ protected int[][] image 図形の形 protected CanvasView view 図形を描画するビュー protected int width 図形の幅 --- メソッドの概要 --- public int getColor() 図形の色を取得する。 public int getHeight() 図形の高さを取得する。 public int[][] getImageTable() 図形描画のテーブルを取得する。 public int getWidth() 図形の幅を取得する。 public int getX() X 座標を取得する。 public int getY() Y 座標を取得する。 public boolean isVisible() この図形が可視かどうかを返す。 public void setNotVisible() 図形を非表示にする。 protected void draw(Canvas canvas, Paint paint, int x, int y, int[][] image) 図形を描画する。 --- ・ AbstractItem.java --- package jp.marunomaruno.android.invader; import android.graphics.Canvas; import android.graphics.Paint; /** * インベーダー・ゲーム上のアイテムのスーパークラス。 * アイテムの座標、色を持っている。 * * @author maruno * */ public abstract class AbstractItem implements IDrawable { /** * 図形の x 座標 */ protected int[][] image; /** * 図形の x 座標 */ public int x; /** * 図形の y 座標 */ public int y; /** * 図形の色 */ protected int color; /** * 図形の幅 */ protected int width; /** * 図形の高さ */ protected int height; /** * 図形を描画するビュー */ protected CanvasView view; protected AbstractItem(CanvasView view, int x, int y, int[][] image, int width, int height, int color) { super(); this.view = view; this.x = x; this.y = y; this.image = image; this.width = width; this.height = height; this.color = color; } /** * 図形を描画する。 * * @param canvas * キャンバス * @param paint * ペイント * @param x * X座標 * @param y * Y座標 * @param image * 図形を表現した2次元配列 */ protected void draw(Canvas canvas, Paint paint, int x, int y, int[][] image) { for (int i = 0; i < image.length; i++) { for (int j = 0; j < image[0].length; j++) { if (image[i][j] == 1) { canvas.drawRect(x + j * BLOCK_SIZE, y + i * BLOCK_SIZE, x + j * BLOCK_SIZE + BLOCK_SIZE, y + i * BLOCK_SIZE + BLOCK_SIZE, paint); } } } } @Override public int[][] getImageTable() { return image; } @Override public int getX() { return x; } @Override public int getY() { return y; } @Override public int getColor() { return color; } @Override public int getWidth() { return width; } @Override public int getHeight() { return height; } @Override public boolean isVisible() { return x >= 0 && y >= 0; } @Override public void setNotVisible() { x = NOT_VISIBLE; y = NOT_VISIBLE; } } --- □ Bullet クラス 弾を管理するクラス。 弾は、一度発射されれば自動で動くので、IMoveAutomatic インターフェースを、また、 相手を攻撃するので、IAttacher インターフェースを実装する。 定数 --- int COLOR 図形の色 int HEIGHT 描画高 int[][] IMAGE 図形イメージ int MOVING_UNIT 移動単位(ピクセル) int WIDTH 描画幅 --- ・ Bullet.java --- package jp.marunomaruno.android.invader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; /** * 弾のクラス。 * * @author marunomaruno */ public class Bullet extends AbstractItem implements IMoveAutomatic, IAttacher { /** * 図形イメージ */ public static final int[][] IMAGE = { { 1 }, }; /** * 描画幅 */ public static final int WIDTH = IMAGE[0].length * BLOCK_SIZE; /** * 描画高 */ public static final int HEIGHT = IMAGE.length * BLOCK_SIZE; /** * 移動単位(ピクセル) */ public static final int MOVING_UNIT = 5; /** * 図形の色 */ public static final int COLOR = Color.GREEN; /** * 弾オブジェクトを生成する。 * * @param view * ビュー * @param x * 図形の左上のX座標 * @param y * 図形の左上のY座標 */ public Bullet(CanvasView view, int x, int y) { super(view, x, y, IMAGE, WIDTH, HEIGHT, COLOR); } /** * 弾オブジェクトを非表示の状態で生成する。 * * @param view * ビュー */ public Bullet(CanvasView view) { this(view, NOT_VISIBLE, NOT_VISIBLE); ; } @Override public void draw(Canvas canvas) { if (isBorder()) { return; } Paint paint = new Paint(); paint.setColor(color); draw(canvas, paint, x, y, IMAGE); } @Override public void move() { y = y - MOVING_UNIT; } @Override public boolean isBorder() { return !isVisible(); } @Override public String toString() { return String.format("Bullet[%d, %d]", x, y); } } --- □ Gun クラス 砲台を管理するクラス。 砲台は、利用者の指示で動くので、IMoveManual インターフェースを実装する。 メソッド --- public Bullet shot() 弾を発射する。 --- ・ Gun.java --- package jp.marunomaruno.android.invader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; /** * 砲台のクラス。 * @author marunomaruno */ public class Gun extends AbstractItem implements IMoveManual { /** * 図形イメージ */ public static final int[][] IMAGE = { { 0, 0, 1, 1, 0, 0 }, { 1, 1, 1, 1, 1, 1 }, }; /** * 描画幅 */ public static final int WIDTH = IMAGE[0].length * BLOCK_SIZE; /** * 描画高 */ public static final int HEIGHT = IMAGE.length * BLOCK_SIZE; /** * 移動単位(ピクセル) */ public static final int MOVING_UNIT = 5; /** * 図形の色 */ public static final int COLOR = Color.BLUE; /** * 砲台オブジェクトを生成する。 * @param view ビュー * @param x 図形の左上のX座標 * @param y 図形の左上のY座標 */ public Gun(CanvasView view, int x, int y) { super(view, x, y, IMAGE, WIDTH, HEIGHT, COLOR); } /** * 砲台オブジェクトを生成する。 * 砲台は画面の下段中央に配置する。 * @param view ビュー */ public Gun(CanvasView view) { this(view, (view.getWidth() - WIDTH) / 2, view.getHeight() - HEIGHT); } /** * 弾を発射する。 */ public Bullet shot() { return new Bullet(view, x + (Gun.WIDTH - Bullet.WIDTH) / 2, y); } @Override public void draw(Canvas canvas) { Paint paint = new Paint(); paint.setColor(color); draw(canvas, paint, x, y, IMAGE); } @Override public void move(int x, int y) { this.x = Math.max(Math.min(x, view.getWidth() - WIDTH), 0); } @Override public String toString() { return String.format("Gun[%d, %d]", x, y); } } --- □ Invader クラス インベーダーを管理するクラス。 インベーダーは、ゲームの最初から自動で動くので、IMoveAutomatic インターフェース を、また、相手の攻撃を受けるので、ITarget インターフェースを実装する。 定数 --- int COLOR 図形の色 int DIRECTION_LEFT 移動方向 左 int DIRECTION_RIGHT 移動方向 右 int HEIGHT 描画高 int HIT_COLOR 当たったときの図形の色 int[][] IMAGE 図形イメージ int MOVING_UNIT 移動単位(ピクセル) int WIDTH 描画幅 --- フィールド --- private int movingUnit 移動単位(ピクセル) --- メソッド --- public void invert() 移動方向を反転する。 --- ・ Invader.java --- package jp.marunomaruno.android.invader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; /** * インベーダーのクラス * @author maruno * */ public class Invader extends AbstractItem implements IMoveAutomatic, ITarget { /** * 図形イメージ */ public static final int[][] IMAGE = { { 0, 0, 0, 1, 0, 1, 0, 0, 0 }, { 1, 0, 1, 1, 1, 1, 1, 0, 1 }, { 1, 1, 1, 0, 1, 0, 1, 1, 1 }, { 0, 0, 1, 1, 1, 1, 1, 0, 0 }, { 0, 0, 1, 1, 0, 1, 1, 0, 0 }, { 0, 0, 1, 0, 0, 0, 1, 0, 0 }, }; /** * 描画幅 */ public static final int WIDTH = IMAGE[0].length * BLOCK_SIZE; /** * 描画高 */ public static final int HEIGHT = IMAGE.length * BLOCK_SIZE; /** * 移動単位(ピクセル) */ public static final int MOVING_UNIT = 1; /** * 図形の色 */ public static final int COLOR = Color.GREEN; /** * 当たったときの図形の色 */ public static final int HIT_COLOR = Color.RED; /** * 移動方向 左 */ public static final int DIRECTION_LEFT = -1; /** * 移動方向 右 */ public static final int DIRECTION_RIGHT = 1; /** * 移動単位(ピクセル) */ private int movingUnit = DIRECTION_RIGHT * MOVING_UNIT; /** * インベーダー・オブジェクトを生成する。 * @param view ビュー * @param x 図形の左上のX座標 * @param y 図形の左上のY座標 */ public Invader(CanvasView view, int x, int y) { super(view, x, y, IMAGE, WIDTH, HEIGHT, COLOR); } /** * インベーダー・オブジェクトを生成する。 * インベーダーは画面の左上から出現する。 * @param view ビュー */ public Invader(CanvasView view) { this(view, 0, 0); } /** * 移動方向を反転する。 */ public void invert() { movingUnit = -movingUnit; } @Override public void draw(Canvas canvas) { Paint paint = new Paint(); paint.setColor(color); draw(canvas, paint, x, y, IMAGE); } @Override public void setColor(int color) { this.color = color; } @Override public void resetColor() { color = COLOR; } /** * インベーダーを動かす。 * 動かすのは、単純に左右に動かし、壁にぶつかったら反転する。 */ @Override public void move() { x += movingUnit; // 左右の端にぶつかったら反転する if (isBorder()) { invert(); } } @Override public boolean isBorder() { return (x < 0) || (x > view.getWidth() - WIDTH); } @Override public boolean isHit(IAttacher attacher) { Bullet bullet = (Bullet) attacher; return attacher.isVisible() && (bullet.y + Bullet.HEIGHT < this.y + HEIGHT) && (this.x < bullet.x) && (bullet.x + Bullet.WIDTH < this.x + WIDTH); } @Override public String toString() { return String.format("Invader[%d, %d]", x, y); } } --- ▲ レイアウト □ res/layout/main.xml ※ Invader01 プロジェクトと同じ ▲ リソース □ res/values/strings.xml ※ Invader01 プロジェクトと同じ 以上
最新の画像[もっと見る]
- あけましておめでとうございます 11年前
- 今年もよろしくお願いいたします 12年前
- あけましておめでとうございます 13年前
- あけましておめでとうございます 16年前