第一步:大嘴鱼类

package fish;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
/**
 *
 *@author huyongjian Oracle(Compus Solution Group)
 * @Date  2013-7-24
 * @version 2.0
 * @since JDK1.6(建议)
   Copy Right Information    COMPUS SOLUTION GROUP
   IDE:Eclipse
   class:
    * 大嘴鱼类
* 此类继承AcitonListener,实现AcitonPerformed方法,练习ActionListener的另一种实现方法
* 此类还练习定时器Timer的使用
* 此类还练习图形类Graphics的使用
*/ public class BigMouthFish implements ActionListener{ /** 大嘴鱼嘴的方向:0-上 */ public static int UP = 0; /** 大嘴鱼嘴的方向:1-右 */ public static int RIGHT = 1; /** 大嘴鱼嘴的方向:2-下 */ public static int DOWN = 2; /** 大嘴鱼嘴的方向:3-左 */ public static int LEFT = 3; /** 大嘴鱼的身体大小,以size为半径画圆 */ public int size = 50; /** 大嘴鱼现在的方向(以嘴的方向为基准) */ public int direction = RIGHT; /** 大嘴鱼身体的颜色 */ private Color color = Color.CYAN; /** 大嘴鱼的位置x */ public int posx = 80; /** 大嘴鱼的位置y */ public int posy = 80; /** 大嘴鱼的速度,鱼每次前进的像素 */ public int speed = 4; /** 大嘴鱼眼睛的大小 */ private int eyesize = size/5; /** 大嘴鱼眼睛的位置x */ private int eyeposx = posx+size/2; /** 大嘴鱼眼睛的位置y */ private int eyeposy = posy+size/5; /** 大嘴鱼眼睛的颜色 */ private Color eyecolor = Color.RED; /** 大嘴鱼嘴的最大角度的一半 */ private int maxMonth = 30; /** 大嘴鱼现在嘴角的角度 */ private int monthsize = 30; /** 大嘴鱼的嘴是否张开 */ private boolean isOpen = true; private Timer time = null; /** * 大嘴鱼缺省构造函数.
* 创建一个位置为(200,200),大小为50,方向为右,颜色为Color.CYAN,速度为10的大嘴鱼 */ public BigMouthFish() { //this的使用。 this(200,200,50,RIGHT,Color.CYAN,4); } /** * 根据位置、大小、方向、颜色、速度构造一个大嘴鱼。 * @param posx 大嘴鱼的位置x。 * @param posy 大嘴鱼的位置y。 * @param size 大嘴鱼的大小。 * @param direction 大嘴鱼的方向:0-上;1-右;2-下;3-左。出入错误时,默认改为1。 * @param color 大嘴鱼的颜色。 * @param speed 大嘴鱼的速度。 */ public BigMouthFish(int posx,int posy,int size,int direction,Color color,int speed) { this.posx = posx; this.posy = posy; this.size = size; if(direction==1 || direction==2 || direction==3 || direction==4) this.direction = direction; this.color = color; this.speed = speed; eyesize = size/7; initEye(); time = new Timer(FishPool.reTime,this); time.start(); } /** * 大嘴鱼移动。根据鱼的方向移动鱼。 */ public void move(){ switch (direction) { case 0: posy--; break; case 1: posx++; break; case 2: posy++; break; case 3: posx--; break; default: break; } } /** * 改变大嘴鱼身体的颜色。 * @param color 欲改变大嘴鱼身体的颜色 */ public void changeColor(Color color){ this.color = color; } /** * 改变大嘴鱼的方向 * @param direction 欲改变大嘴鱼的方向 */ public void changeDir(int direction){ this.direction = direction; } /** * 使用画笔绘画大嘴鱼.大嘴鱼的头像:一个扇形的是鱼脸,上面有一个小圆是眼睛
* 1.保存画笔颜色
* 2.绘制大嘴鱼的脸
* 3.绘制大嘴鱼的眼睛
* 4.恢复画笔颜色
* @param g 画笔 */ public void paint(Graphics g){ //保存画笔的颜色 Color c = g.getColor(); //绘制鱼脸 g.setColor(color); //从(posx,posy)点开始,绘制宽为size,高为size,开始角度为(direction%2==0?(direction+1):(direction-1))*90+monthsize,弧度为360-2*maxMonth的弧形 g.fillArc(posx, posy, size, size, (direction%2==0?(direction+1):(direction-1))*90+monthsize, 360-2*monthsize); //绘制鱼眼 initEye(); g.setColor(eyecolor); g.fillOval(eyeposx, eyeposy, eyesize, eyesize); //恢复画笔颜色 g.setColor(c); } /** * 大嘴鱼张嘴闭嘴事件
* 此处只负责鱼的角度的变化,不负责鱼的重绘。 * 重绘在鱼池中实现。
* 这样的好处:保证鱼的绘制和步伐分开。显示层和逻辑层单独处理。(面向对象的责任问题)。 * @param e 事件对象 */ public void actionPerformed(ActionEvent e) { if(isOpen){ monthsize -= 2; if(monthsize<=0) isOpen = false; }else{ monthsize += 2; if(monthsize>=maxMonth) isOpen = true; } } /** * 方向改变时,眼睛的改变。 */ private void initEye(){ switch (direction) { case 0: eyeposx = posx+size/7; eyeposy = posy+size/2-eyesize; break; case 1: eyeposx = posx+size/2; eyeposy = posy+size/7; break; case 2: eyeposx = posx+size*5/7; eyeposy = posy+size/2; break; case 3: eyeposx = posx+size/2-eyesize; eyeposy = posy+size/7; break; default: break; } } }


第二步:豆豆类

package fish;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
/**
 *
 *@author huyongjian Oracle(Compus Solution Group)
 * @Date  2013-7-24
 * @version 2.0
 * @since JDK1.6(建议)
   Copy Right Information    COMPUS SOLUTION GROUP
   IDE:Eclipse
   class:
   豆豆类
 */
public class FishBean implements ActionListener{
    /** 小鱼的位置x */
    public int posx = 190;
    /** 小鱼的位置y */
    public int posy = 190;
    /** 小鱼的大小,以size为半径画豆豆(圆)。 */
    public int size = 10;
    /** 小鱼的当前颜色 */
    private Color color = Color.MAGENTA;
                                 
    /** 小鱼的新颜色 */
    private Color newColor = Color.MAGENTA;
    /** 小鱼的旧颜色 */
    private Color oldColor = Color.GRAY;
                                 
    /** 小鱼消失时的闪烁时间 */
    public static int flickerTime = 350;
    /** 小鱼消失时的闪烁次数 */
    public static int filckerNum = 8;
    private int hasFileckedNum = 0;
    /** 小鱼消失时闪烁的定时器 */
    private Timer timer = null;
                                 
    /**
     * 缺省构造函数。按位置(40,40),大小15,颜色Color.MAGENTA构造一个豆豆。
     */
    public FishBean() {
        this(190,190,10,Color.MAGENTA,Color.GRAY);
    }
                                 
    /**
     * 按照位置、大小、颜色构造一个豆豆类。
     * @param posx 豆豆的水平坐标x。
     * @param posy 豆豆的垂直坐标y。
     * @param size 豆豆的大小。
     * @param newColor 豆豆的颜色。
     * @param oldColor 豆豆的旧颜色
     */
    public FishBean(int posx,int posy,int size,Color newColor,Color oldColor) {
        this.posx = posx;
        this.posy = posy;
        this.size = size;
        this.newColor = newColor;
        this.oldColor = oldColor;
        this.color = newColor;
        timer = new Timer(flickerTime,this);
    }
                                 
    /**
     * 移动豆豆到新的位置。
     * @param posx 豆豆的新水平坐标x。
     * @param posy 豆豆的新垂直坐标y。
     */
    public void newPos(int posx,int posy){
        this.posx = posx;
        this.posy = posy;
    }
                                 
    /**
     * 绘制豆豆。
     * @param g 画笔
     */
    public void paint(Graphics g){
        Color c = g.getColor();
        g.setColor(color);
        g.fillOval(posx, posy, size, size);
        g.setColor(c);
    }
                                 
    /**
     * 停止Timer。
     */
    public void stopTimer(){
        color = newColor;
        timer.stop();
        hasFileckedNum = 0;
    }
                                 
    /**
     * 启动Timer。
     */
    public void runTimer(){
        timer.start();
    }
                                 
    /**
     * 定时器事件。
     */
    public void actionPerformed(ActionEvent e) {
        hasFileckedNum++;
        if(color.equals(newColor) || color==newColor)
            color = oldColor;
        else
            color = newColor;
        //定时器运行的次数完成时,停止定时器。
        if(hasFileckedNum==filckerNum && timer.isRunning()){
            stopTimer();
        }
    }
                                 
    /**
     * Timer定时器是否运行中。
     * @return 返回Timer是否运行中。
     */
    public boolean timerIsRunning(){
        return timer.isRunning();
    }
}


第三部:鱼池类

package fish;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JLabel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import main.BigMouthFishFrame;
import tools.FishUtilities;
import tools.RandomUtil;
/**
 *
 *@author huyongjian Oracle(Compus Solution Group)
 * @Date  2013-7-24
 * @version 2.0
 * @since JDK1.6(建议)
   Copy Right Information    COMPUS SOLUTION GROUP
   IDE:Eclipse
   class:
    * 鱼池类。
* 大鱼和小鱼放到此鱼池中,当大嘴鱼每吃10条小鱼的时候,鱼的size加1,速度加2。
* 每条小鱼默认存在的时间是15秒钟。10秒后自动消失。
* 此类练习if、for、Timer、内部类等的使用。
* 更重要的是,希望学员做这个的游戏时候能做好严谨的逻辑考虑后,再动手。
* 难点:大嘴鱼在鱼池边界的处理、大嘴鱼升级时的相关问题的处理。
*/ public class FishPool extends JLabel{ private BigMouthFish fish = null; private FishBean bean = null; /** 小鱼存在的时间长短,单位:毫秒 */ private int timeLegth = 20*1000; /** 重绘时间长短,单位:毫秒 */ public static int reTime = 100; /** 鱼池重绘定时器 */ private Timer timer = null; /** 小鱼生成定时器 */ private Timer time = null; /** 大嘴鱼每升级1次属性增加:大小加1 */ private int sizeAdd = 1; /** 大嘴鱼每升级1次属性增加:速度加2 */ private int speedAdd = 2; /** 大嘴鱼每吃1条小鱼属性增加:分数加1 */ private int scoreAdd = 1; /** 大嘴鱼现在的分数 */ private int score = 0; /** 大嘴鱼升级的速度:每吃10条小鱼升级1次 */ private int upgradeNum = 10; /** 大嘴鱼已吃小鱼的数量,每吃upgradeNum个,变为0 */ private int eatNum = 0; /** 大嘴鱼的水平最小边界 */ private int min_x = 0; /** 大嘴鱼的水平最大边界 */ private int max_x = 0; /** 大嘴鱼的垂直最小边界 */ private int min_y = 0; /** 大嘴鱼的垂直最大边界 */ private int max_y = 0; public FishPool() { setSize(BigMouthFishFrame.width,BigMouthFishFrame.height); setLocation(10, 10); fish = new BigMouthFish(); initFishBean(); //此处加3、减3是为了大嘴鱼不直接接触鱼池边界。 min_x = 3; max_x = BigMouthFishFrame.width-fish.size-3; min_y = 3; max_y = BigMouthFishFrame.height-fish.size-3; //注册向上键 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP,0),"UpPress"); getActionMap().put("UpPress", new UpListenerImpl()); //注册向右键 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0),"RightPress"); getActionMap().put("RightPress", new RightListenerImpl()); //注册向下建 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0),"DownPress"); getActionMap().put("DownPress", new DownListenerImpl()); //注册向左键 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0),"LeftPress"); getActionMap().put("LeftPress", new LeftListenerImpl()); timer = new Timer(reTime,new TimerListenerImpl()); timer.start(); time = new Timer(timeLegth-FishBean.flickerTime*FishBean.filckerNum,new ActionListener(){ public void actionPerformed(ActionEvent e) { //先停止本身定时器。 time.stop(); //启动小鱼的闪烁定时器。 bean.runTimer(); //检测小鱼是否已经闪烁完毕。 new Thread(){ public void run() { while (true) { //小鱼闪烁完毕后,移动大嘴鱼。 //重新启动本身定时器。 if(!bean.timerIsRunning()){ beanNewPos(); time.restart(); break; } } } }.start(); } }); time.start(); } /** * 初始化小鱼。
* 保证小鱼生成的位置与大嘴鱼的位置不重叠。 */ private void initFishBean(){ int size = 15; int posx = 0; int posy = 0; do{ posx = RandomUtil.randomInt(BigMouthFishFrame.width-size); posy = RandomUtil.randomInt(BigMouthFishFrame.height-size); //System.out.println("posx:"+posx); //System.out.println("posy:"+posy); }while (posx>=fish.posx && posx<=fish.posx+fish.size && posy>=fish.posy && posy<=fish.posy+fish.size && posx+size>=fish.posx && posx+size<=fish.posx+fish.size && posy+size>=fish.posy && posy+size<=fish.posy+fish.size); bean = new FishBean(posx,posy,size,Color.BLUE,getBackground()); } /** * 小鱼重新生成新位置。
* 保证小鱼生成的位置与大嘴鱼的位置不重叠。 */ private void beanNewPos(){ int size = 15; int posx = 0; int posy = 0; do{ posx = RandomUtil.randomInt(2*size, BigMouthFishFrame.width-2*size); posy = RandomUtil.randomInt(2*size,BigMouthFishFrame.height-2*size); }while (posx>=fish.posx && posx<=fish.posx+fish.size && posy>=fish.posy && posy<=fish.posy+fish.size && posx+size>=fish.posx && posx+size<=fish.posx+fish.size && posy+size>=fish.posy && posy+size<=fish.posy+fish.size); bean.newPos(posx, posy); } /** * 覆盖JLabel的paint事件。 * @param g 画笔。 */ @Override public void paint(Graphics g) { super.paint(g); fish.paint(g); bean.paint(g); } /** * 大嘴鱼的移动。
* 根据大嘴鱼嘴的方向,判断其是否到了鱼池边界。
* @param direction 大嘴鱼移动的方向 */ private void bigFishMove(int direction){ MOVE: for(int i=0;i=min_y+1) if(isTouched()) break MOVE; break; case 1: if(fish.posx<=max_x-1) if(isTouched()) break MOVE; break; case 2: if(fish.posy<=max_y-1) if(isTouched()) break MOVE; break; case 3: if(fish.posx>=min_x+1) if(isTouched()) break MOVE; break; } } } /** * 检测大嘴鱼是否吃到了小鱼。 * @return 吃到小鱼就重新生成小鱼。 */ private boolean isTouched(){ fish.move(); boolean b = FishUtilities.isInteraction(fish, bean); if(b){ //鱼的升级 eatNum++; score += scoreAdd; BigMouthFishFrame.lblScore.setText(score+""); if(eatNum==upgradeNum){ eatNum = 0; fish.size += sizeAdd; fish.speed += speedAdd; BigMouthFishFrame.lblSize.setText(fish.size+""); BigMouthFishFrame.lblSpeed.setText(fish.speed+""); //下面的这段代码是发现游戏bug后增加的代码 //bug:如果大嘴鱼在鱼池边界处恰好升级,将使鱼出界。 if(fish.posx==max_x || fish.posy==max_y){ fish.posx -= sizeAdd; fish.posy -= sizeAdd; } } //下面的这段代码是发现游戏bug后增加的代码。 //bug:如果大嘴鱼在小鱼闪烁时吃到小鱼。小鱼将触发新位置,而又将使小鱼继续闪烁。 //停止小鱼闪烁的定时器,更改颜色为原来的颜色。已经闪烁次数为0。 if(bean.timerIsRunning()){ bean.stopTimer(); } beanNewPos(); time.restart(); } return b; } /** * 内部类 - 向上按键事件。 * 2013-7-24 * @author Oracle * @version 2.0 * @since JDK1.6(建议) * @author 小夜的传说 */ private class UpListenerImpl extends AbstractAction{ public void actionPerformed(ActionEvent e) { bigFishMove(0); } } /** * 内部类 - 向右按键事件。 * 2013-7-24 * @author Oracle * @version 2.0 * @since JDK1.6(建议) * @author 小夜的传说 */ private class RightListenerImpl extends AbstractAction{ public void actionPerformed(ActionEvent e) { bigFishMove(1); } } /** * 内部类 - 向下按键事件。 * 2013-7-24 * @author Oracle * @version 2.0 * @since JDK1.6(建议) * @author 小夜的传说 */ private class DownListenerImpl extends AbstractAction{ public void actionPerformed(ActionEvent e) { bigFishMove(2); } } /** * 内部类 - 向左按键事件。 * 2013-7-24 * @author Oracle * @version 2.0 * @since JDK1.6(建议) * @author 小夜的传说 */ private class LeftListenerImpl extends AbstractAction{ public void actionPerformed(ActionEvent e) { bigFishMove(3); } } /** * 内部类 - 图形重回事件.
* 2013-7-24 * @author Oracle * @version 2.0 * @since JDK1.6(建议) * @author 小夜的传说 */ private class TimerListenerImpl implements ActionListener{ public void actionPerformed(ActionEvent e) { repaint(); } } }


第四步:随机生成类

package tools;
import java.util.Random;
/**
 *
 *@author huyongjian Oracle(Compus Solution Group)
 * @Date  2013-7-24
 * @version 2.0
 * @since JDK1.6(建议)
   Copy Right Information    COMPUS SOLUTION GROUP
   IDE:Eclipse
   class:
    * 数学类-随机数生成类
 */
public class RandomUtil {
    /**
     * 生成a-b的随机数
     * @param a 整数a
     * @param b 整数b
     * @return a-b的随机数
     */
    public static int randomInt(int a,int b){
        int t,n=0;
        if(a>b)
        {
            t=a;a=b;b=t;
        }
        t=(int)(Math.ceil(Math.log10(b)));//对b向上取整
        while(true)
        {
            n=(int)(Math.random()*Math.pow(10,t));//随机数*10的t次方
            if(n>=a && n<=b)
                break;
        }
        //System.out.println("生成的随机数如下:"+n);
        return n;
    }
                           
    /**
     * 返回0-a的随机数。
     * @param a 整数a。
     * @return 返回0-a的随机数。
     */
    public static int randomInt(int a){
        return new Random().nextInt(a);
    }
}


第五步:检测大嘴怪吃豆豆类

package tools;
import fish.BigMouthFish;
import fish.FishBean;
/**
 *
 *@author huyongjian Oracle(Compus Solution Group)
 * @Date  2013-7-24
 * @version 2.0
 * @since JDK1.6(建议)
   Copy Right Information    COMPUS SOLUTION GROUP
   IDE:Eclipse
   class:
 * 检测大嘴鱼和小鱼碰撞类。
* 此类练习Math的静态函数的使用。 */ public class FishUtilities { /** * 返回大嘴鱼是否吃掉了小鱼。
* 方式:检测2鱼(圆)之间是否相切或相交。
* 判断2圆心之间的距离小于2圆半径之和。此处判断小于大嘴鱼的半径即可。让大嘴鱼可以吃掉小鱼。 * @param fish * @param bean * @return 返回大鱼是否吃掉小鱼。 */ public static boolean isInteraction(BigMouthFish fish,FishBean bean){ return Math.pow(fish.posx+fish.size/2-bean.posx-bean.size/2,2)+Math.pow(fish.posy+fish.size/2-bean.posy-bean.size/2, 2)<=Math.pow(fish.size/2, 2); } }

第六步:面板函数

package main;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.border.EtchedBorder;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
import fish.FishPool;
/**
 *
 *@author huyongjian Oracle(Compus Solution Group)
 * @Date  2013-7-24
 * @version 2.0
 * @since JDK1.6(建议)
   Copy Right Information    COMPUS SOLUTION GROUP
   IDE:Eclipse
   class:
   大嘴鱼洁面类。完成大嘴鱼的界面的绘制。
 */
public class BigMouthFishFrame extends JFrame{
    private FishPool pool = null;
                  
    public static int width = 800;
    public static int height = 600;
                  
    private JLabel lblInfo = new JLabel("欢迎进入大嘴鱼!V2.0");
                  
    private JLabel lblFishSize = new JLabel("鱼 的 大 小:");
    private JLabel lblFishSpeed = new JLabel("鱼 的 速 度:");
    private JLabel lblFishScore = new JLabel("现 在 得  分:");
                  
    public static JLabel lblSize = new JLabel("50");
    public static JLabel lblSpeed = new JLabel("4");
    public static JLabel lblScore = new JLabel("0");
                  
    private JTextArea txtInfo = new JTextArea();
                  
                  
    public BigMouthFishFrame() {
        pool = new FishPool();
        pool.setBorder(new EtchedBorder(EtchedBorder.RAISED));
                      
        setTitle("大嘴鱼游戏");
        setSize(width+180,height+50);
        setResizable(false);
        Toolkit tk=Toolkit.getDefaultToolkit();
        setLocation((tk.getScreenSize().width-getSize().width)/2,(tk.getScreenSize().height-getSize().height)/2);
                      
        lblInfo.setSize(150,20);
        lblInfo.setLocation(width+25,240);
                      
        String str = "    大嘴鱼游戏的简单使用说明:使用键盘上的上、下、左、右控制大嘴鱼的方向,每吃1条小鱼,得分加1分。每吃10条小鱼,大嘴鱼将升级:大小加1,速度加2。\n\n";
        str += "研发部总监:小胡\n分 析 设 计 :小夜的传说\n程 序 编 写 :小夜的传说";
                      
        txtInfo.append(str);
        txtInfo.setBackground(getBackground());
        txtInfo.setEditable(false);
        txtInfo.setLineWrap(true);
        txtInfo.setSize(150,240);
        txtInfo.setLocation(width+15,370);
        txtInfo.setBorder(new TitledBorder(new LineBorder(Color.GRAY),"游戏说明"));
                      
                      
        JPanel pan = new JPanel();
        pan.setSize(150,100);
        pan.setLocation(width+15,265);
        pan.setLayout(new FlowLayout(FlowLayout.CENTER,5,5));
        pan.setBorder(new TitledBorder(new LineBorder(Color.GRAY),"游戏积分"));
        pan.add(lblFishSize);
        pan.add(lblSize);
        pan.add(lblFishSpeed);
        pan.add(lblSpeed);
        pan.add(lblFishScore);
        pan.add(lblScore);
                      
                      
        setLayout(null);
        add(pool);
        add(lblInfo);
        add(pan);
        add(txtInfo);
                      
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        new BigMouthFishFrame();
    }
}


Java程序猿联盟:160243674 欢迎大家的加入!

Java编写大嘴怪游戏--原创_第1张图片