現時点では、静止した宇宙の中をAndroid君が移動するだけのゲームになっています。これではちょっと寂しいので、障害物のほうにも動きをつけてあげましょう。
今まで障害物は Rect クラスの配列で扱ってきましたが、拡張するためには専用クラスを作ってあげるのが良いでしょう。Rockクラスとして新しく定義します。とりあえずはMyViewの内部クラスにしときます。
public class Rock {
Rect r;
int w, h, dx, dy;
public Rock(int sw, int sh) {
w = 6 + rand.nextInt(7);
h = 6 + rand.nextInt(7);
int x = rand.nextInt(sw - w);
int y = rand.nextInt(sh - h);
r = new Rect(x, y, x + w, y + h);
dx = rand.nextInt(5) - 2;
dy = rand.nextInt(5) - 2;
}
}
Rockオブジェクトは、従来のRect の他に幅(w)、高さ(h)、x方向の速さ(dx)、y方向の速さ(dy)を属性として持ちます。dx, dy が目新しいですね。
Rockのコンストラクタは、addRock関数にあった初期化部分をもってきています。移動速度dx,dyは -2~2 の範囲でランダムに設定されるようにしてみました。また小さな障害物が動くのはつらいので、縦横の最小サイズをそれぞれ+2しています。
ついでに障害物に関する処理をRockクラスに集めてしまいましょう。以下のようなメソッドを追加します。
void move(Canvas canvas){
r.left = MyGame.check(r.left + dx, r.width(), canvas.getWidth());
r.top = MyGame.check(r.top + dy, r.height(), canvas.getHeight());
r.right = r.left + w;
r.bottom = r.top + h;
}
Boolean collision(Rect d) {
return d.contains(r);
}
void draw(Canvas canvas) {
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawRect(r, paint);
}
move()は移動、collisionはAndroid君などとの衝突判定、draw()は画面表示の部分をもってきました。内部クラスということで、無駄はありますが気にしないでください。
check()は以下のような静的メソッドで、座標を補正して上下左右を繋げる役割です。将来的にはUtilだとかToolだとかいうクラスにまとめちゃう便利関数ですね。
static int check(int x, int w, int sw) {
return x > sw ? -w : x < -w ? sw : x;
}
さて、全体をRockクラスを利用するように変更してみましょう。まず、addRock()メソッドは以下のように簡単になります。
void addRock(int sw, int sh){
Rect safe = new Rect(d_r);
safe.offset(d_dx * 4, d_dy * 4);
Rock rock;
do {
rock = new Rock(sw, sh);
} while(rock.collision(d_r) || rock.collision(safe));
rocks.add(rock);
}
他にも、こまごまと変更がありますね。
List<Rect> rocks = new ArrayList<Rect>();
↓
}
さてこれで従来と同様になったはずです。それに加えて、さりげなく障害物を動かすmove()も加えてあります。
さて、実行してみましょう。障害物も動いていることで、なんか一気にゲームっぽい感じになってきました。
ま、例によって画面キャプチャだと判り辛いですけどね…
もう一点、修正しておきましょう。ゲームオーバーの画面から「戻る」操作をすると、ゲームが初期化されていないので妙なことになるのが、気になります。
まずはMyViewのコンストラクタなどにある初期化部分を抜き出して、init()メソッドに集めます。
void init() {
Display disp = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
d_x = (disp.getWidth() - droid.getWidth()) / 2;
d_y = (disp.getHeight() - droid.getHeight()) / 2 - 20;
d_dx = d_dy = 0;
rocks.clear();
score = 0;
startTime = System.currentTimeMillis();
}
check()は以下のような静的メソッドで、座標を補正して上下左右を繋げる役割です。将来的にはUtilだとかToolだとかいうクラスにまとめちゃう便利関数ですね。
static int check(int x, int w, int sw) {
return x > sw ? -w : x < -w ? sw : x;
}
void addRock(int sw, int sh){
Rect safe = new Rect(d_r);
safe.offset(d_dx * 4, d_dy * 4);
Rock rock;
do {
rock = new Rock(sw, sh);
} while(rock.collision(d_r) || rock.collision(safe));
rocks.add(rock);
}
List<Rect> rocks = new ArrayList<Rect>();
↓
List<Rock> rocks = new ArrayList<Rock>();
for (Rect rock : rocks)
canvas.drawRect(rock, paint);
↓
for (Rock rock : rocks)
rock.move(canvas);
rock.draw(canvas);}
for (Rect rock : rocks)
if (d_r.contains(rock))
gameover();
↓
for (Rock rock : rocks)
if (rock.collision(d_r))
gameover();
さてこれで従来と同様になったはずです。それに加えて、さりげなく障害物を動かすmove()も加えてあります。
さて、実行してみましょう。障害物も動いていることで、なんか一気にゲームっぽい感じになってきました。
ま、例によって画面キャプチャだと判り辛いですけどね…
もう一点、修正しておきましょう。ゲームオーバーの画面から「戻る」操作をすると、ゲームが初期化されていないので妙なことになるのが、気になります。
まずはMyViewのコンストラクタなどにある初期化部分を抜き出して、init()メソッドに集めます。
void init() {
Display disp = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
d_x = (disp.getWidth() - droid.getWidth()) / 2;
d_y = (disp.getHeight() - droid.getHeight()) / 2 - 20;
d_dx = d_dy = 0;
rocks.clear();
score = 0;
startTime = System.currentTimeMillis();
}
そしてこのinit()を、gameover()メソッドの最後でも呼び出します。startActivity(intent);でゲームオーバー画面を表示した後、戻ってきたタイミングで初期化するわけですね。
ちょっと遊んでみたところ、ちと簡単すぎる気が。最初から障害物が1個表示されるようにして、かつ5秒に1個づつ増えるように変更しておきます。
if (rocks.size() < (now - startTime) / 30000)
↓
if (rocks.size() <= (now - startTime) / 5000)
さーて、これでひと段落。今日はここまでにして、次回はパッケージなどまともに整理しましょうかねー
現時点のソースは こんな感じ になっています。
ちょっと遊んでみたところ、ちと簡単すぎる気が。最初から障害物が1個表示されるようにして、かつ5秒に1個づつ増えるように変更しておきます。
if (rocks.size() < (now - startTime) / 30000)
↓
if (rocks.size() <= (now - startTime) / 5000)
さーて、これでひと段落。今日はここまでにして、次回はパッケージなどまともに整理しましょうかねー
現時点のソースは こんな感じ になっています。