レッスン3

今度は自動的にボールが跳ね返るようにしました。


import java.awt.*;
import java.applet.Applet;

public class Lesson3 extends Applet implements Runnable {
    int x = 150, y = 50, dx = 10, dy = 10;
    Thread ball;

    public void init() {
        setBackground(Color.white);
        setForeground(Color.blue);
    }

    public void start() {
        if (ball == null)
            ball = new Thread(this, "Ball");
        ball.start();
    }

    public void stop() {
        ball = null;
    }

    public void paint(Graphics g) {
        g.fillOval(x - 10, y - 10, 20, 20);
    }

    public void run() {
        Thread thisThread = Thread.currentThread();
        while (ball == thisThread) {
            try {
                thisThread.sleep(100);  // sleep 100msec
            } catch (InterruptedException e) {
                break;
            }
            if (x <= 10 || x >= 290) dx = -dx;
            if (y <= 10 || y >=  90) dy = -dy;
            x += dx;  y += dy;
            repaint();
        }
    }
}

枠線は Java ではなく HTML の table タグで作りました:

<table border="1">
  <tr><td><applet code="Lesson3.class" width="300" height="100"></applet></td></tr>
</table>

このようなプログラムは,外からの指示がなくても,独立に動いてくれなければなりません。 他と独立に動くようなプログラムの部分をスレッド (thread) といいます。

スレッドを使うには,最初の

public class Lesson3 extends Applet

の次に

       implements Runnable

というオマジナイが必要です。 Runnable とは,他と独立に run する(走る)ような部分を持つということです。

スレッドは変数に入れて使います。そのために, Thread 型の変数をあらかじめ作っておきます。 その変数には,適当な名前を付けておきます。 ここではボールを動かすスレッドなので ball という名前にしましょう。

    Thread ball;

スレッドは一つのプログラム内でいくつ作ってもかまいません。 スレッドが複数ある場合には,

    Thread ball1, ball2;

のように,複数の変数を作る必要があります。

ここで作った Thread 型の変数は,作られた当初は null という特別な値を持ちます。 null は「何もないこと」を意味する英語です。 正しい英語の読み方は「ナル」ですが,日本では「ヌル」と読む人も多いようです。

Web ブラウザは,このアプレットを読み込むと,まずこの中の init() を実行し,次に start() を実行します。 そして,このページを見終わって次のページに移るときは,アプレットの stop() を実行します。

そこで,start() ではスレッドの実行を開始し,stop() ではスレッドの実行を止めるのが普通です。

そこで,start() メソッドには次のようなことを書いておきます。

    public void start() {
        if (ball == null)
            ball = new Thread(this, "Ball");
        ball.start();
    }

これは,まだ ballnull(作られていない)なら,ball に新しいスレッドを代入するという意味です。

    new Thread(this, "Ball")

が新しいスレッドを作るためのオマジナイです。 最後の "Ball" はスレッドの名前です(この名前は省略できます)。

stop() では単に

    public void stop() {
        ball = null;
    }

のようにします。 つまり,変数 ballnull を代入します。 スレッドそのものを stop するのは良くないようです。

スレッドの実際に動く部分は,必ず run() というメソッド名にしておきます。 ここでは次のようになっています。

    public void run() {
        Thread thisThread = Thread.currentThread();
        while (ball == thisThread) {
            try {
                thisThread.sleep(100);  // sleep 100msec
            } catch (InterruptedException e) {
                break;
            }
            if (x <= 10 || x >= 290) dx = -dx;
            if (y <= 10 || y >=  90) dy = -dy;
            x += dx;  y += dy;
            repaint();
        }
    }

ここではまず自分自身のスレッドを thisThread という変数に代入しています。

while (ball == thisThread) { ... } は,thisThreadball スレッドである間ずっと繰返しを続けるという意味です。 ただし,ループを回りっぱなしだと CPU が他の仕事をするのに支障が出るので,毎回 Thread.sleep(100) を実行して,100ミリ秒だけ休ませています。 この間に CPU は他の仕事ができるわけです。

Thread.sleep() を実行している間に,他から割り込みがかかることがあります。 ここでは割り込みがかかれば単に100ミリ秒の休止を中断するだけにします:

            try {
                thisThread.sleep(100);  // sleep 100msec
            } catch (InterruptedException e) {
                break;
            }

その次の

            if (x == 10 || x == 290) dx = -dx;
            if (y == 10 || y ==  90) dy = -dy;
            x += dx;  y += dy;
            repaint();

の部分では,x 座標が 10 または 290 なら x 軸に沿っての動く向きを逆にし,また y 座標が 10 または 90 なら y 軸に沿っての動く向きを逆にし,x,y 座標を更新し,最後に repaint() を実行します。

この repaint() は,CPU に余裕ができ次第 paint() を実行する,という意味です。 paint() メソッドは

    public void paint(Graphics g) {
        g.fillOval(x - 10, y - 10, 20, 20);
    }

となっていますので,x,y 座標の値を中心として半径が 10 の円が描かれます。


奥村晴彦

Last modified: 2005-12-29 10:57:29