広告

■□■□■□■□■□■□■□■□■□■□■□■□■□■□■
                      2013年04月26日

   Java総合講座 - 初心者から達人へのパスポート
                 2009年11月開講コース 066号

                                セルゲイ・ランダウ
 バックナンバー: http://www.flsi.co.jp/Java_text/
■□■□■□■□■□■□■□■□■□■□■□■□■□■□■


-------------------------------------------------------
当メールマガジンはこれまで初心者向けレベルと中級レベルの
2部構成で運営しておりました。
しかし、2011年の東日本大震災の際に中級レベルのコースで使用
していたワークスペースが壊れ、これを復元することが困難であ
ったため、中級レベルの部の続行を断念することになりました。
その代わりに、2009年11月開講コース(初心者向けレベル)をでき
るだけ早急に中級レベルまで引き上げて続行していくとともに、
新たに初心者向けレベルの部として2013年開講コースを並行して
開始することにしました。
2013年開講コースでは、最新の技術などを取り込んでコース内容
を刷新し、例えばAndroidのアプリ開発など、最近需要が急増して
いるテーマも含めた新しい内容を盛り込んで行きます(ただいま
準備中)。どうぞご期待下さい。

・このメールマガジンは、画面を最大化して見てください。
小さな画面で見ていると、不適切な位置で行が切れてしまう
など、問題を起すことがあります。
・このメールマガジンに掲載されているソース・コード及び
文章は特に断らない限り、すべて筆者が著作権を所有してい
ます。また、これらのソース・コードは学習用のためだけに
提供しているものです。
-------------------------------------------------------


========================================================
◆ 01.ゲームのプログラミング
========================================================


今回は、簡単なゲームのプログラムを作ってみましょう。
ハエを撃墜する(たたき落とす)ゲームです。

今回のゲーム・プログラムは絵が2つだけの極めて単純なアニ
メーションにします。
すなわち、右向きに飛ぶハエと左向きに飛ぶハエの2つの絵だけ
を使います。

また、ハエは右へ左へ上へ下へと非常に敏捷に方向転回しながら
空中を飛び回りますが、この様子は乱数を使って表現します。


では、さっそくEclipseを起動してプログラミングを開始しま
しょう。

まず最初にJStudy1プロジェクトの中にjp.co.flsi.lecture.fly
というパッケージを作ってください。

次に、このパッケージの中に下記のようなFlyPictureというクラス
を作りましょう。FlyPictureは、ハエを描画するためのクラスです。
ハエを描画するといっても極めて単純なハエの絵です。こんな単純
な絵じゃ満足できない、という人は自分で描き直してください。

--------------------------------------------------------
package jp.co.flsi.lecture.fly;

import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.GeneralPath;

public class FlyPicture {
   public void drawFly(Graphics2D g2dImage, int x, int y, boolean leftward)
{
      g2dImage.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
      g2dImage.setStroke(new BasicStroke(1f, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND));
      GeneralPath path = new GeneralPath();
      if (leftward) {
         path.moveTo(x + 3, y + 7);
         path.lineTo(x + 13, y + 7);
         path.lineTo(x + 15, y);
         path.closePath();
         g2dImage.draw(path);
         path.moveTo(x + 3, y + 7);
         path.lineTo(x + 13, y + 7);
         path.lineTo(x + 18, y + 2);
         path.closePath();
         g2dImage.draw(path);
         g2dImage.fillOval(x + 1, y + 7, 15, 4);
         g2dImage.fillOval(x, y + 7, 5, 6);
         g2dImage.drawLine(x + 6, y + 7, x, y + 15);
         g2dImage.drawLine(x + 6, y + 7, x + 8, y + 15);
         g2dImage.drawLine(x + 8, y + 7, x + 13, y + 15);
      }
      else {
         path.moveTo(x + 15, y + 7);
         path.lineTo(x + 5, y + 7);
         path.lineTo(x + 3, y);
         path.closePath();
         g2dImage.draw(path);
         path.moveTo(x + 15, y + 7);
         path.lineTo(x + 5, y + 7);
         path.lineTo(x, y + 2);
         path.closePath();
         g2dImage.draw(path);
         g2dImage.fillOval(x + 2, y + 7, 15, 4);
         g2dImage.fillOval(x + 13, y + 7, 5, 6);
         g2dImage.drawLine(x + 12, y + 7, x + 18, y + 15);
         g2dImage.drawLine(x + 12, y + 7, x + 10, y + 15);
         g2dImage.drawLine(x + 10, y + 7, x + 5, y + 15);
      }
   }
}
--------------------------------------------------------
ここで、GeneralPathのmoveTo()メソッドを呼び出し、lineTo()メソッ
ドを2回呼び出し、最後にclosePath()メソッドを呼び出しているのは、
三角形を描いてハエの羽を表現するためです。
このように、閉じた図形を描くためには最後にclosePath()メソッドを
呼び出して図形を閉じます。
また、引数のx、yはハエの座標で、leftwardはハエが左向きかどうか
(右向きならfalse)を表します。



