2学期第6週

テーマ:めだかっちの実習(3)

 

(67.5.30更新)

はざまさんの作成された「めだかっち」の完成品です。
見せていただきましょう!

まず、コメントです:

 
PigFaceMedakaクラスとDeadMedakaクラスはメダカクラスのサブクラスにしました。
意外と簡単にメダカの仲間が作れて、変身も簡単に出来てしまって驚きです。(^_^)
 
変数のprivateやprotected指定はこんなもんでいいでしょうか?
MedakAappletクラスでtime、Vector mucks、Vector foodsをstatic変数にしてしまい
ましたが...。(^^;
 
何も指定しないで、例えば  int food_x; とした場合は public指定したことに
なるのですか?
 
見た目にはわかりませんが、欠陥があります。それは、メダカがブタメダカに
変身した時(体重80gで変身)、lossWeight が0から1になるまでは
メダカは泳ぐ方向を変えたり出来ないみたいです。(?_?)
 
time変数はMedakaAppletクラスのを使いました。そうしないと
メダカクラスの lossWeight メソッドがどうも働かないようです。(。_゜)?
疑問点がいくつかありますが、原因がよくわからないので
そのままにしてあります。^^)^^);;
 
実際に音をつけるとゲームらしくなりますね。(^_^)
ボタンの横に表示されているのはめだかっち世界時刻です。
 

HTMLです:

 
//------------------ medaka.html -------------------------------
<HTML>
<HEAD>
<TITLE>Medakatch</TITLE>
</HEAD>
<BODY>
 
<APPLET CODE="MedakaApplet.class" WIDTH=350 HEIGHT=250 >
</APPLET>
 
</BODY>
</HTML>
 
 

アプレットです:

 
//------------------- MedakaApplet.java  -----------------------
 
import java.awt.*;
import java.applet.*;
import java.util.*;
 
//****************  メダカクラス  *******************
 
class Medaka{
 
        private MedakaApplet applet = new MedakaApplet();
        protected String figure[];  //メダカの姿(文字列)を入れる配列
        private int OffsetFigure;       //メダカの一方向の姿の数の変数
        int x;                  //メダカが最初にいるx座標
        int y;                  //メダカが最初にいるy座標
        private int direction = 1;//メダカの泳ぐ方向
        protected int ownW;             //めだかの長さ
        protected int ownH;             //メダカの幅
        protected Color color;
        protected Dimension dimension;
        private AudioClip song, help; //メダカの鳴き声
        protected int initialWeight;  //最初の体重
        protected int weight;             //現在の体重
        private int lossWeight;           //減少体重
        private int eatFoodCount;     //食べた餌の数
        private int longevity;//寿命
 
        Medaka(int x, int y, Applet a){
                this.x = x;
                this.y = y;
                song = a.getAudioClip(a.getDocumentBase(),"medaka.au");
                help = a.getAudioClip(a.getDocumentBase(),"help.au");
                setFigure();
                getLifeSpan();  //寿命を算出
        }
 
        public void setFigure(){
                figure = new String[4]; //文字列を入れるための配列(4個入る)
                figure[0] = ">^)))><";
                figure[1] = "=^)))><";
                figure[2] = "><(((^<";
                figure[3] = "><(((^=";
                ownW = 40;
                ownH = 13;
                color = Color.blue;
                initialWeight = 30;
        }
 
//寿命は最初っから決まってる(¨;)、運がよくて幸せだと長生き (^_^)
        public int getLifeSpan(){
                int lifeSpan , happiness;
 
                lifeSpan = (int)(Math.random()*100000);
                happiness = (int)(Math.random()*10) + 1;
                longevity = lifeSpan * happiness;
                return longevity;
        }
 
    public Rectangle getRectangle(){
        return new Rectangle(x, y, ownW, ownH);
    }
 
        public void addMuck(){ //糞をする
                if(MedakaApplet.time%50 == 0)
                MedakaApplet.mucks.addElement( new Muck ( x + (ownW/2) , y ));
        }
 
