最近在学习Java,看了First learning Java,以及Java从入门到精通,大致对于Java的语法规则有个大致的了解,可是对于写程序,还是far far from,怎么办了,上网看看别人的代码呗,于是就找了一个吃豆豆的程序,很是不错,注释多,而且代码写的也不是很晦涩,于是就有种冲动学习的冲动。
大部分的代码还是他的,只是我改了其中的一些东西而已,还有,我的程序需要后期做的还有很多,
我打算下一步做的就是,当大鱼碰到鱼池边缘的时候减分,或者让小鱼也可以进行移动,大家可以看看我的代码的运行结果,不怎样,但是可以跑
界面做的有点挫,我也懒得再调了,等着下一步一起来改动吧
下面看看我的代码
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.Random;
import javax.swing.*;
import javax.swing.border.LineBorder;
import javax.swing.border.MatteBorder;
import javax.swing.border.TitledBorder;
/*
* 这个程序修改自网上的吃豆豆的程序,很多都是借鉴这个程序
* 思想就是随机生成一个小豆豆的原点,以及一条小鱼,移动小鱼可以将豆吃掉,随之
* 边上会显示你的成绩以及吃豆的个数,在程序中用小鱼所包含的值来显示这个数量
* 每吃了N个豆的时候,会提高小鱼的移动速度
* 可以判断小鱼是否是移动到边界了
* 这里面有个难点就睡如何判定小鱼可以或者不可以吃到小豆
* 这个是通过数学中的Math.pow函数来实现的
*/
public class BigMouseFish extends JFrame
{
/**
* 游戏的主界面已经搭建出来了
*/
private static final long serialVersionUID = 1L;//Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。
/**
* @param args
* 首先来构建游戏中的主界面,所有的组件都是搭建在JPanel上面,显示的组件
* 基本上是用的都是Jlabel这个控件
*/
private FishPool pool = null;
//主窗口的大小
public static int width = 800;
public static int height = 600;
private JLabel label_infor = new JLabel("欢迎来到我的世界!");
private JLabel label_size = new JLabel("\t大 小 :");
private JLabel label_score = new JLabel("\t得 分 :");
private JLabel label_speed = new JLabel("\t速 度 :");
//默认设置,鱼的大小速度和得分
public static JLabel size = new JLabel("50");
public static JLabel score = new JLabel("0");
public static JLabel speed = new JLabel("5");
//可以多行文字显示框
private JTextArea textinfo = new JTextArea();
//构造函数
public BigMouseFish()
{
pool = new FishPool();
/*
* 使得主界面显示出阳浮雕的效果
*/
//pool.setBorder(new EtchedBorder(EtchedBorder.RAISED));
/*
* 使得主界面显示出阴浮雕的效果
*/
//pool.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
/*
* 使得主界面显示出绿色边框的特点
*/
pool.setBorder(new MatteBorder(5,5,30,30,Color.GREEN));
setTitle("bedlamite的第一个大鱼吃小鱼的游戏!!");
setSize(width+180,height+50);
/*
* 这个函数可以实现游戏框架的大小的变化,直接进行鼠标边缘拖动就可以
*/
setResizable(true);
/*
* 此类是所有 Abstract Window Toolkit 实际实现的抽象超类。Toolkit 的子类被用于将各种组件绑定到特定本机工具包实现。
*/
Toolkit tk = Toolkit.getDefaultToolkit();//拿到默认工具包
setLocation((tk.getScreenSize().width-getSize().width)/2,tk.getScreenSize().height-getSize().height/2);
/*
* 游戏的标题位置和大小
*/
label_infor.setSize(150,20);
label_infor.setLocation(width+25,240);
/*
* 游戏的开发者说明和使用说明书
*/
String info_devp = "大鱼吃小鱼的游戏,就是使用方向键上下左右来控制小鱼移动的方向" +
"每吃小鱼一条,得分加10,每吃十条小鱼,大鱼将打怪升级,大小加1,速度将加5.\n\n" +
"开发者:bedlamite\n" +
"时间:2014年7月\n" +
"地点:徐州\n";
textinfo.append(info_devp);
textinfo.setBackground(getBackground());
textinfo.setEditable(false);//文本框不可编辑
textinfo.setLineWrap(true);//文本显示可以自动适应文本框的大小
textinfo.setSize(150,240);
textinfo.setLocation(width+15,370);
textinfo.setBorder(new TitledBorder(new LineBorder(Color.BLUE),"游戏介绍!!"));
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.BLUE),"游戏积分!!"));
pan.add(label_size);
pan.add(size);
pan.add(label_speed);
pan.add(speed);
pan.add(label_score);
pan.add(score);
setLayout(null);
add(pool);
this.add(label_infor);
this.add(pan);
this.add(textinfo);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new BigMouseFish();
}
}
/*
* 游戏的主界面已经出来了,剩下的就是把大鱼和小鱼的类写出来了
* 下面这个类就是搭建大鱼的框架
*/
class BigFish implements ActionListener
{
/*
* 很多同学和我一样不知道ActionListener有啥用,其实我现在也不知道
* 百度了一下,oh,yeah,依然不知道
* ActionListener用于接收操作事件的侦听器接口。
* 对处理操作事件感兴趣的类可以实现此接口,
* 而使用该类创建的对象可使用组件的
* addActionListener 方法向该组件注册。
* 在发生操作事件时,调用该对象的 actionPerformed 方法。
*/
/*
* 大鱼的移动的方向,通过上下左右来移动
*/
public static int up = 0;
public static int right = 1;
public static int down = 2;
public static int left = 3;
/*
* 大鱼的大小,通过圆来绘制,半径为size
*/
public int size = 50;
//大鱼现在的方向,默认为朝向左
public int direction = left;
//大鱼的颜色,默认是黑色
private Color color = Color.BLACK;
//大鱼现在的位置,在X轴的坐标
public int posx = 80;
//大鱼现在的位置,在Y轴的坐标
public int posy = 80;
//大鱼现在的速度
public int speed = 5;
//大鱼眼睛的大小
private int eyesize = size/7;
//大鱼眼睛的位置,在X轴的坐标
private int eyesposx = posx + size/2;
//大鱼眼睛的位置,在Y轴的坐标
private int eyesposy = posy + size/4;
//大鱼眼睛的颜色,默认是红色
private Color eyecolor = Color.RED;
//大鱼的嘴可以最大张开的角度(正向)
private int maxmouse = 30;
//大鱼现在嘴的角度
private int mousesize = 30;
//大鱼现在的嘴是否张开
private boolean isopen = true;
/*
* 在应用开发中,经常需要一些周期性的操作,比如每5分钟执行某一操作等。
* 对于这样的操作最方便、高效的实现方式就是使用java.util.Timer工具类。
*/
private Timer time = null;
/*
* 大鱼的默认构造函数
*/
public BigFish()
{
this(80,80,50,left,Color.BLACK,5);
}
public BigFish(int posx, int posy, int size, int direction, Color color, int speed)
{
// TODO Auto-generated constructor stub
this.posx = posx;
this.posy = posy;
this.size = size;
//方向只有四个
if(direction == 0||direction == 1||direction == 2||direction == 3)
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;
}
}
public void change_direction(int direction)
{
this.direction = direction;
}
/*
* 开始绘制大鱼的身体
*/
public void paint_bigfish(Graphics g)
{
//Graphics这个是抽象类,它的对象是用来传给paint()方法作为画笔的
//画笔的颜色
Color c = g.getColor();
/*
* 绘制大鱼的轮廓
* 从默认的大鱼位置开始绘制大鱼
*/
g.fillArc(posx, posy, size, size, (direction%2==0?(direction+1):(direction-1))*90+mousesize, 360-2*mousesize);//没整明白
//绘制大鱼的眼睛
initeye();
g.setColor(eyecolor);
g.fillOval(eyesposx, eyesposy, eyesize, eyesize);
//恢复画笔的颜色
g.setColor(c);
}
/*
* (non-Javadoc)
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
* 用于接收操作事件的侦听器接口。对处理操作事件感兴趣的类可以实现此接口,
* 而使用该类创建的对象可使用组件的 addActionListener
* 方法向该组件注册。在发生操作事件时,调用该对象的 actionPerformed 方法。
* 这个ActionListener接口中只有一个方法
* 虽然这个方法貌似看起来在整个工程中没有被调用,可是在ActionListener这个接口中有,
* 必须被实现,实现了
*/
public void actionPerformed(ActionEvent e)
{
if(isopen)
{
mousesize -= 2;
if(mousesize<=0)
{
isopen = false;
}
}
else
{
mousesize += 2;
if(mousesize>=maxmouse)
{
isopen = true;
}
}
}
/*初始化鱼眼睛,当方向变化了,鱼的眼睛必然会变化
* 这个坐标轴为什么会这个样子变化大家自己拿笔慢慢的研究吧
*/
private void initeye()
{
switch (direction) {
case 0:
eyesposx = posx + size/7;
eyesposy = posy +size/2 - eyesize;
break;
case 1:
eyesposx = posx + size/2;
eyesposy = posy +size/7;
break;
case 2:
eyesposx = posx + size*5/7;
eyesposy = posy +size/2;
break;
case 3:
eyesposx = posx + size/2 - eyesize;
eyesposy = posy +size/7;
break;
default:
break;
}
}
/*
* 大鱼的类已经写完了,很显然下一步需要写小鱼的类,也就是豆豆的类
*/
}
class LittlerFish implements ActionListener
{
//小鱼的位置(X坐标)
public int posx = 200;
//小鱼的位置(Y坐标)
public int posy = 200;
//小鱼的大小
public int size = 20;
//小鱼的颜色
public Color color = Color.BLUE;
/*
* 如果超过一段时间大鱼还没有把小鱼吃掉的话,那么小鱼就会自动闪烁
* 固定时间然后消失,之后重新再新的位置上出现小鱼
*/
public static int flicktime = 300;
/*
* 小鱼每次闪烁的次数,默认是10次
*/
public static int flicknum = 10;
private int hasfilcknum = 0;
/*
* 小鱼的的闪烁定时器
*/
private Timer timer = null;
/*
* 小鱼的构造函数
*/
public LittlerFish()
{
this(200,200,20,Color.BLUE);
}
public LittlerFish(int posx, int posy, int size, Color color)
{
// TODO Auto-generated constructor stub
this.posx = posx;
this.posy = posy;
this.size = size;
this.color = color;
timer = new Timer(flicktime,this);
}
/*
* 小鱼的新位置
*/
public void newPos(int posx,int posy)
{
this.posx = posx;
this.posy = posy;
}
/*
*绘制小鱼的函数
*/
public void paint(Graphics g)
{
Color c = g.getColor();
g.setColor(color);
g.fillOval(posx, posy, size, size);
g.setColor(c);
}
/*
* 计时器停止函数
*/
public void stopTimer()
{
timer.stop();//计时器停止
hasfilcknum = 0;//计数器重置
}
/*
* 重新绘制小鱼之后,开始重启计时器
*/
public void runTimer()
{
timer.start();
}
/*
* (non-Javadoc)
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
hasfilcknum++;
if(hasfilcknum == flicknum && timer.isRunning())
{
stopTimer();
}
}
/*
* 判定定时器是否运行
*/
public boolean timerIsRunning()
{
return timer.isRunning();
}
}
/*
* 这个类需要做的就是判断大鱼到底抓到小鱼没有
* 通过判定这两个圆之间的距离来判定两个小鱼有没有相交或者相切
* 只要满足其中任何一个条件的话,就判定小鱼被大鱼吃掉
*/
class collision_detect
{
/*
* 小鱼是否被大鱼吃掉的判定条件是:
* 2个圆心之间的距离小于两个圆的半径和就可以
* 它的判定方法比较诡异,我捉摸了比较长的时间,数学功底有点恶心
* @param bigfish
* @param littlefish
* @return 小鱼是否被大鱼吃掉
*/
public static boolean isCollision(BigFish bigfish,LittlerFish littlerfish)
{
/*
* 下面注释的语句是看打印输出的,也就是判定碰撞时候的坐标值
System.out.println("the bean.posx is: "+bean.posx);
System.out.println("the bean.posy is: "+bean.posy);
System.out.println("the bean.size is: "+bean.size);
System.out.println("the fish.posx is: "+fish.posx);
System.out.println("the fish.posy is: "+fish.posy);
System.out.println("the fish.size is: "+fish.size);
System.out.println("the fish.size/2 is: "+fish.size/2);
System.out.println("fish.posx+fish.size/2-bean.posx-bean.size/2,2||"+Math.pow(fish.posx+fish.size/2-bean.posx-bean.size/2,2));
System.out.println("fish.posy+fish.size/2-bean.posy-bean.size/2,2||"+Math.pow(fish.posy+fish.size/2-bean.posy-bean.size/2,2));
System.out.println("fish.size/2,2||"+Math.pow(fish.size/2,2));
System.out.println("the result of judgment is: "+(Math.pow(fish.posx+fish.size/2-bean.posx-bean.size/2,2)
*/
return Math.pow(bigfish.posx+bigfish.size/2-littlerfish.posx-littlerfish.size/2, 2)
+Math.pow(bigfish.posy+bigfish.size/2-littlerfish.posy-littlerfish.size/2, 2)
<=Math.pow(bigfish.size/2, 2);
}
}
/*
* 大鱼和小鱼的绘制程序都写完了
* 两个鱼的碰撞程序也写完了
* 下面需要考虑的是,每次小鱼出现的位置
* 因为小鱼是随机出现的,所以需要写一个小鱼出现的随机函数类
*/
class random_occu
{
/*
* 这个我自己尝试了很多次随机函数,无论怎样,都会出现死角的情况
* 死角的情况就是小鱼出现在鱼池的边角上,大鱼的和它永远都没有交点
* 这样永远都不能被吃掉
*/
public static int random_number(int a,int b)
{
//这个函数我完全没搞明白为什么这么写
int t,n = 0;
if(a>b)
{
t = a;
a = b;
b = t;
}
t = (int)(Math.ceil(Math.log10(b)));
while(true)
{
n = (int)(Math.random()*Math.pow(10,t));
if(n>=a&&n<=b)
break;
}
return n;
}
public static int random_number(int a)
{
return new Random().nextInt();
}
}
/*
*
* 所有的零部件都已经写完了
* 剩下的就是把这个函数组件放到鱼池中
* 下面就是构造鱼池
*/
class FishPool extends JLabel
{
/**
*
*/
private static final long serialVersionUID = 1L;
private BigFish bigfish = null;
private LittlerFish littlerfish = null;
/*
* 控制messagebox中的输出的内容格式,文字的大小和字体
*/
Font font;
//小鱼存在的时间
private int timelength = 20*1000;
static int retime = 100;
//鱼池重回定时器
private Timer timer = null;
//小鱼生成的定时器
private Timer time = null;
private int sizeAdd = 5;
private int speedAdd = 2;
private int scoreAdd = 1;
//大鱼现在的分数、速度、吃鱼的数量
private int score = 0;
private int upgradeNumber = 10;
private int eatnumber = 0;
//大鱼规定的边界
private int max_x = 0;
private int max_y = 0;
private int min_x = 0;
private int min_y = 0;
public FishPool()
{
setSize(BigMouseFish.width,BigMouseFish.height);
setLocation(10,10);
bigfish = new BigFish();
initLittlerfish();
min_x = 3;
max_x = BigMouseFish.width-bigfish.size-3;
min_y = 3;
max_y = BigMouseFish.height-bigfish.size-3;
//注册一下控制键,也就是键盘上的上下左右键
//向上键
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP,0),"Up");
getActionMap().put("Up", new UplistenerImpl());
//向右键
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0),"Right");
getActionMap().put("Right",new RightlistenerImpl());
//向下键
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0), "Down");
getActionMap().put("Down", new DownlistenerImpl());
//向左键
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0), "Left");
getActionMap().put("Left", new LeftlistenerImpl());
timer = new Timer(retime, new TimerListennerImpl());
timer.start();
time = new Timer(timelength-littlerfish.flicktime*littlerfish.flicknum, new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
// TODO Auto-generated method stub
time.stop();
littlerfish.runTimer();
new Thread()
{
public void run()
{
while(true)
{
if(!littlerfish.timerIsRunning())
{
littlerfishNewPos();
timer.restart();
break;
}
}
}
}.start();
}
});
time.start();
}
//初始化小鱼
private void initLittlerfish()
{
int size = 20;
int posx = 0;
int posy = 0;
do{
posx = random_occu.random_number(BigMouseFish.width-size);
posy = random_occu.random_number(BigMouseFish.height-size);
}while(posx>=bigfish.posx && posx<=bigfish.posx+bigfish.size && posy>=bigfish.posy && posy<=bigfish.posy+bigfish.size &&
posx+size>=bigfish.posx && posx+size<=bigfish.posx+bigfish.size && posy+size>=bigfish.posy && posy+size<=bigfish.posy+bigfish.size);
littlerfish = new LittlerFish( posx, posy, size,Color.YELLOW);
}
/**
* 小鱼重新生成新位置。
* 保证小鱼生成的位置与大嘴鱼的位置不重叠。
*/
public int size = 15;
public int litterfish_posx = 0;
public int littlerfish_posy = 0;
private void littlerfishNewPos(){
do{
litterfish_posx = random_occu.random_number(2*size, BigMouseFish.width-2*size);
littlerfish_posy = random_occu.random_number(2*size,BigMouseFish.height-2*size);
}while (litterfish_posx>=bigfish.posx && litterfish_posx<=bigfish.posx+bigfish.size && littlerfish_posy>=bigfish.posy && littlerfish_posy<=bigfish.posy+bigfish.size &&
litterfish_posx+size>=bigfish.posx && litterfish_posx+size<=bigfish.posx+bigfish.size && littlerfish_posy+size>=bigfish.posy && littlerfish_posy+size<=bigfish.posy+bigfish.size);
littlerfish.newPos(litterfish_posx, littlerfish_posy);
/*System.out.println("the bean.posx is: "+bean_posx);
System.out.println("the bean.posx is: "+posy);*/
}
public void paint(Graphics g)
{
super.paint(g);
bigfish.paint_bigfish(g);
littlerfish.paint(g);
}
private void bigfishMove(int direction){
int i;
while(true)
{
for(i=0;i<bigfish.speed;i++){
bigfish.change_direction(direction);
//大嘴鱼到池边,不可在移动
switch(direction){
case 0:
if(bigfish.posy>=min_y+1)
if(istouched()) break;
break;
case 1:
if(bigfish.posx<=max_x-1)
if(istouched()) break;
break;
case 2:
if(bigfish.posy<=max_y-1)
if(istouched()) break;
break;
case 3:
if(bigfish.posx>=min_x+1)
if(istouched()) break;
break;
}
}
if(i==bigfish.speed)
break;
}
}
private boolean istouched()
{
font = new Font("宋体",0,22);
bigfish.move();
boolean b = collision_detect.isCollision(bigfish, littlerfish);
if(b)
{
//碰撞了,大鱼就需要升级
eatnumber++;
score += scoreAdd;
BigMouseFish.score.setText(score+"");
if(eatnumber==upgradeNumber)
{
eatnumber = 0;
bigfish.size += sizeAdd;
bigfish.speed += speedAdd;
BigMouseFish.size.setText(bigfish.size+"");
BigMouseFish.speed.setText(bigfish.speed+"");
//下面的这段代码是发现游戏bug后增加的代码
//bug:如果大嘴鱼在鱼池边界处恰好升级,将使鱼出界。
if(bigfish.posx==max_x || bigfish.posy==max_y){
bigfish.posx -= sizeAdd;
bigfish.posy -= sizeAdd;
}
}
//下面的这段代码是发现游戏bug后增加的代码。
//bug:如果大嘴鱼在小鱼闪烁时吃到小鱼。小鱼将触发新位置,而又将使小鱼继续闪烁。
//停止小鱼闪烁的定时器,更改颜色为原来的颜色。已经闪烁次数为0。
if(littlerfish.timerIsRunning()){
littlerfish.stopTimer();
}
littlerfishNewPos();
time.restart();
//加上的为了使得显示更加明显,无任何意义
font = new Font("宋体",0,42);
UIManager.put("Button.font",font);
UIManager.put("Label.font",font);
ImageIcon img = new ImageIcon("D:\\2.jpg");
JOptionPane.showMessageDialog( null , "Big Dark " ,"国栋-王" , JOptionPane.INFORMATION_MESSAGE,img) ;
//JOptionPane.showMessageDialog( null , "Big Dark " ,"国栋-王" , JOptionPane.INFORMATION_MESSAGE) ;
//JOptionPane.s
///
}
return b;
}
///
//向上的按钮事件
private class UplistenerImpl extends AbstractAction
{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
bigfishMove(0);
}
}
//向右按钮事件
private class RightlistenerImpl extends AbstractAction
{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
bigfishMove(1);
}
}
//向下的按钮事件
private class DownlistenerImpl extends AbstractAction
{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
bigfishMove(2);
}
}
//向左的按钮事件
private class LeftlistenerImpl extends AbstractAction
{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
bigfishMove(3);
}
}
///
private class TimerListennerImpl implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
repaint();
}
}
}
自认为代码的注释已经相当的详细了,但是中间有两个代码段没有看懂,没法跟大家分享我的注释了,如果有大神的话,可以帮我补充一下