では、続いて、ハエの絵を描画するときのキャンバス(画板)の役割
をするパネルを作成しましょう。FlyJPanelというクラス名にします。

(1) パッケージ・エクスプローラーの中のJStudy1のsrc配下の
jp.co.flsi.lecture.fly
を右クリックし、「新規」→「その他」を選択します。

(2) 「新規」ウインドウにおい「WindowBuilder」配下の
「Swing Designer」配下の「JPanel」を選択し、「次へ」ボタンを
クリックします。

(3)  「ソース・フォルダー」に「JStudy1/src」が入力されていること
を確認し、「パッケージ」がjp.co.flsi.lecture.flyになっていること
を確認し、「名前」の欄に

FlyJPanel

と入力しましょう。

(4) 「スーパークラス」が「javax.swing.JPanel」になっていることを確認し、
「完了」ボタンをクリックします。


FlyJPanel.javaのエディターが開きましたら、まず最初にRunnableインター
フェースをimplementsしましょう。(ハエに動きをつける、つまり、アニ
メーションにするためにRunnableをimplementsし、マルチスレッドのプロ
グラムにするのです。)
つまり、
--------------------------------------------------------
public class FlyJPanel extends JPanel {
--------------------------------------------------------
の行を
--------------------------------------------------------
public class FlyJPanel extends JPanel implements Runnable {
--------------------------------------------------------
に書き換えておきます。

すると、Runnableインターフェースのrun()というメソッドを実装しなけ
ればならなくなりますね。(いったんCtrl+Sキーで保管して下さい。
まだrun()を実装していないためにエラーのマークがついたはずです。)

エラーのマークを消すために、ソース・コードの中のFlyJPanelの中に
カーソルを入れ、Ctrl+1キー(Ctrlキーを押しながら、同時に数字の1のキー
を押す)でメニューを表示し、「実装されていないメソッドの追加」を
選択して下さい。

そうすると、

--------------------------------------------------------
   @Override
   public void run() {
      // TODO 自動生成されたメソッド・スタブ

   }
--------------------------------------------------------
という行が自動的に挿入されますね。
この状態でCtrl+Sキーで保管すればエラーのマークは消えますね。



次に、ソース・コードに下記のようなフィールドを追加してください。
--------------------------------------------------------
private int flyX = 100;
private int flyY = 100;
private int agility = 100;
private int velocityBase = 200;
private boolean flyLeftward = false;
private boolean flyAlive = true;
private int panelWidth = 300;
private int panelHeight = 200;
private int ngTimes = 0;
private int score = 0;
--------------------------------------------------------
ここで、flyXはハエのx座標、flyYはハエのy座標、agilityはハエの
敏捷性、velocityBaseはハエが飛ぶ速さの基準、flyLeftwardはハエが
左を向いているかどうか(falseなら右を向いている)を表すフィールド
です。このうちagilityは数値が小さいほど敏捷で、velocityBaseは数値
が大きいほど速いものとします。
また、flyAliveはハエが生きている(撃墜されていない)かどうかを表す
フィールドで、panelWidthはこのパネルの幅、panelHeightはこのパネル
の高さを表します。
ngTimesは無駄撃ちの回数(無駄撃ちは"no good"なのでngという名前を
使っており、timesは回数の意味です)、scoreはゲームの得点を表します。
panelWidthとpanelHeightはハエの移動範囲を制限する(ハエが常に見え
る範囲で飛ぶようにする)ために使用します。


また、panelWidthおよびpanelHeightへのsetter、getterとして下記のよ
うなメソッドを追加しておきましょう。
(「リファクタリング」→「フィールドのカプセル化」の機能を使うと
いいでしょう。)
--------------------------------------------------------
public void setPanelWidth(int panelWidth) {
   this.panelWidth = panelWidth;
}

public int getPanelWidth() {
   return panelWidth;
}

public void setPanelHeight(int panelHeight) {
   this.panelHeight = panelHeight;
}

public int getPanelHeight() {
   return panelHeight;
}
--------------------------------------------------------



続いて、このパネルにFlyPictureを組み込みましょう。
Designビューを開いて(エディターの画面は最大化しておきましょう)、
Palette(パレット)からSystem配下の「Choose component」(コーヒー豆のアイコン)

を選択し、「型を開く」ウインドウにおいて
FlyPicture
と入力して「OK」ボタンをクリックし、パネルの画像の外側
の白いところ(空白のエリア)をクリックすることによって貼りつけます。

そうするとソース・コード上に
--------------------------------------------------------
   private final FlyPicture flyPicture = new flyPicture;
--------------------------------------------------------

という行が自動的に生成されますね。Sourceビューを開いて確認して下さい。
(直接手でコードを書き込んでも同じことですが、このようにビジュアルな操作
をしたほうが簡単ですし、入力間違いもしなくて済みます。)

このflyPictureフィールドを使えば、FlyPictureのインスタンスを取り出す
ことができます。



続いて、FlyJPanelのソース・コードに下記のようなpaintComponent()
のオーバーライドを行っておきましょう。(下記メソッドをソース・コードの
中に書き加えて下さい。)
--------------------------------------------------------
public void paintComponent(Graphics g) {
   Image image = createImage(getPanelWidth(), getPanelHeight());
   Graphics2D g2dImage = (Graphics2D) image.getGraphics();
   flyPicture.drawFly(g2dImage, flyX, flyY, flyLeftward);
   if (!flyAlive)
      g2dImage.drawString("Score = " + score, getPanelWidth()/2,
getPanelHeight()/2);
   g.drawImage(image, 0, 0, getPanelWidth(), getPanelHeight(), null);
}
--------------------------------------------------------
このメソッドでは、FlyPictureにハエの絵を描かせていると同時に、
ハエが撃墜されたとき(flyAliveがfalseのとき)には得点(score)
をパネルの中央あたりに表示するようにしています。


続いて、ハエが飛び回る様子を描くためのアニメーションを別のスレッド
にするために、run()メソッド
--------------------------------------------------------
public void run() {
   // TODO 自動生成されたメソッド・スタブ

}
--------------------------------------------------------
を下記のように実装しておきましょう。
--------------------------------------------------------
public void run() {
   double rand;
   int times = 1;
   while (flyAlive) {
      try {
         Thread.sleep(agility);
         rand = Math.random() - 0.5;
         if (flyX < 0) flyX = -flyX;
         if (flyX > getPanelWidth()) flyX -= 100;
         if (flyY < 0) flyY = -flyY;
         if (flyY > getPanelHeight()) flyY -= 100;
         if (rand > 0) flyLeftward = false;
         else flyLeftward = true;
         flyX = flyX + (int) (rand * velocityBase);
         flyY = flyY + (int) ((Math.random() - 0.5) * velocityBase);
         repaint();
      }
      catch (InterruptedException e) {
      }
      times++;
   }
   score = 1000 - times - (50 * ngTimes);
   if (score < 0) score = 0;
   while (flyY < getPanelHeight()) {
      try {
         Thread.sleep(50);
         flyY = flyY + 10;
         repaint();
      }
      catch (InterruptedException e) {
      }
   }
}
--------------------------------------------------------
このメソッドでは、agility=100ミリ秒ごとにハエの新しい座標を
決めていますが、ハエのx座標やy座標が負の値になったりパネルの
境界の外に出たりしたときには境界のなかに収まるように修正し、
ハエが右に移動しているときはflyLeftwardをfalseに、左に移動し
ているときはflyLeftwardをtrueに設定し、あとは乱数に従って次
の座標値を決定しています。
また、最後のwhile (flyY < getPanelHeight())のループは、ハエが
たたかれた後で真っ直ぐ下に墜落していく様子をアニメーションに
するためのものです。

これらは、かなり大雑把なコードなので、ハエの飛び方をもっと
真剣に精密に表現したい人は、自分で工夫してみてください。

得点(score)の計算の仕方も、たんにハエをたたき落とすまでの
経過時間が短く無駄撃ちの回数が少ないほど得点を高くしている
だけの、安直な計算式です。
もっと意味のある計算式にしたい人は自分で工夫してみてください。
(ちなみに掛け算のほうが引き算よりも先に計算されますが、上記の
コードでは、計算の順番を分かりやすく、かつ、間違いを犯しにくく
するためにわざわざ括弧で囲んでいます。ちなみに、括弧で囲んでも
プログラムの性能が悪くなることはありません。括弧で囲む習慣を
つけた方がミスをしにくくなるし、ソース・コードを読みやすくなる
ので、得であるということを覚えておいて下さい。)



続いて、アニメーションを開始したり停止したりするために、下記
のようなメソッドを追加しましょう。
--------------------------------------------------------
public void start() {
   flyAlive = true;
   ngTimes = 0;
   Thread aThread = new Thread(this);
   aThread.start();
}

public void stop() {
   flyAlive = false;
}
--------------------------------------------------------



次に、パネルのサイズが変更されたときにpanelWidth、panelHeight
が変わるようにするために、以下のようにプログラミングを行って
ください。

(1) Designビューに戻してパネルの領域内を右クリック(あるいはComponents欄の
(javax.swing.JPanel)を右クリック)し、「Add event handler」→「コンポーネント」

→「componentResized」を選択します。

(2) ソース・コード(のコンストラクター)の中に自動生成された
--------------------------------------------------------
      addComponentListener(new ComponentAdapter() {
         @Override
         public void componentResized(ComponentEvent e) {
         }
      });

--------------------------------------------------------
というコードを下記のように編集してください。
--------------------------------------------------------
      addComponentListener(new ComponentAdapter() {
         @Override
         public void componentResized(ComponentEvent e) {
            setPanelWidth(getBounds().width);
            setPanelHeight(getBounds().height);
         }
      });
--------------------------------------------------------
ここで、getBounds()メソッドは、Component(GUI部品)の境界を表す
矩形(Rectangle型)を取り出すためのメソッドで、widthフィールドは
その幅、heightフィールドはその高さの値を持っています。
したがって、上記のコードを使えば、このパネルのサイズが変更された
ときにその変更後の幅や高さがpanelWidthやpanelHeightに設定される
ことになります。



このゲームでは、ハエをたたく行為をマウスのクリックで表現すること
にしますので、マウスをクリックしたときにマウス・ポインターがハエの
絵の上に乗っかっていたかどうかを確認する必要があります。
そして、マウス・ポインターがハエの絵の上に乗っかっていた場合には
flyAliveの値をfalseにします。つまり、ハエが亡くなることにします。
このために、以下のようにプログラミングを行ってください。

(1) Designビューに戻してパネルの領域内を右クリック(あるいはComponents欄の
(javax.swing.JPanel)を右クリック)し、「Add event handler」→「mouse」
→「mouseClicked」を選択します。

(2) ソース・コード(のコンストラクター)の中に自動生成された
--------------------------------------------------------
      addMouseListener(new MouseAdapter() {
         @Override
         public void mouseClicked(MouseEvent e) {
         }
      });
--------------------------------------------------------
というコードを下記のように編集してください。
--------------------------------------------------------
      addMouseListener(new MouseAdapter() {
         @Override
         public void mouseClicked(MouseEvent e) {
            Rectangle flyRegion = new Rectangle(flyX, flyY, 18, 15);
            if (flyRegion.contains(e.getX(), e.getY())) flyAlive = false;
            else ngTimes++;
         }
      });
--------------------------------------------------------
ここでは、ハエのサイズを大雑把に幅18、高さ15にしています。
そして、この領域内にマウス・ポインターの座標が含まれていれば
flyAliveの値をfalseにしています。
逆に領域内にマウス・ポインターの座標が含まれていなければ無駄撃ち
ということですから、ngTimesを増加しています。



次に、ゲームを開始するためのボタンと途中で停止するためのボタン
を貼り付けることにしましょう。

このパネルに、以下のようにしてJButtonを2つ貼り付けてください。

(1) Designビューに戻し、パネルのレイアウト・マネジャー(LayoutManager)
をBorderLayoutにしましょう。
つまり、パネルの領域内(あるいはComponents欄の(javax.swing.JPanel))を
右クリックし、「Set layout」→「BorderLayout」を選択します。

(2) ボタンを貼り付ける土台にするためにJPanelをNorthの位置に貼り
付けましょう。

(3) そのパネルの上に一つ目のJButtonを貼り付けましょう。
Variable(変数名)はjButtonStartにしましょう。
そのtextプロパティーの値を「ゲームの開始」にしましょう。

(3) 同様にパネルの上のjButtonStartの右隣に二つ目のJButtonを貼り付けましょう。

Variable(変数名)はjButtonStopにしましょう。
textプロパティーの値を「ゲームの中止」にしましょう。


続いて、「ゲームの開始」ボタンをクリックしたらゲームが開始する
ようにプログラミングしましょう。以下のように作業してください。

(1) 「ゲームの開始」ボタンを右クリックし、「Add event handler」→「アクション」

→「actionPerformed」を選択します。

(2) ソース・コード(のコンストラクター)の中に自動生成された以下のコード
--------------------------------------------------------
      jButtonStart.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
         }
      });
