アプレット中のスレッド

アプレットの中でアニメーション(リアルタイムゲームを含む)をするのは結構 面倒である.まず,アプレットにはmainがない(書いても呼ばれない)ので,明 示的にスレッドを作る必要がある.アプレットは Applet クラスのサブクラス で作らなくてはいけないので,Runnable インタフェースを継承することになる.

mainがないのでどこで,実行のためのスレッドを作るかということだが, initメソッドの中で作るのも可能だが,一度スレッドを作るとWWWブラウザで 他のページに移っても動き続けるというのはやっかいなので,アプレットのあ るページに移動した際に呼ばれる start メソッドの中で作って,アプレット のあるページから抜けた際に呼ばれる stop メソッドの中で終了させるのが一 般的である.アプレットとで書いたゲームが以下のプログラムである.

// <applet code=BallGameApplet width=300 height=600></applet>
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class BallGameApplet extends Applet implements Runnable{
    // ボールの座標,速度のx, y成分の宣言
  int ball_x=100,ball_y=100,ball_vx=16,ball_vy=12;
    // ボールの大きさ
  int ball_size=20;
    // 画面の幅,高さ
  int width=300, height=600;
    // バーの座標
  int bar_x=0, bar_y=550;
    // バーの速度
  int bar_vx=0;
    // バーの幅,高さ
  int bar_width=60,bar_height=10;
    // BallGameクラスのコンストラクタ
    // アニメーションを行うためのスレッド
  public Thread th=null;
    // WWWブラウザがアプレットを含むページに来たときに呼ばれる.
  public void init(){
    System.out.println("init is called");
    addKeyListener(new KeyAdapter(){
    // キーが押された時にこのメソッドが呼ばれる.
      public void keyPressed(KeyEvent e){
	System.out.println("keyPressed("+e+")");
	int key=e.getKeyChar();
	// 'h' のキーが押された時は,バーの移動速度を -10に
	if(key=='h'){
	  bar_vx= -10;
	}
	// 'l' のキーが押された時は,バーの移動速度を 10に
	else if(key=='l'){
	  bar_vx=10;
	}
	// 'j' のキーが押された時は,バーの移動速度を 0に
	else if(key=='j'){
	  bar_vx=0;
	}
	else if(key=='q'){
	  System.exit(0);
	}
      }
    });
    addMouseListener(new MouseAdapter(){
      public void mousePressed(MouseEvent e){
	System.out.println(e);
      }
    });
    requestFocus();
  }
  public void start(){
    System.out.println("start is called");
      // スレッドができていない時はここで作成する
    if(th==null){ th=new Thread(this); th.start();}
  }
  public void stop(){
    System.out.println("stop is called");
      // スレッドがある時はスレッドを止める
    th=null;
  }
    // 上のstartメソッドの中で,th.start()が呼ばれるとスレッドからこの
    // メソッドが呼ばれる.
  public void run(){
    Thread thisThread=Thread.currentThread();
    while(th==thisThread){
        // 0.1秒(100ミリ秒)スリープする
      try { Thread.sleep(100); }
      catch(Exception e){}
        // ボール,バーの移動をおこなう
      timeTick();
    }
  }
  void timeTick(){
      // バーの移動
    bar_x=bar_x+bar_vx;
      // 左端から飛び出そうになったら左端に合わせる
    if(bar_x<0) bar_x=0;
      // 右端から飛び出そうになったら右端に合わせる
    else if(bar_x+bar_width >width) bar_x=width-bar_width;
      // ボールの移動.古い座標を保存
    int old_x=ball_x;
    int old_y=ball_y;
      // 速度に従って,次の座標を決定
    ball_x=ball_x+ball_vx;
    ball_y=ball_y+ball_vy;
      // 左端から飛び出そうになったら,反射させる
    if(ball_x<0){
      ball_x= -ball_x;
      ball_vx= -ball_vx;
    }
      // 右端から飛び出そうになったら,反射させる
    else if(ball_x >width-ball_size){
      ball_x=(width-ball_size)-(ball_x-(width-ball_size));
      ball_vx= -ball_vx;
    }
      // 上端から飛び出そうになったら,反射させる
    if(ball_y<0.0){
      ball_y= -ball_y;
      ball_vy= -ball_vy;
    }
      // バーのある線を通過しそうになったら,
    else if(ball_y >bar_y && old_y<=bar_y){
        // バーのある線を横切るX座標を計算
      int x=old_x+(ball_x-old_x)*(bar_y-old_y)/(ball_y-old_y);
        // バーと接触している場合は反射させる.
      if(bar_x <= x && x<=bar_x+bar_width){
        ball_y=bar_y-(ball_y-bar_y);
        ball_vy= -ball_vy;
      }
    }
      // 下端から飛び出そうになったら,反射させる
    else if(ball_y > height){
      ball_vy=-ball_vy;
    }
      // 再表示
    repaint();
  }
  Image offScreenImage;
  Graphics offScreenGraphics;
  public void update(Graphics g){
    if(offScreenImage==null){
      offScreenImage=createImage(getWidth(),getHeight()); // オフスクリーンイメージを600x400のサイズで作成
      offScreenGraphics=offScreenImage.getGraphics(); // オフスクリーンイメージに描画するための Graphics オブジェクト
    }
    paint(offScreenGraphics); // 次の画面のイメージを作る.
    g.drawImage(offScreenImage,0,0,this); // イメージを本物のスクリーンに書き込む
  }
  public void paint(Graphics g){
      // 描画色を黒にする.
    g.setColor(Color.black);
      // 全体を塗り潰す
    g.fillRect(0,0,width,height);
      // 描画色を白にする.
    g.setColor(Color.white);
      // 警告文字列を(0,200)の点を左下にして描く
    g.drawString("Not a game, but a excercise in CP1(Tue/tanaka)",0,200);
      // 描画色を赤にする.
    g.setColor(Color.red);
      // ボールを描く
    g.fillOval(ball_x,ball_y,ball_size,ball_size);
      // オフスクリーンイメージへの描画色を青にする.
    g.setColor(Color.blue);
      // バーを描く
    g.fillRect(bar_x,bar_y,bar_width,bar_height);
  }
}
アプレット