さて、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();
以下のコードを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);
}
さあ実行してみましょう… あれ?前と一緒だ。てか、障害物を追加する部分がまだできていない、ですね。ははは。
さて、障害物を追加しますかー
で、とりあえず、30秒ごとに障害物が追加されていくというルールにしてみましょう。onDraw()のunlockCanvasAndPost()の直前ぐらいに、以下のロジックを追加してみます。
long now = System.currentTimeMillis();
if (rocks.size() < (now - startTime) / 30000)
addRock(canvas.getWidth(), canvas.getHeight());
さて、障害物を追加しますかー
まずは障害物を表示するロジックを加えます。これは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
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);
}
さて、これで障害物(赤い四角)を避けながら宇宙を漂えるようになりました。ちょっとゲームっぽくなりましたよね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);
}
}
表示はこんな感じ。
これで本当に最低限ですが、ゲームの体裁が整いました。ただしやっつけの部分が多いので、次からは少しずつまともにしていきましょう。
現時点のソースコードは こんな感じ。
障害物を発生させる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);
}
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);
}
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 件のコメント:
コメントを投稿