ボールを障害物にあてて消していくゲームにする予定でボールをひとつにします。
AnimeDisk7.java から作る方がいいでしょう。AnimeDisk8.javaから作る場合、最低でも run() 内の if ( isnaname != mypnl.isnaname ) { } の部分を削除またはコメントアウトする必要があります。その他 AnimeDisk8.java を作るときに加えた isnaname 関係のものは不要なので削除しても構いません。ということは AnimeDisk7.java から作るということですね。
Startボタンでボールを1個作ったときにボタンを押せなくします。
AnimeDisk7.javaをもとにactionPerformedに書き加えます。setEnabled(false)がボタンを押せなくするメソッド。setText("busy")がボタンの表示を変えるメソッドで、busyに変えて押せなくしていることを知らせます。
//イベント処理 @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == rbtn) { rbtn.setEnabled(false); rbtn.setText("busy"); MoveDisk disk = new moveDisk(mypnl); disk.start();
次のように45度の板をボールの前に置いたように跳ね返ることにします。
わかりやすいように、greenを/に書き換えます。このためには、gbtn = new JButton("green");を次のようにします。
gbtn = new JButton("/");
"/"は「スラッシュ」で変換し全角を選択します。
この機能を細かく書きだすと、たとえばdy=10であれば次のようになります
右へ行っていたものは上へ、(dx= 10, dy= 0) → (dx= 0, dy=-10) 下に行っていたものは左へ、(dx= 0, dy= 10) → (dx=-10, dy= 0) 左に行っていたものは下へ、(dx=-10, dy= 0) → (dx= 0, dy= 10) 上に行っていたものは右へ。(dx= 0, dy=-10) → (dx= 10, dy= 0)
つまり dx,dy をマイナスをつけて交換します。
greenボタンが押されたときに greenct に1を加えてボールにクリックを通知する仕組みは、AnimeDisk7.javaとAnimeDisk8.javaで設定済みですからそのまま使います。
run()メソッドには dx,dy をそのまま交換するようになっていますから、マイナスをつければ完成です。
run()メソッド内の書き換え
if ( greenct != mypnl.greenct ) { int tmp = dx; dx = -dy; dy = -tmp; greenct = mypnl.greenct; }
greenとは逆の45度の板で反射させます。
わかりやすいように、blueを\に書き換えます。このためには、bbtn = new JButton("blue");を次のようにします。
bbtn = new JButton("\");
"\"は「バックスラッシュ」で変換し全角を選択します。半角の\にするときには\\と2つ重ねないと別の意味になってコンパイルが通りません。
この機能を細かく書きだすと、たとえばdy=10であれば次のようになります。
右へ行っていたものは下へ、(dx= 10, dy= 0) → (dx= 0, dy= 10) 下に行っていたものは右へ、(dx= 0, dy= 10) → (dx= 10, dy= 0) 左に行っていたものは上へ、(dx=-10, dy= 0) → (dx= 0, dy=-10) 上に行っていたものは左へ。(dx= 0, dy=-10) → (dx=-10, dy= 0)
どの場合もdx,dyを入れ替えればよいことが分かります。
プログラムは次の様にします。
run()メソッド内の if ( greenct != mypnl.greenct ) { } の後に挿入。
if ( bluect != mypnl.bluect ) { int tmp = dx; dx = dy; dy = tmp; bluect = mypnl.bluect; }
もちろん、greenct と同様にbluectという変数を用意する必要があります。greenctを探して近くにbluectを書けばいいでしょう。
actionPerformed()内に
if (e.getSource() == bbtn) { mypnl.bluect++; }
class MyPanel のフィールドに
int greenct = 0; int bluect = 0;
class MoveDiskのフィールドにも
int greenct; int bluect;
MoveDiskコンストラクタ内に
this.greenct = mypnl.greenct; this.bluect = mypnl.bluect;
とりあえずここまでで、ボールが1つ動いて/,\ボタンで思うように跳ね返るか確認しなさい。
確認できたら次の障害物に進みます。
障害物を加えます。プログラムをコピーしてプログラム名を変えて
障害物を複数作れるようにTargetというクラスで表し、配列にします。
これもクラスとして、MoveDiskクラスのあとにつなげましょう。
}//end of class MoveDisk class Target{ int x; //x位置 int y; //y位置 boolean cleared = false; //クリアされたかどうか public Target(int w,int h){ x = (int)(w*Math.random()); y = (int)(h*Math.random()); } }//end of class Target
MoveDiskのフィールドにこの配列を加えます。tmaxは障害物の数をあとで変更できるように導入しました。TargetはMyPanelかBufferedImageに属させる方が良い気もしますが、ボールはひとつだけですからここでもかまいません。
clearct=0はクリアした障害物の数を数えるための変数です。
class MoveDisk extends Thread {
...省略..
int greenct;
int bluect;
int clearct = 0;
int tmax = 4;
Target[] ts = new Target[tmax];
MoveDiskのコンストラクタで実際の障害物を作ります。
障害物は配置される四角の大きさが決まってから作る必要があります。MovdDiskのコンストラクタの中がいいでしょう。
public MoveDisk(MyPanel mypnl) { ...省略.. this.greenct = mypnl.greenct; this.bluect = mypnl.bluect; for(int i=0 ; tmax>i ; i++){ ts[i] = new Target(xmax,ymax); }
画面に書くのはスタートボタンを押したときにします。
public void run() { thg.setColor(Color.blue); for(int i=0 ; tmax>i ; i++){ if (ts[i].cleared) continue; thg.fillOval(ts[i].x-d/2,ts[i].y-d/2,d,d); } while ( true ){
とりあえずここまでで、ボールが1つ動いて/,\ボタンでぶつけると青丸の障害物が消えるか確認しなさい。
確認できたら次の衝突判定に進みます。
ボールが通ったところが背景色になりますから、ぶつかっても半分削れるだけという現象が起こります。また、すべてクリアしたときに何かの動作を起こすためにも衝突判定が必要です。
ボールを消すと障害物の一部を消すかもしれません。一部削れた時のために障害物を描き直します。そのためには障害物の描画はrunメソッド中のボールを動かしているwhile内で行うように記述を移動します。
さらに衝突判定を入れて衝突していたと判断すると多少ずれていてもボールを四角形で消します。
public void run() {thg.setColor(Color.blue);//ABall2.javaで加えたが消去for(int i=0 ; tmax>i ; i++){if (ts[i].cleared) continue;thg.fillOval(ts[i].x-d/2,ts[i].y-d/2,d,d);}while ( tmax > clearct ){ thg.setColor(mypnl.bgcolor); thg.fillRect(x-d/2,y-d/2,d,d); for(int i=0 ; tmax>i ; i++){ //クリアされていなければ障害物を書きなおす if (ts[i].cleared) continue; thg.setColor(Color.blue); thg.fillOval(ts[i].x-d/2,ts[i].y-d/2,d,d); if (d/2>Math.abs(ts[i].x-x)){ //衝突したら全部消す if (d/2>Math.abs(ts[i].y-y)){ ts[i].cleared = true; thg.setColor(mypnl.bgcolor); thg.fillRect(ts[i].x-d/2,ts[i].y-d/2,d,d); clearct++; } } } if ( greenct != mypnl.greenct ) { int tmp = -dx; ......省略....
この色の部分はクリアした障害物の数を数えているところです
ここまでで障害物の中心に届いたら全部消えるようになります。
また、whileの継続条件を変更して、障害物を全部消したら終了するようにしています。
やってみると消えない場合が出るかもしれません。一番最初はボール大きさが10で dx や dy が10でした。ボールと障害物の位置関係によってはボールが障害物を飛び越えることもありえます。また、動きが粗い感じを与えるかもしれません。
そこで、一回に動く距離を小さくすれば、なめらかな動きになり判定も緻密になりますが動きが遅くなります。その分Thread.sleep()も小さくして速度を調節します。
例えば int dx=10; を int dx=5; に変え、その代わりに Thread.sleep(100) を100から50にすれば速度は同じです。ただしdxをあまり小さくするとコンピュータの仕事量が増えすぎて追いつかなくなるかもしれませんから限界はあります。
sleepの値は int stime=50; として変数で指定するようにしました。dx,dyの初期値設定の近くに置けば調整しやすくします。ボールの速さは難易度を決定しますからうまく調整してください。
(1)MoveDiskクラスのフィールドです。
class MoveDisk extends Thread { int x; int y; int d, dx, dy, stime; int xmax, ymax; ....省略...
(2)MoveDiskコンストラクタ内です。
....省略... d = xmax/40; //円の大きさ 10 dx = 0; dy = ymax/80; //動きの大きさ 5 (40を80にした) stime = 50; ....省略...
(3)100とあったところをこの変数にします。
....省略... try { Thread.sleep(stime); } ....省略...
上記の様に働くようにし、動作を確認しなさい。