marunomaruno-memo

marunomaruno-memo

[Android] インベーダー・ゲームを作る (2) オブジェクト指向

2012年04月21日 | Android
[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 プロジェクトと同じ

                                                                            以上


最新の画像もっと見る

1 コメント

コメント日が  古い順  |   新しい順
d (d)
2015-03-19 12:50:35
さっぱり動かない

コメントを投稿