--------------------------------------------------------
を下記のように編集しましょう。
--------------------------------------------------------
      jButtonStart.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            start();
         }
      });
--------------------------------------------------------


続いて、「ゲームの中止」ボタンをクリックしたらゲームが中止する
ようにプログラミングしましょう。以下のように作業してください。

(1) Designビューに戻って、「ゲームの中止」ボタンを右クリックし、
「Add event handler」→「アクション」→「actionPerformed」を
選択します。

(2) ソース・コード(のコンストラクター)の中に自動生成された以下のコード
--------------------------------------------------------
      jButtonStop.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
         }
      });
--------------------------------------------------------
を下記のように編集しましょう。
--------------------------------------------------------
      jButtonStop.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            stop();
         }
      });
--------------------------------------------------------


それから、テスト時(FlyJPanelをJava beanだけでテストする時)にパネルの
サイズを設定しておくために、FlyJPanelのコンストラクターの先頭に下記
のようにサイズを設定するコードを書き込んでおきましょう。
--------------------------------------------------------
setSize(panelWidth, panelHeight);
--------------------------------------------------------



以上で、FlyJPanelは完成です。念のために、FlyJPanelの全ソース・コード
を下に提示しておきます。
--------------------------------------------------------
package jp.co.flsi.lecture.fly;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;

