円(ボール)を1個に限定する

ゲームにしたてる

ボールを障害物にあてて消していくゲームにする予定でボールをひとつにします。

AnimeDisk7.java から作る方がいいでしょう。AnimeDisk8.javaから作る場合、最低でも run() 内の if ( isnaname != mypnl.isnaname ) { } の部分を削除またはコメントアウトする必要があります。その他 AnimeDisk8.java を作るときに加えた isnaname 関係のものは不要なので削除しても構いません。ということは AnimeDisk7.java から作るということですね。

moveDisk

ボタンのコントロール

Startボタンでボールを1個作ったときにボタンを押せなくします。

moveDisk

AnimeDisk7.javaをもとにactionPerformedに書き加えます。setEnabled(false)がボタンを押せなくするメソッド。setText("busy")がボタンの表示を変えるメソッドで、busyに変えて押せなくしていることを知らせます。

ファイル名 ABall1.java の一部

//イベント処理
@Override
public void actionPerformed(ActionEvent e) {
    if (e.getSource() == rbtn) {
        rbtn.setEnabled(false);
        rbtn.setText("busy");
        MoveDisk disk = new moveDisk(mypnl);
        disk.start();

greenボタンの設定追加

次のように45度の板をボールの前に置いたように跳ね返ることにします。

moveDisk

わかりやすいように、greenをに書き換えます。このためには、gbtn = new JButton("green");を次のようにします。

ファイル名 ABall1.java の一部

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()メソッド内の書き換え

ファイル名 ABall1.java の一部

if ( greenct != mypnl.greenct ) {
     int tmp = dx;
     dx = -dy;
     dy = -tmp;
     greenct = mypnl.greenct;
}

blueボタンの設定追加

greenとは逆の45度の板で反射させます。

moveDisk

わかりやすいように、blueをに書き換えます。このためには、bbtn = new JButton("blue");を次のようにします。

ファイル名 ABall1.java の一部

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 ) { } の後に挿入。

ファイル名 ABall1.java の一部

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.

とりあえずここまでで、ボールが1つ動いて/,\ボタンで思うように跳ね返るか確認しなさい。

ファイル名 ABall1.java

確認できたら次の障害物に進みます。

障害物

障害物を加えます。プログラムをコピーしてプログラム名を変えて

障害物を複数作れるようにTargetというクラスで表し、配列にします。

これもクラスとして、MoveDiskクラスのあとにつなげましょう。

ファイル名 ABall2.java の一部

}//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はクリアした障害物の数を数えるための変数です。

ファイル名 ABall2.java の一部

class MoveDisk extends Thread {
     ...省略..
    int greenct;
    int bluect;
    int clearct = 0;
    int tmax = 4;
    Target[] ts = new Target[tmax];

MoveDiskのコンストラクタで実際の障害物を作ります。

障害物は配置される四角の大きさが決まってから作る必要があります。MovdDiskのコンストラクタの中がいいでしょう。

ファイル名 ABall2.java の一部

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);
    }

画面に書くのはスタートボタンを押したときにします。

ファイル名 ABall2.java の一部

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  ){

課題

2.

とりあえずここまでで、ボールが1つ動いて/,\ボタンでぶつけると青丸の障害物が消えるか確認しなさい。

ファイル名 ABall2.java

確認できたら次の衝突判定に進みます。

衝突判定

ボールが通ったところが背景色になりますから、ぶつかっても半分削れるだけという現象が起こります。また、すべてクリアしたときに何かの動作を起こすためにも衝突判定が必要です。

moveDisk

ボールを消すと障害物の一部を消すかもしれません。一部削れた時のために障害物を描き直します。そのためには障害物の描画はrunメソッド中のボールを動かしているwhile内で行うように記述を移動します。

さらに衝突判定を入れて衝突していたと判断すると多少ずれていてもボールを四角形で消します

ファイル名 ABall3.java の一部

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の初期値設定の近くに置けば調整しやすくします。ボールの速さは難易度を決定しますからうまく調整してください。

ファイル名 ABall3.java の一部

(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);
}
     ....省略...

課題

3.

上記の様に働くようにし、動作を確認しなさい。

ファイル名 ABall3.java


Javaプログラミング
聖愛中学高等学校
http://www.seiai.ed.jp/
Dec.2003
Oct.2010 Oct.2012