        public int getWeight(){//体重を計算:餌は必ず食べる:1個5グラム
                weight = initialWeight +
                        ((MedakaApplet.FOOD_WEIGHT * eatFoodCount) - lossWeight);
                return weight;
        }
 
        public void lossWeight(){//エネルギー消費
                int loss = 1;
 
                if(MedakaApplet.time%20 == 0)lossWeight += loss;
        }
 
        public void directionOfMedaka(Dimension dimension){//泳ぐ方向
                this.dimension = dimension;
                int margin = 5;         //メダカが端まで行かないようマージン
 
                if(MedakaApplet.foods.size() > 0 ){//餌がある時の動く方向
                        Food food = (Food)MedakaApplet.foods.elementAt(0);
 
                        if(y < food.food_y){
                                if(x < food.food_x ) direction = 5;
                                if(x > food.food_x ) direction = 6;
                        }
                        if(y == food.food_y ){
                                if(x < food.food_x ) direction = 4;
                                if(x > food.food_x ) direction = 1;
                        }
                        if(y > food.food_y ){
                                if(x < food.food_x ) direction = 3;
                                if(x > food.food_x ) direction = 2;
                        }
                //餌に接すると食べる
                        if (getRectangle().intersects(food.getRectangle())){
                applet.removeFood();
                                eatFoodCount++; //食べた餌の数
               }
                }
                else{   //水槽内に餌が無い時泳ぐ方向
                        if(MedakaApplet.time % 20 == 0){//20回数えてモードを変える
                                direction = 1 + (int)(Math.random() * 10);
                        }
                        if(dimension.width - (x + ownW) < margin ){
                                direction = 1;  //右端に来ると左に進む
                        }
                        if(x < margin){
                                direction = 4;  //左端に来ると右に進む
                        }
                        if(y < margin + MedakaApplet.MARGIN_H + ownH){
                                //上端に来たら
                                if(x < dimension.width/2){//左寄りの時は右下へ
                                        direction = 5;
                                }
                                else{//右寄りの時は左下へ
                                        direction = 6;
                                }
                        }
                        if(y > dimension.height - MedakaApplet.MARGIN_H - margin - ownH){
                                //下端に来た時
                                if(x < dimension.width/2){//左寄りの時は右上へ
                                        direction = 3;
                                }
                                else{//右寄りの時は左上へ
                                        direction = 2;
                                }
                        }
                }
        }
                                                //  左上    2 ~\ /~ 3   右上
                                                //   左     1 - @ - 4    右
                                                //  左下    6 _/ \_ 5   右下
 
        public void medakaMove(){
                int move_x = 0;         //メダカのx座標移動数値
                int move_y = 0;         //メダカのy座標移動数値
                int LEFT=0;
                int RIGHT=2;
 
                switch(direction){
                        case 1:         //左向きに泳ぐ
                        OffsetFigure = LEFT;
                                move_x = -5;
                                move_y = 0;
                                break;
                        case 2:         //左上向きに泳ぐ
                        OffsetFigure = LEFT;
                                move_x = -5;
                                move_y = -5;
                                break;
                        case 3:         //右上に進む
                        OffsetFigure = RIGHT;
                                move_x = 5;
                                move_y = -5;
                                break;
                        case 4:         //右向きに泳ぐ
                        OffsetFigure = RIGHT;
                                move_x = 5;
                                move_y =0;
                                break;
                        case 5:         //右下に進む
                        OffsetFigure = RIGHT;
                                move_x = 5;
                                move_y = 5;
                                break;
                        case 6:         //左下に進む
                        OffsetFigure = LEFT;
                                move_x = -5;
                                move_y = 5;
                                break;
                        case 7:
                        OffsetFigure = LEFT;
                                move_x = -2;
                                move_y = 2;
                                break;
                        default:        //モード1〜7以外は右下へ進む
                        OffsetFigure = RIGHT;
                                move_x = 2;
                                move_y = 2;
                                OffsetFigure = RIGHT;
                                break;
                }
                        x = x + move_x;                 //メダカのx座標移動
                        y = y + move_y;                 //メダカのy座標移動
        }
//健康状態良くて、水槽もきれいだと「めだかの学校」を歌う
        public void singSong(){
                if( 65 > weight && weight > 55 ){
                        if(MedakaApplet.mucks.size() < 5){
                                song.play();
                        }
                }
        }
//痩せすぎたり、糞がたまると泣く
        public void singHelp(){
                if(10 > weight && weight > 6)help.play();
                if(30 < MedakaApplet.mucks.size() &&
                            MedakaApplet.mucks.size() < 35)help.play();
        }
 