import javax.swing.JPanel;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.BorderLayout;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class FlyJPanel extends JPanel implements Runnable {
   private int flyX = 100;
   private int flyY = 100;
   private int agility = 100;  // もともとは100
   private int velocityBase = 200;
   private boolean flyLeftward = false;
   private boolean flyAlive = true;
   private int panelWidth = 300;
   private int panelHeight = 200;
   private int ngTimes = 0;
   private int score = 0;
   /**
    * @wbp.nonvisual location=607,196
    */
   private final FlyPicture flyPicture = new FlyPicture();

   /**
    * Create the panel.
    */
   public FlyJPanel() {
      setSize(panelWidth, panelHeight);
      setLayout(new BorderLayout(0, 0));

      JPanel panel = new JPanel();
      add(panel, BorderLayout.NORTH);

      JButton jButtonStart = new
JButton("\u30B2\u30FC\u30E0\u306E\u958B\u59CB");
      jButtonStart.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            start();
         }
      });
      panel.add(jButtonStart);

      JButton jButtonStop = new
JButton("\u30B2\u30FC\u30E0\u306E\u4E2D\u6B62");
      jButtonStop.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            stop();
         }
      });
      panel.add(jButtonStop);
      addMouseListener(new MouseAdapter() {
         @Override
         public void mouseClicked(MouseEvent e) {
            Rectangle flyRegion = new Rectangle(flyX, flyY, 18, 15);
            if (flyRegion.contains(e.getX(), e.getY())) flyAlive = false;
            else ngTimes++;
         }
      });
      addComponentListener(new ComponentAdapter() {
         @Override
         public void componentResized(ComponentEvent e) {
            setPanelWidth(getBounds().width);
            setPanelHeight(getBounds().height);
         }
      });

   }

   public void paintComponent(Graphics g) {
      Image image = createImage(getPanelWidth(), getPanelHeight());
      Graphics2D g2dImage = (Graphics2D) image.getGraphics();
      flyPicture.drawFly(g2dImage, flyX, flyY, flyLeftward);
      if (!flyAlive)
         g2dImage.drawString("Score = " + score, getPanelWidth()/2,
getPanelHeight()/2);
      g.drawImage(image, 0, 0, getPanelWidth(), getPanelHeight(), null);
   }

   public void start() {
      flyAlive = true;
      ngTimes = 0;
      Thread aThread = new Thread(this);
      aThread.start();
   }

   public void stop() {
      flyAlive = false;
   }

   @Override
   public void run() {
      double rand;
      int times = 1;
      while (flyAlive) {
         try {
            Thread.sleep(agility);
            rand = Math.random() - 0.5;
            if (flyX < 0) flyX = -flyX;
            if (flyX > getPanelWidth()) flyX -= 100;
            if (flyY < 0) flyY = -flyY;
            if (flyY > getPanelHeight()) flyY -= 100;
            if (rand > 0) flyLeftward = false;
            else flyLeftward = true;
            flyX = flyX + (int) (rand * velocityBase);
            flyY = flyY + (int) ((Math.random() - 0.5) * velocityBase);
            repaint();
         }
         catch (InterruptedException e) {
         }
         times++;
      }
      score = 1000 - times - (50 * ngTimes);
      if (score < 0) score = 0;
      while (flyY < getPanelHeight()) {
         try {
            Thread.sleep(50);
            flyY = flyY + 10;
            repaint();
         }
         catch (InterruptedException e) {
         }
      }
   }

   public void setPanelWidth(int panelWidth) {
      this.panelWidth = panelWidth;
   }

   public int getPanelWidth() {
      return panelWidth;
   }

   public void setPanelHeight(int panelHeight) {
      this.panelHeight = panelHeight;
   }

   public int getPanelHeight() {
      return panelHeight;
   }

}
--------------------------------------------------------



