1学期第7週

テーマ:(続)めだか飼育システム


(96.9.25更新)
先週から作成している「めだか飼育システム」について、さらに研究していきます。
議論にまとまりがついた時点で整理しますが、中間報告です。


まとめ
上拾石@名古屋大さんのまとめページ

プログラムの基本方針

(畑さん)

めだかクラス:
<変数>
・めだかの位置
・めだかの色
・めだかの形
<メソッド>
・めだかクラスの生成(コンストラクタ)
     - めだかの位置の決定
     - めだかの色の決定(任意の色生成クラスのメソッドで作る)
・めだかを描画(draw()メソッド)
     - インスタンス変数の「めだかの色」でめだかを描画

めだかアプレットクラス:
<変数>
・めだか(Medakaクラス)
<メソッド>
・初期化(めだかの生成 init()メソッド)
     - めだかクラスのインスタンスの生成
・描画(めだかの描画 paint()メソッド)
     - めだかを描画する。(Medakaクラスのdraw()メソッドの呼び出し)

任意の色生成クラス:
<変数>
・Randomクラス
<メソッド>
・RGB値の決定
(このクラス内のみで利用されるメソッド ramdomRGBValue()メソッド)
     - インスタンス変数であるRandomクラスのインスタンスメソッドを
        利用して0〜255までのあいだの任意の整数値を取得する。
・任意の色の作成(randomColor()メソッド)
     - randomRGBValue()メソッドを利用して色の合成に必要なRGB値
       を取得し、任意の色を作成する。

めだかの位置について
(しらかみさん)

>    Medaka(int x, int y) {
>       position = new Point(x, y);
>    }

  これが気持ち悪いです。
  めだかが生まれる時に位置が決まるものでしょうか?

        とあるめだかが生まれる
        そのめだかを水槽に入れてあげる
        そのときにめだかの水槽内位置が決まる

というのが自然な気がしませんか。

public class MedakaApplet extends java.applet.Applet {
    Medaka medaka;

    public void init() {
       medaka = new Medaka(100, 100);
       setBackground(Color.white);
    }

    public void paint(Graphics g) {
       medaka.draw(g);
    }
}

  随分すっきりしましたね。
  これなら、めだかを2匹にするとかも簡単にできそう。


(中野さん)   位置は、めだかが属する世界のものなので、実際はその外の世界から   必要な時に与えられるものではないかと思われます。      私の考えていたモデルでは、メダカは自立して行動するものですから   (最終的には、細かい形式はどうあれスレッドで実装する事になるはず    です。)   表に対して見えているメソッドは、action()のみだろうと考えていました。    #便宜上、描画したりするためのメソッドは別の場合もある。    #本来なら、メダカが action() したと同時に、world の更新を行う    #わけだが、その際、action() そのものに、(例えば)メダカを描画する    #ルーチンと比べて時間がかかりすぎる場合は、恐らく、描画する    #ルーチンを切り離して public で実装する事になるはず。   そのなかで、最初に perceive()して、内部的な action() を決定すると。   それに対して、表の世界は行動後のメダカの位置を要求するので       #ここらへんは、意見の別れるところだと思います。      結局   class Medaka {     public 3DPosition action(WORLD world);     // もろもろの private なメソッド   };   class WORLD {     // メダカが存在している空間にあるオブジェクトへのリスト。     // または、メダカが存在している空間そのもの。          // 現在のメダカの空間相対な位置情報は、このクラスが保持している。   };   class 3DPosition {     int x;     int y;     int z;          // 他の座標系へのマッピング等のメソッド群   };      wG赴wN他の座標系へのマッピング等   めだかが返せる、3DPosition は、もちろん、前回に観察された時からの   相対位置情報です。ありていに言えば、前回観察された位置を原点とする   ベクトルで表現するわけです。   


めだかの色について
めだかに色を付けたいので、ちょっと改造しました。(畑さん)
以下のようなソースにしました。


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

class Medaka  {
        private Point position;
        private Color color;
        private String figure = ">^)))><";

        Medaka(int x, int y) {
                position = new Point(x, y);

                Random  random = new Random();          // *1
                int     red   = random.nextInt() % 256; // *2
                int     green = random.nextInt() % 256;
                int     blue  = random.nextInt() % 256;

                if( red < 0 ) {                         // *3
                        red = -red;
                }
                if( green < 0 ) {
                        green = -green;
                }
                if( blue < 0 ) {
                        blue = -blue;
                }

                color = new Color(red, green, blue);     // *4
         }

        public void draw(Graphics g) {
                Color current = g.getColor();

                g.setColor(color);
                g.drawString(figure,position.x,position.y);
                g.setColor(current);
        }
}

public class MedakaApplet extends Applet {
        Medaka  medaka;

        public void init() {
                medaka = new Medaka(100, 100);
                setBackground(Color.cyan);
        }

        public void paint (Graphics g) {
                medaka.draw(g);
        }
}


*1
 Randomクラス:
  乱数を発生させることができるクラスです。めだかの色を指定
  するために今回は乱数を使うことにしてます。
  正確にいうと、めだかの色のを構成する光の3原色を乱数で
  設定するためにRandomクラスを利用します。