        public void action(Dimension dimension){
                this.dimension = dimension;
 
                directionOfMedaka(dimension);
                medakaMove();
                addMuck();
                lossWeight();
                singSong();
                singHelp();
        }
 
        public void draw(Graphics g){
                g.setColor(color);
                g.drawString(figure[MedakaApplet.time%2+OffsetFigure],x,y);
        }
}
 
//****************  ブタメダカクラス  ***************
 
class PigFaceMedaka extends Medaka{
 
        public PigFaceMedaka(int x, int y, Applet a){
                super(x, y, a);
        }
 
        public void setFigure(){
                figure = new String[4]; //文字列を入れるための配列
                figure[0] = ">(oo) ))))><";     //(oo)ブタの鼻に見える?
                figure[1] = "=(oo) ))))><";
                figure[2] = "><(((( (oo)<";
                figure[3] = "><(((( (oo)=";
                ownW = 55;
                ownH = 13;
                color = Color.black;
                initialWeight = 80;
        }
}
 
//****************  永眠クラス  *************************
 
class DeadMedaka extends Medaka{
 
        private String figure;
 
        public DeadMedaka (int x, int y, Applet a){
                super(x, y, a);
        }
 
        public void setFigure(){
                figure = "@++++++<";    //黒田さんのを真似てみました
                color = Color.black;
                ownH = 4;
        }
 
        public int getWeight(){
                weight = 3;
                return weight;
        }
 
        public void medakaMove(Dimension dimension){
                this.dimension = dimension;
                int move_y = 3;
 
                if(dimension.height - (y + ownH) < MedakaApplet.MARGIN_H){
                        move_y = 0;
                }
                y += move_y;
        }
 
        public void action(Dimension dimension){
                medakaMove(dimension);
        }
 
        public void draw(Graphics g){
                g.setColor(color);
                g.drawString(figure, x, y);
        }
}
 
//***************  MedakaApplet.class  **********************
 
public class MedakaApplet extends Applet implements Runnable{
 
        private Dimension appletSize;   //Dimensionオブジェクトを作成
        private Image offScreen;                //ちらつき防止のためのImageオブジェクト
        private Graphics offGraphics;   //ちらつき防止のためのGraphicsオブジェクト
        private Label conditionLabel;   //メダカの状態を表示するためのラベル変数
        private Label weightLabel;              //体重表示
        private Label ageLabel;                 //年齢表示
        private Label medakaWTimeLabel; //メダカワールド時刻表示
        private Panel top, bottom;              //パネル2個作成するための変数
        private Button foodButton, cleanButton; //ボタン2個作成するための変数
        private Thread m_Thread;                //スレッド変数
        private Medaka medaka;
        private Plant plant;
        private VirtualTime medakaTime;
    final static int MARGIN_H = 30;     //縦のマージン(ラベル幅)
        final static int FOOD_WEIGHT = 5;//餌の重さ一個5グラム
        final int MILLI_SEC_PER_DAY = 1000*60*60*24;//1日のミリ秒
        static int time;
        static Vector mucks;
        static Vector foods;
 