では、保管をしてからテストをしてみましょう。パッケージ・エクス
プローラーの中でFlyJPanelを右クリックし、「実行」→「Java Bean」
を選択してみてください。
「ゲームの開始」ボタンをクリックするとハエが動き始め、
「ゲームの中止」ボタンをクリックするとハエが停止して墜落しますね。
また、途中でパネル(ウインドウ)のサイズを変えて(広げたり縮めたり
して)みても、ちゃんと対応しますね。

一方、ハエを撃墜するのはなかなかむずかしいことがわかりますね。
うまくハエのところにマウス・ポインターをもってこれたと思っても、
クリック(マウスのボタンを押してから離す)の動作の間にハエが動い
てしまうとダメです。ハエが止まっている間にマウスのボタンを押す操作
から離す操作までを完了させなければなりません。
もし、どうしてもハエの動きが速すぎて撃墜できないようであれば、
agilityやvelocityBaseの値を調整してみてください。
動作確認のためには、agilityは1000(1秒間隔でハエが動く)くらい
にして、マウスのボタンを何度もすばやく連打してみたほうがいいかも
しれません。



では、最後に、このFlyJPanelを使ってゲームを行うためのアプレット
を作りましょう。FlyAppletというクラス名にします。

(1) パッケージ・エクスプローラーの中のJStudy1のsrc配下の
jp.co.flsi.lecture.fly
を右クリックし、「新規」→「その他」を選択します。

