2011年7月24日日曜日

とりあえずゲーム作り(2)

涼しくて過ごしやすい日曜日、いいですね。今日も頑張ろう!

さて、Android君が動くようになったMyGame、今回はゲームっぽくしていきましょう。得点の概念が必要でしょうし、障害を配置してゲームオーバーの危険性があるといいですね。

最初はシンプルに、動かない障害物を表示してみましょう。宇宙空間に浮かんでいる岩やデブリのイメージです。Android君が衝突したらそれでゲームオーバー。時間とともに増えていく仕組みも欲しいですね。

とりあえず今回の準備として、MyViewクラスに以下のメンバを追加しておきます。

Rect d_r = new Rect();
List<Rect> rocks = new ArrayList<Rect>();
long startTime = System.currentTimeMillis();
long score;
Random rand = new Random();

衝突判定に必要なのは、Android君の実体(当たり判定)の定義です。表示している画像の大きさをそのまま使ってもいいのですが、通常はそれより小さめに設定します。

以下のコードをonDraw()のdrawBitmap()直後に追加します。描画するときに、その当たり判定も設定してあげる、という感じです。単に画像サイズより4ドットづつ小さい領域にしているのは、いつもどおりの手抜きでありんす。

d_r.set(d_x+4, d_y+4, d_x+droid.getWidth()-8, d_y+droid.getHeight()-8);

これを使って、障害物との当たり判定のロジックも作成しましょう。処理は単純で、Android君の実体(d_r)が、障害物のどれかと接触したら、つまり領域が重なったらゲームオーバーです。

onDraw()のunlockCanvasAndPost()の後ぐらいに以下のコードを追加します。

for (Rect rock : rocks)
  if (rock.contains(d_r))
    gameover();

あ、ゲームオーバー処理がまだなかった。Activityクラスに仮のものを実装しておきましょう。現時点では、単に元メニューにもどるだけ。そのうちゲームオーバー用のActivity作成します。

public void gameover() {
  Intent intent = new Intent(MyGame.this, Main.class);
  startActivity(intent);
}

さあ実行してみましょう… あれ?前と一緒だ。てか、障害物を追加する部分がまだできていない、ですね。ははは。


さて、障害物を追加しますかー

まずは障害物を表示するロジックを加えます。これはonDraw()のdrawBitmap()の前がいいと思います。後だとAndroid君より手前に表示されてしまいますので。

またもや手抜きで、赤い四角にしておきます。以下のような感じ。

Paint paint = new Paint();
paint.setColor(Color.RED);
for (Rect rock : rocks)
  canvas.drawRect(rock, paint);

で、とりあえず、30秒ごとに障害物が追加されていくというルールにしてみましょう。onDraw()のunlockCanvasAndPost()の直前ぐらいに、以下のロジックを追加してみます。

long now = System.currentTimeMillis();
if (rocks.size() < (now - startTime) / 30000)
  addRock(canvas.getWidth(), canvas.getHeight());

MyViewのメソッドaddRock()が未定義ですね。コードは以下です。

void addRock(int sw, int sh){
  int w = 4 + rand.nextInt(7);
  int h = 4 + rand.nextInt(7);
  int x = rand.nextInt(sw - w);
  int y = rand.nextInt(sh - h);
  Rect rock = new Rect(x, y, x + w, y + h);
  rocks.add(rock);
}

このメソッドを使って、画面内のランダムな位置に幅4~10ドット、高さ4~10ドットの障害物(赤い四角)を追加します。

さて、これで障害物(赤い四角)を避けながら宇宙を漂えるようになりました。ちょっとゲームっぽくなりましたよねw


何回かゲームとしてプレイしてみると、不条理な状況があることに気がつきます。いきなり Android君の内部に障害物が発生して、そのままゲームオーバーになることがあるのです。

障害物を発生させるaddRock()を修正しましょう。

void addRock(int sw, int sh){
  Rect safe = new Rect(d_r);
  safe.offset(d_dx * 4, d_dy * 4);
  Rect rock;
  do {
    int w = 4 + rand.nextInt(7);
    int h = 4 + rand.nextInt(7);
    int x = rand.nextInt(sw - w);
    int y = rand.nextInt(sh - h);
    rock = new Rect(x, y, x + w, y + h);
  } while(d_r.contains(rock) || safe.contains(rock));
  rocks.add(rock);
}

追加したコードは、障害物を生成した位置がAndroid君の領域内であれば、生成しなおすというベタなロジックになっています。

Ract safeは4フレーム(0.4秒)後にAndroid君が占める領域です。これもあわせてチェックすることにより、移動するすぐ先に障害物が発生することを防いでいます。

ただこのロジックだと、上下左右の画面の繋がりまでは考慮していません。端のほうは危険だよ、というゲーム性なのです、という言い訳で誤魔化しておきましょう。

さてこれで、一応はプレイできるものになりました。まだ面白くはないですが… よりゲームっぽくするために、次は得点に関する機能を実装してみましょう。

基本的には、得点は時間経過で増えていくとします。長くプレイすることで、高得点が狙えるわけですね。ただし動いていないときは点が増えないことにしましょう。そうでないと、最初の位置で動かないことが攻略法になってしまいますからね。

加えて、動作速度が速いほうが点が上がりやすい、としたほうが面白いでしょう。より危険なプレイが評価される、ゲームデザインの王道でありますな。

ではonDraw()の後に以下のようなコードを追加してみましょう。

score += Math.abs(d_dx) + Math.abs(d_dy);
paint.setColor(Color.WHITE);
paint.setTextSize(24);
String s = "0000000000"+score;
canvas.drawText(s.substring(s.length() - 10), 0, paint.getTextSize(), paint);

得点は単純に移動距離にしてみました。高速移動のときのボーナスが大きすぎる気もしますが、後でゲームバランスを調整するときに見直しましょう。スコアも単純に、左上に表示するだけにしています。

ついでに現在の速度も表示しておきましょう。先ほどのコードの後に以下を追加します。

paint.setColor(Color.GRAY);
s = "x:" + d_dx + " y:" + d_dy;
canvas.drawText(s, 0, paint.getTextSize() * 2, paint);

さて実行してみましょう。よりゲームっぽくなった気がしますね。


さてプレイしてみると、ゲームオーバーした時に得点がわからないですね。そろそろ専用画面を追加してみましょう。

まずはgameover()メソッドを以下のように書き換えます。

public void gameover() {
  Intent intent = new Intent(MyGame.this, MyGameOver.class);
  intent.putExtra("SCORE", score);
  startActivity(intent);
}

で、MyGameOverクラスを新規作成しましょう。とりあえずは得点を表示するだけのシンプルなAvtivityです。

public class MyGameOver extends Activity {
  protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setTitle("My Game - Game Over");
    TextView view = new TextView(this);
    Bundle extras = getIntent().getExtras();
    Long score = extras.getLong("SCORE");
    view.setText("SCORE: " + score);
    setContentView(view);
  }
}

表示はこんな感じ。


これで本当に最低限ですが、ゲームの体裁が整いました。ただしやっつけの部分が多いので、次からは少しずつまともにしていきましょう。

現時点のソースコードは こんな感じ

0 件のコメント:

コメントを投稿