        public void init() {
                setBackground(Color.gray);      //背景をグレイにする
                appletSize = size();            //アプレットの幅と高さを得る
                                                                        //描画用領域を生成
                offScreen=createImage(appletSize.width, appletSize.height);
                                                        //offScreenに描くためのGraphicsオブジェクト
                offGraphics=offScreen.getGraphics();
 
                top = new Panel();
            foodButton = new Button("FOOD ");   //餌ボタン
            cleanButton = new Button("CLEAN");  //掃除ボタン
                medakaWTimeLabel = new Label("Date :                                         ");
                top.setLayout(new FlowLayout(FlowLayout.CENTER));
                top.add(foodButton);            //ボタンをパネルに貼り付ける
                top.add(cleanButton);
                top.add(medakaWTimeLabel);
                bottom = new Panel();
                conditionLabel = new Label("          ( ^ ^ )         ");
                weightLabel = new Label("weight :          ");
                ageLabel = new Label("age :     ");
                bottom.setLayout(new FlowLayout(FlowLayout.CENTER));
                bottom.add(conditionLabel);                     //ラベルをパネルに貼り付ける
                bottom.add(weightLabel);
                bottom.add(ageLabel);
                setLayout(new BorderLayout());
                add("North", top);        //ボタンを貼り付けたパネルをアプレット上部に配置
                add("South", bottom); //ラベルを貼り付けたパネルをアプレット下部に配置
 
                mucks = new Vector();
                foods = new Vector();
                medaka = new Medaka(100,75, this);
                plant = new Plant(appletSize.width/3, appletSize, this);
                medakaTime = new VirtualTime(true, 1000);
        }
 
        public void start(){
                if (m_Thread == null){
                        m_Thread = new Thread(this);
                        m_Thread.start();       //スレッドを実行させる
                }
        }
 
        public void run(){      //スレッドの処理内容
                while (true){   //ずっと繰り返す
                        time++;
                        medaka.action(appletSize);
                        medakaTime.forward();
                        labelChange();
                        newLife();
                        terminate();
                        repaint();
                        pause();
                }
        }
//食べ過ぎるとブタメダカに変身、もう元の姿にはもどれない(-_-#)
        public void newLife(){
                if(medaka.getWeight() == 80){
                        medaka = new PigFaceMedaka(medaka.x, medaka.y ,this);
                }
        }
//死んじゃうよ〜(;;)
        public void terminate(){
                if(  (time == medaka.getLifeSpan())
                   ||(medaka.getWeight() == 3)
                   ||(mucks.size() > 50)){
                                medaka = new DeadMedaka(medaka.x, medaka.y, this);
                }
        }
 
        private void pause(){
                try{
                        Thread.sleep(200);
                }catch (InterruptedException e){}
        }
 
        public void stop(){
                if (m_Thread != null){
                        m_Thread.stop();        //スレッドを止める
                        m_Thread =null;
                }
        }
 
        public void update(Graphics g){
                paint(g);
        }
 
    private void flush(){//水槽の掃除をする時に使う
        offGraphics.setColor(Color.cyan);
                offGraphics.fillRect(0, 0, appletSize.width, appletSize.height);
   }
 
        public void paint(Graphics g){
                offGraphics.setColor(Color.cyan);
                offGraphics.fillRect(0, 0, appletSize.width, appletSize.height);
 
                        //描画用領域offScreenに対して描画を行う
                plant.draw(offGraphics);
                medaka.draw(offGraphics);
                        //餌
                for(int i = 0; i < foods.size(); i++){
                        getFood(i).foodMove(appletSize);
                }
        for(int i = 0; i < foods.size(); i++){
                        getFood(i).draw(offGraphics);
                }
                        //糞
                for(int i = 0; i < mucks.size(); i++){
                        getMuck(i).muckMove(appletSize);
                }
        for(int i = 0; i < mucks.size(); i++){
                        getMuck(i).draw(offGraphics);
                }
                        //画面へと転送する
                g.drawImage(offScreen, 0, 0, this);
        }
 
        public Muck getMuck(int number){
                        return(Muck)mucks.elementAt(number);
        }
 