(2) 「新規」ウインドウにおい「WindowBuilder」配下の
「Swing Designer」配下の「JApplet」を選択し、「次へ」ボタンを
クリックします。

(3)  「ソース・フォルダー」に「JStudy1/src」が入力されていること
を確認し、「パッケージ」がjp.co.flsi.lecture.flyになっていること
を確認し、「名前」の欄に

FlyApplet

と入力しましょう。

(4) 「スーパークラス」が「javax.swing.JApplet」になっていることを確認し、
「完了」ボタンをクリックします。


では、このFlyAppletのDesignビューを開いて、まず最初にアプレットの中の
レイアウト・マネジャーをnullにしておきましょう。
すなわち、アプレットの画像の中を右クリックし、「Set Layout」→
「Absolute layout」を選択します。
また、同パネルの背景(background)を白色にしておきましょう。アプレット
の画像の中をクリックして選択状態にし、Properties欄のbackgroundプロパティー
の値をWHITE(「...」ボタンをクリックして「Color chooser」ウインドウ内の
「AWT colors」タブで「WHITE」を選択し「OK」ボタンをクリック)にしておき
ましょう。

続いて、このアプレットにFlyJPanelを貼り付けることにしましょう。
PaletteからSystem配下の「Choose component」を選択し、「型を開く」ウインドウ
において
FlyJPanel
を入力して「OK」ボタンをクリックし、アプレットの画像の中に
貼り付け(クリック)してください。

そうするとソース・コードのFlyApplet()コンストラクターの中に
--------------------------------------------------------
      FlyJPanel flyJPanel = new FlyJPanel();
      flyJPanel.setBounds(94, 79, 209, 31);
      getContentPane().add(flyJPanel);
--------------------------------------------------------
というようなコードが自動的に追加されますね。
このコードの中の
--------------------------------------------------------
      flyJPanel.setBounds(94, 79, 209, 31);
--------------------------------------------------------
という行(数字は貼り付け位置によって異なります)を
--------------------------------------------------------
      flyJPanel.setBounds(0, 0, 600, 400);
--------------------------------------------------------
のように書き換えて、flyJPanelの張り付け位置とサイズを調整しておきましょう。