*2
 random.nextInt()メソッド:
  -2147483648〜214783647までの間の任意の整数値を返すメソッド
 random.nextInt % 256 の意味:
  色を構成する3原色は、それぞれ0〜255までの256の値を持つこと
  が可能です。発生した値を0〜255の範囲の整数の値にするために
  % 256としています。なんで、色の範囲が256なのかはJDKのAPI
  ドキュメントに0〜255までと書いてたので。(^^;;
  %というのは、演算子で、例えば、10 % 3 とした場合、10を3で
  割った場合の余りはいくらかを計算してくれる演算子です。

*3
 if( red < 0 )の意味:
  各原色の値は、正の整数値しか認めません。しかし、
  red = random.nextInt() % 256では、-255〜255までの値になる可能性
  があります。ゆえに、もしredが0以下の数値の場合は正の値にして
  あげる必要があります。ここでは、redの値が0より小さいかを比べて
  います。でもって、if(red < 0){・・・}の { 〜 }の間に、もし、
  redが0より小さい場合の処理を記述します。ここでは、

  if( red < 0 ) {  // もし、redの値が0より小さい場合
      red = -red;  // redの値に-1をかけた値をredの値にする。
  }  

  という風になります。

*4
 乱数で獲得した、各原色を元にしてColorクラスのインスタンスを生成
 しています。

ここで畑さんがのせていたソースに自分なりにアレンジをしてみました。 (美里さん) import java.awt.*; import java.util.*; public class ColorMedaka{ private String figure = ">^)))><"; private Point position; private Color color; private int red, green, blue; ColorMedaka( int x, int y ){ position = new Point( x, y ); setColor(); } private void setColor(){ //1 Random random = new Random(); red = random.nextInt() % 256; green = random.nextInt() % 256; blue = random.nextInt() % 256; if( red < 0 ) { red = -red; } if( green < 0 ) { green = -green; } if( blue < 0 ) { blue = -blue; } color = new Color(red, green, blue); } private boolean match(){ //2 if( red == 0 && green == 255 && blue == 255 ){ return ( true ); } return ( false ); } public void DrawMedaka( Graphics g ){ Color current = g.getColor(); while( match() ){ //3 setColor(); } g.setColor( color ); g.drawString( figure, position.x, position.y ); g.setColor( current ); } } public class ColorMedakaApplet extends java.applet.Applet{ ColorMedaka medaka1, medaka2, medaka3; public void init(){ medaka1 = new ColorMedaka( 100, 100 ); medaka2 = new ColorMedaka( 100, 150 ); medaka3 = new ColorMedaka( 100, 200 ); resize( 300, 300 ); repaint(); setBackground( Color.cyan ); } public void paint( Graphics g ){ medaka1.DrawMedaka( g ); medaka2.DrawMedaka( g ); medaka3.DrawMedaka( g ); } } 1ですが、めだかの色をメソッドで行いました。 そのときのrandom, red, green, blueは局所変数ではなく インスタンス変数にしました。なぜなら、背景の色と めだかの色が一緒の時はめだかの色を再設定したかったからです。 そこで2で背景の色とめだかの色が等しいかをチェックしています。 背景の色は僕の場合init()でcyanに設定しているので、その色の RGB値と比較しています。 3で背景とめだかの色が等しければ何回でも再設定を行うように しています。(そんなことは起こらないとおもうんw注sうように =$N?' これで、万が一背景の色とめだかの色が一致したときの対処が できたと思います。


(佐藤さん) この「適当に色を決めてくれる物」を、RandomColorGeneratorとし ますと、めだかのコンストラクタはこのクラスのインスタンスを使っ て、適当な色を一つもらう、とすればすむ気がします。そして、こ のクラスは、他の適当な色が必要となる場面でも、そのまま利用す ることができるわけです。 というわけで、これをクラスとして定義してみると、こんなかんじ。 色の決め方は畑さんが示した方法と基本的に一緒です。 なお、import文は省略してありますので、適宜追加するか、Medaka アプレットのソースに組み込む必要が有ります。 class RandomColorGenerator { private Random randomNumberGenerator = new Random(); private randomRGBValue() { int value = randomNumberGenerator.nextInt() % 256; if (value < 0) { value = -value; } return value; } Color randomColor() { int red = randomRGBValue(); int green = randomRGBValue(); int blue = randomRGBValue(); return new Color(red, green, blue); } } red, green, blueの各値を決める部分も「RGBの値を適当に決める」 メソッドrandomRGBValueとしました。すると、「適当に色を決める」 方法は、「red, green, blueをそれぞれ適当に決めて、Colorのイ ンスタンスを作る」という一言で表せることになります。「RGBの 値を適当に決める」というのが、「0-255の範囲の乱数を得る」と いうことなのですが、それが「適当な整数の乱数を256で割った余 りを得て、絶対値を取る」という風に実現される、と。これまた一 言で言い表せるというわけですね。 これを使うと、先のコンストラクタはこんな感じになります。 Medaka(int x, int y) { position = new Point(x, y); color = generator.randomColor(); } この中で参照しているgeneratorは、staic変数で、内容は RandomColorGeneratorのインスタンス。つまり、Medakaクラスの定 義に、次の文が必要ということです。 private static RandomColorGenerator generator = new RandomColorGenerator(); 全体のソースは今回は載せません。試してみたい方は、説明を理解 した上で、自分で改造してみて下さいね。


めだかの学校 教室
教員に対するご意見はこちらへ:(Mail to : cbu-staff@ML.psn.or.jp
用務員:臼井義美 (Mail to : yosimi-u@is.aist-nara.ac.jp