        public Food getFood(int number){
                        return(Food)foods.elementAt(number);
        }
//メダカが餌を食べたらめだかから呼ばれて姿を消す
        public void removeFood(){
                        foods.removeElementAt(0);
        }
//ラベルの表示文字を書き換える
        private void labelChange(){
 
                weightLabel.setText("weight : " + medaka.getWeight() + "g");
                medakaWTimeLabel.setText("Date : " + medakaTime.toGMTString());
                ageLabel.setText("age : " + (medakaTime.getCurrentValue() /
                                                                                                        MILLI_SEC_PER_DAY));
                                                        //メダカ年齢はメダカ時間の1日に1歳づつふえる
                if(foods.size() > 5){
                        conditionLabel.setText("I'M FULL!");
                }
                if(mucks.size() > 30){
                        conditionLabel.setText("WATER IS DIRTY!");
                }
        }
 //ボタンのイベント処理
    public boolean action(Event v, Object o){
        if (v.target == foodButton){ //ボタンがfoodButtonであれば
            foods.addElement(new Food(appletSize));     //餌が落とされる
                        conditionLabel.setText("Yum-Yum");
                } else if (v.target == cleanButton){//水槽の掃除
            conditionLabel.setText("( ^_^ )");
                        foods.removeAllElements();
                        mucks.removeAllElements();
                        flush();
        }
        return true;
    }
}
 
//**************  水草クラス  ****************
 
class Plant{
 
        private Image plantIm[];        //水草(30*50)の絵を入れる変数
        private int plantH = 50;        //水草の高さ
        private int plant_x, plant_y;
        private Dimension dimension;
        private int time;
 
        public Plant(int x,Dimension dimension, Applet a){
                this.plant_x = x;
                this.dimension = dimension;
            plantIm = new Image[2];
                plantIm[0] = a.getImage(a.getDocumentBase(), "plant1.gif");
                plantIm[1] = a.getImage(a.getDocumentBase(), "plant2.gif");
                plant_y = dimension.height - plantH - MedakaApplet.MARGIN_H;
        }
 
        public void draw(Graphics g){
                time++;
                g.drawImage(plantIm[(time/4)%2], plant_x, plant_y, null);
        }
}
 
//**************  糞クラス  ******************
 
class Muck{
 
        private Dimension dimension;
        private int muck_x, muck_y;     //在る位置
        private int ownW = 10;
    private int ownH = 0;
        private String shape = "*";     //糞の形
        private Color color = Color.black;
        private int move_y = 3;
 
        public Muck(int x, int y){
                this.muck_x = x;
                this.muck_y = y;
        }
 
        public void muckMove(Dimension dimension){
                this.dimension = dimension;
                if(dimension.height - (muck_y + ownH) < MedakaApplet.MARGIN_H){
                        move_y = 0;
                }
                muck_y += move_y;
        }
 
        public void draw(Graphics g){
                g.setColor(color);
                g.drawString(shape, muck_x,muck_y);
        }
}
 
//**************   餌クラス  ****************
 
class Food{//Foodボタンを押すと中央から落とされる
 
        private Dimension dimension;
        int food_x, food_y;     //在る位置
        private int ownW = 10;
    private int ownH = 10;
        private Color color = Color.magenta;
        private int move_y = 1;
 
        public Food(Dimension dimension){
                this.dimension = dimension;
                food_x = dimension.width/2;
                food_y = MedakaApplet.MARGIN_H;
        }
 
        public Rectangle getRectangle(){
        return new Rectangle(food_x, food_y, ownW, ownH);
    }
 
        public void foodMove(Dimension dimension){
                this.dimension = dimension;
                if(dimension.height - (food_y + ownH) < MedakaApplet.MARGIN_H){
                        move_y = 0;
                }
                food_y += move_y;
        }
 
        public void draw(Graphics g){
                g.setColor(color);
                g.fillOval(food_x, food_y , ownW, ownH);
        }
}
----------------------------------------------
MedakaApplet.java と 三浦さんの時計クラス(4/30)にメソッド
 
    public long getCurrentValue() {
        return getTime() - startTime;
    }
 
を追加したものを使わせて頂いています。
 


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