続いて、このアプレットにFlyJPanelを貼り付けることにしましょう。
パレットから「Choose Bean」を選択し、「Choose a Bean」ウインドウ
においてFlyJPanelを入力して「OK」ボタンをクリックし、アプレット
の画像の中のCenterの位置に貼り付け(クリック)してください。
Bean NameはflyJPanelのままにしておきましょう。


これで、今回のゲーム・ソフトは完成です。
保管してFlyAppletを起動し、テストしてみてください。(アプレットのサイズは
ウインドウの端をドラッグして自分で調整して下さい。)


では、今回はここまでにします。なお、今回作成したアプレットは
ホームページにも掲載しておきます。

何か、わからないところがありましたら、下記のWebページまで質問を
お寄せください。


(続く)



========================================================
◆ 02.Java(文法等)解説 [JavaBeans (1)]
========================================================

[JavaBeansの概要]

JavaBeansというのは、Javaのクラスを部品化し、ビルダー・ツール
(builder tool = プログラムをビジュアル(visual = 視覚的)に構築
するツール)で部品をビジュアルに操作することによってプログラム
を構築できるようにすることを目的として作られた技術です。

JavaBeansという名前は、この技術仕様の名前であり、JavaBeansに
おける部品のことをBean(複数形はBeans)と呼びます。
JavaのBeanというときは単数形(ただし、英語では複数個あれば複数形)
ですが、仕様名は必ずJavaBeansと、複数形にしますので、間違えないで
ください(間違えたからって、罰則があるわけではありませんが)。

JavaBeansを最大限に利用した代表的なビルダー・ツールとしては、IBM
のVisualAgeという製品に含まれるビジュアル・コンポジション・エディ
ターというものがありました。
このツールは、既存のBeanをツール上に配置させ、Bean同士をビジュアル
に線でつなぎ合わせるだけでプログラムが作れるというもので、極めて
生産性の高いものでしたが、残念ながらあまり普及しませんでした。
あまりにも従来のプログラミング手法と違いすぎるために、従来型のプロ
グラマーにはとっつきにくかった(習得しにくかった)ことが原因だと
思われますが、結局この製品は無くなりました。
しかし、実はEclipseは元々このVisualAgeを改築して作られたツール
なのです。
残念ながら、ビジュアル・コンポジション・エディターは無くなりました
が、他のいくつかの機能がEclipseの中に引き継がれています。


このように、JavaBeansによってビジュアルにプログラムを構築するプロ
グラミング手法自体は普及しませんでした(ただし、Visual Editorの
ようにビジュアルに部品を貼り付けるという程度の手法なら普及して
います)が、JavaBeansが持つさまざまな技術は現在でも有効利用され
ています。


そこで、今回から、現在でも有効利用されているJavaBeansの技術を
少しずつ解説していきたいと思います。

まず最初に、Beanが持っている主な機能として、以下のものを概観して
いきましょう。

・Beanが持つフィーチャー(feature = 機構)
イベント、プロパティー、メソッド
・イントロスペクション(introspection)
Beanが持つフィーチャーをBeanの外から調べる機能
・シリアライゼーション(直列化:serialization)
Beanの状態をファイルなどに保管したり、逆に復元したりする機能
あるいはネットワークを通してBeanの状態をやり取りすることもできる。


Beanの元々の意味は豆です。Javaの名前がジャワ・コーヒーから来ている
ことから、Beanという名前もコーヒー豆に由来します。
コーヒー豆がコーヒーの原料であるように、Javaにおける部品のことを
Beanと呼ぶことにしたのです。

部品のことをコンポーネント(Component)と呼ぶことがありますが、
JavaにおいてはComponentはGUI部品のクラス名になっていますので
注意してください。
Beanと言った場合は、GUI部品(Component)だけでなく、GUI以外の
一般のクラスも含みます。


では、クラスならなんでもBeanと呼べるかというとそうではなく、Bean
と呼ばれるためにはそのクラスが最低限でも以下の特徴を持っている
必要があります。

(1) フィーチャー(イベント、プロパティー、メソッド)を持っている
こと。
(2) publicなデフォルトのコンストラクター(引数のないpublicなコン
ストラクター)を持っていること。
(3) イントロスペクションの機能を提供すること。



(1) Beanのフィーチャーの概観

まず最初に、Beanはイベント(event)、プロパティー(property)、
メソッド(method)のいずれかのフィーチャー(feature = 機構)を持っ
ていなければなりません。これらのフィーチャーはBeanが外部のオブジェ
クトとやり取りするために必要なものであり、イベントもプロパティーも
メソッドもどのフィーチャーも持っていないクラスは無用のものとなって
しまい、存在する価値自体がなくなってしまいます。


このうち、イベントというのは今までに頻繁に出てきましたが、何らかの
事象を通知する機構です。

プロパティーというのは、簡単にいうとオブジェクト指向における属性
のことだと思えばいいのですが、ただし、正確にいうと属性を表すのは
フィールドであり、プロパティーは、そのフィールドに値を設定したり
値を取り出したりする、setter、getterなどのメソッドを含めた機構の
名称です。
むしろ外からは見えないフィールドなどは重要ではなく、publicな
setterやgetterを実装することによって、はじめてプロパティーが提供
されたことになります。

メソッドというのは、ずばりオブジェクト指向におけるメソッドのこと
です。プロパティーのsetterやgetterを除く、一般のメソッドのことを
意味しますが、ただし、Beanのフィーチャーとしてのメソッドは外部の
オブジェクトとやり取りするためのものであるため、publicなものに
限られます。


(2) publicなデフォルトのコンストラクター

Beanはビルダー・ツールやサーブレット、JSPなどによって、デフォルト
のコンストラクター(引数なしのコンストラクター)が呼び出されてイン
スタンスが生成されます(詳しくは後述)。
そのために、publicなデフォルトのコンストラクターが必ず必要になります。

コンストラクターの定義が一切ない場合にはJavaコンパイラーが自動的に
デフォルトのコンストラクターを生成してくれるので問題ないのですが、
引数つきのコンストラクターを定義した場合には、必ずデフォルトのコン
ストラクターも明示的に定義しておく必要があります。なお、デフォルト
のコンストラクターには外部からアクセスできる必要があります(ビルダー・
ツールやサーブレット、JSPなどからアクセスされる)から必ずpublic指定
でなければなりません。


(3) イントロスペクションの概観

イントロスペクションは、Beanが持つフィーチャーを外部から認識できる
機能であり、この機能はビルダー・ツールなどによって使用されます。
この機能を提供するためには、以下のいずれかのルールに従う必要があり
ます。

・フィーチャーの名前が一定の命名規則に従っていること。
・BeanInfoと呼ばれるクラスを実装していること。

BeanInfoが用意されている場合は、BeanのフィーチャーはBeanInfoを通して
認識されますが、BeanInfoが用意されていない場合は、Beanのフィーチャー
はリフレクションという仕組みを通して命名規則に従って認識されます。

リフレクション(reflection)というのは、プログラムの実行時にクラス
のフィールドやメソッドを調べる仕組みであり、この仕組みを使えば、
実行時にそのクラスがどのようなメソッドを持っているのかを調べて、
そのメソッドを実行することもできます。
ビルダー・ツールだけでなく、一般のプログラマーもリフレクションの
仕組みを利用することができます(詳しくは後述)。



(続く)



以上、今回は
┌───────────────────────────┐
・GeneralPathを使った閉じた図形(今回は三角形)の描き方
・Component(GUI部品)の境界の取得方法(getBounds())
・Componentの境界からサイズを取得する方法(getBounds().width、
getBounds().height)
・Componentのサイズが変更されたときに処理を実行する方法
(ComponentListener(今回はComponentAdapter)の
componentResized()メソッドを実装。および、Componentの
addComponentListener()メソッドでComponentに登録)
└───────────────────────────┘
を学習しました。
では、また来週。



================================================
◆ 03.演習問題
================================================

上記のハエたたきゲームにおいて、agilityフィールドやvelocityBase
フィールドの値をユーザーが変更できるように、プログラミングして
みてください。(たとえば、JTextFieldで数値を入力できるようにし、
JButtonをクリックすると入力した値がフィールドに代入されるように
します。)



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      http://www.flsi.co.jp/Java_text/
★このメールマガジンは
     「まぐまぐ(http://www.mag2.com)」
 を利用して発行しています。
★バックナンバーは
      http://www.flsi.co.jp/Java_text/
 にあります。
★このメールマガジンの登録/解除は下記Webページでできます。
      http://www.mag2.com/m/0000193915.html
★このメールマガジンへの質問は下記Webページにて受け付けて
 います。わからない所がありましたら、どしどしと質問をお寄
 せください。
      http://www.flsi.co.jp/Java_text/
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Copyright (C) 2007-2013 Future Lifestyle Inc. 不許無断複製