捕鱼达人游戏采用 Java 高级程序语言设计。本次游戏的程序设计包含java swing组件的合理运用,还有图像的变动达到一个动态的动画效果,线程的运用,游戏的异常处理,还有ArrayList集合类的使用等方面的知识。
捕鱼达人游戏,是以深海为游戏场景,各种鱼儿在其中游动的姿态自然,炮弹打击粒子效果华丽,打击感强烈,很具有趣味性。需要有不同种类的鱼,让鱼游动 起来,被炮击中时有特效(抽搐),并获得金币; 炮台可以根据鼠标的位置调整方向; 炮台发射时消耗金币,可以切换背景,增加趣味性等功能。
主要依靠如 FishPane1.java,Fish.java,FishNet.java,等一些类和框架实现相关功能。
目录
第一章:需求分析 1
1.1用户需求 1
第二章:设计思路 2
2.1 工作原理 2
2.2 功能规划 2
2.2.1系统分析 2
2.2.2 游戏的模块图 3
第三章:详细设计 4
3.1 数据分析 4
3.2 算法思路 4
3.2.1 Fish类的核心算法 4
3.2.2 FishNet类的核心算法 5
3.2.3 FishPanel类的核心算法 6
3.3类设计 7
3.3.1游戏主界面的设计 7
3.3.2游戏鱼池的设计 8
3.3.3游戏中鱼的设计 9
3.3.4游戏渔网的设计 10
3.4功能实现 11
3.4.1程序流程图 11
3.4.2 主要代码及注释 12
3.5界面 22
第四章:运行调试与分析讨论 23
4.1 运行调试 23
4.1.1发射炮弹没捕到鱼 23
4.1.2发射炮弹捕到鱼 23
4.2分析运行结果 24
第五章:设计体会与小结 25
5.1 设计体会与小结 25
参考文献 26
第一章:需求分析
进行软件项目设计,首先必须准确了解并分析用户需求(包括数据和处理) ,是捕鱼达人游戏设计的重要基础和依据。
1.1用户需求
捕鱼达人这个项目是一个娱乐性的游戏开发,该游戏可以给人们带来娱乐的同时还可以给人感官上的享受,所以很受人们的欢迎。本次游戏的程序设计包含java swing组件的合理运用,还有图像的变动达到一个动态的动画效果,线程的运用,游戏的异常处理,还有ArrayList集合类的使用等方面的知识。
所开发的游戏应包含以下功能:
(1) 需要有不同种类的鱼,鱼池中有很多鱼,鱼各自游动;
(2) 炮台发射时消耗子弹数
(3) 鼠标在屏幕上点击时根据鼠标的方向发射炮弹;
(4) 让鱼游动起来,被炮击中时有特效(抽搐),被抓捕到的时候能获得金币(并且是根据鱼被抓捕到时的所处的切片状态来判定获得的金币数,切片状态越靠后,所获得的金币将以几何级倍数增加,也就是*2、*4、*6、*8);
(5) 炮台可以根据鼠标的位置调整方向;
(6) 炮台发射时消耗金币;
(7) 子弹消失后撒网,等2秒之后让网消失
(8) 根据实际开发情况适当修改和优化程序。
第二章:设计思路
2.1 工作原理
1.初始化界面需要800*500大小,需要一个游戏界面,游戏框上显示为“捕鱼达人”。
2.在程序中为一个对象的线程,便于对鱼的完美操作与控制,游戏中我们定义了从大到小总共11种不同种类的雨,每条鱼都有十种不同的状态,通过切换状态来实现雨在动的效果,同一时刻鱼的数量可以通过更改线程的停滞时间来控制。
3.鱼的每次移动,它的水平坐标改变了,所以我们需要将它移动之后的状态重画(repaint()方法)出来,从而达到鱼是在移动的。
4.捕鱼的效果,鱼就需要有一个血量值(大鱼的血量高,小鱼血量低),当网在鱼的范围之内的时候,点击鼠标左键让鱼扣除相应的血量值,当鱼的血量小于0的时候,鱼就会弹跳几下然后消失,达到捕鱼成功。
5.每个游戏都是需要平衡的,所以我们不能一直捕鱼下去。子弹数,计分,等数据来平衡游戏。当子弹数为0 的时候就结束游戏,而捕一条鱼,加上相应鱼的分数,不同的鱼分数一样,分值是靠捕鱼时捕到的10个切换状态来判定的,鱼被捕到的状态越靠后,分数就越高。最后判定输赢就是看分数的高低。
2.2 功能规划
2.2.1系统分析
实现所需要的功能需要在程序中定义相关的类,完成相关功能的设计。
根据游戏中主要功能实现应包括: MainFrame.java(主文件类),FishPane.java (鱼池鱼塘类),Fish.java(鱼类),FishThread.java(鱼游动的线程实现类),Bullet.Java(子弹类),BulletThread.java(子弹移动的线程实现类) ,FishNet.java(渔网类),LocationUtil.java(窗口拖拽类),Demo.java(初始化类)。
图1为本程序各主要的类。
图1程序中主要的类
2.2.2 游戏的模块图
分别有:鱼的模块,渔网的模块,鱼池的模块,游戏的主模块,每一个模块相当于 一个类
鱼模块:主要实现鱼图片的加载,鱼游动效果,、还有鱼的一些基本属性,比如鱼的横纵坐标,存鱼图片的数组,还有血量值,移动路线的存放,移动速度,三个静态变量来存放鱼是从哪个方向来的。一些基本方法,鱼的构造方法,画鱼的方法,鱼移动的方法。
渔网模块:主要实现渔网图片的加载,渔网的属性有横纵坐标,鱼被渔网的抓捕次数,用来暂存鱼的ArrayList集合。渔网的构造方法,根据鱼和渔网坐标位置来判定鱼是否被抓的抓鱼方法(核心方法)。
鱼池模块(核心模块):鱼池的构造方法初始化,将鱼类的对象,网类的对象都在本类中进行调用,并将相应对象的图片显示出来,画背景图、画炮筒、画能量条、分数、子弹个数显示,并将游戏的背景画出来(重写继承的JPanel类中的paint()方法)。实现Runnable接口重写run()方法实现多线程来判定游戏是否结束。实现MouseListener、MouseMotionListener鼠标监听器重写监听器及里内的处理机制。。
游戏主模块:把运行游戏的载体窗口建立起来。
图2系统模块图
第三章:详细设计
3.1 数据分析
用合理的数据模型描述对象。
本游戏用到的对象有:窗口、鱼池、多条鱼、子弹、渔网。
每张图片使用时其横纵坐标为其图片的左上角。横坐标为自左向右,纵坐标为自上而下。
鱼的运动可以用坐标来实现,鱼的动态变化可以用短时间切换不同状态的鱼图片来实现。鱼有横纵坐标、宽和高、存鱼图片的数组,还有血量值,移动路线,移动速度,存放鱼方向的静态变量。
子弹有其横纵坐标,图组,子弹移动的角度,以及各种从其他类里面传过来的用来调用的变量:鱼是否存活变量,鱼池变量等。
渔网有其横纵坐标,鱼被渔网的抓捕次数,用来暂存鱼的ArrayList集合,用来调用的鱼池变量,鱼是否存活变量等。
鱼池设定了总的分数,剩余子弹数,窗口的高宽,炮筒图片横纵坐标的设定,炮筒角度,用来暂存的鼠标横纵坐标,以及与是否存活变量。
3.2 算法思路
3.2.1 Fish类的核心算法
Fish类的核心算法是move();
//如果没被捕
if(!isCatch){
switch(road){
case 1:
x = x-step;
if(x<-imgs[imgNum].getIconWidth()){
panel.fishs.remove(this);
}
break;
case 2:
y = y + step;
if(y>panel.getHeight()){
panel.fishs.remove(this);
}
break;
case 3:
x = x+step;
if(x>panel.getWidth()){
panel.fishs.remove(this);
}
break;
}
}else{//抓住就移除
panel.fishs.remove(this);
}
imgNum++;
if(imgNum>=imgs.length && !isCatch){
imgNum = 0;
}
if(imgNum>=imgsCatch.length && isCatch){
imgNum = 0;
}
panel.repaint();
如果鱼没被抓住,那么就去匹配鱼的移动路径,左到右,右到左,上到下,匹配到后对其原先的位置进行一个步进,对,是通过横纵坐标的加减操作来实现位置的移动,每次大概是两个像素,并且说是一旦此时的横纵坐标超出鱼池范围了,就把鱼池里的这条鱼移掉。
若说是鱼被抓住了,那处理起来就简单了,直接移掉。
接续地通过多帧图片实现鱼的动画效果,也就是去把鱼图片数组里的显示的图片下标加一操作。判别置零。
最后不断调用鱼池的paint()来画出这个动效。
3.2.2 FishNet类的核心算法
FishNet类的核心算法是caFish()(抓鱼方法)。
for(int j=0;j<panel.fishs.size();j++){
Fish f = panel.fishs.get(j);
if(f.x>this.x-(f.imgs[0].getIconWidth()/2) && f.x+f.imgs[f.imgNum].getIconWidth()/2<this.x+this.icon.getIconWidth() //+5减去命中率会高很多
&& f.y>this.y-(f.imgs[0].getIconHeight()/2) && f.y+f.imgs[f.imgNum].getIconHeight()/2<this.y +this.icon.getIconHeight())
{
f.hp–;
if(f.hp==0){
panel.fishs.get(j).isCatch = true;
catchF.add(f);
// Money m = new Money(f.x,f.y,panel,f);
// panel.ms.add(m);
panel.score+=f.money;
// new MoneMoveThread(m).start();
}
}
}
通过for循环每一个集合里的鱼,得到它,进行判定。
f.x>this.x-(f.imgs[0].getIconWidth()/2) //利用大于号小于号来判断就可以了
渔网抓鱼的判定方法,鱼的横坐标大于渔网的横坐标减去鱼图片宽度一半的话
f.x+f.imgs[f.imgNum].getIconWidth()/2<this.x+this.icon.getIconWidth()
并且,鱼的横坐标加上鱼的图片宽的一般要小于鼠标横坐标加上网的宽度的一半
那么鱼的血量减少
等鱼的血量减少为0时,鱼被抓到,catchF中添加一个被抓到的鱼,分数(也就是金钱)加上鱼的价值。
3.2.3 FishPanel类的核心算法
FishPanel类的核心算法是paint(),对,集合到这里来了,要画一起画。
public void paint(Graphics g){
super.paint(g);
Image bgImg=null;
Image bgImg1=null;
Image bgImg2=null;
bgImg=new ImageIcon(“images/bg_03.png”).getImage(); //鱼塘背景图
bgImg1=new ImageIcon(“images/bg1.jpg”).getImage(); //显示积分图片
bgImg2=new ImageIcon(“images/solider.png”).getImage(); //横条
//画背景图
g.drawImage(bgImg,0,0,this);
g.drawImage(bgImg1,15,this.height-72,this);
Image rightAdd=new ImageIcon(“images/ui_button_65_hv.png”).getImage(); //加号图标
g.drawImage(rightAdd,498,this.height-35,50,40,this);
Image lefeSub=new ImageIcon(“images/ui_button_63_hv.png”).getImage(); //减号图标
g.drawImage(lefeSub,338,this.height-35,50,40,this);
g.drawImage(bgImg2,560,453,this);
// 画分数
g.setFont(new Font("微软雅黑", Font.BOLD, 28));
g.setColor(Color.red);
g.drawString("" + score % 10 + "", 150, 478); //先宽后高
g.drawString("" + (score / 10 % 10), 127, 478);
g.drawString("" + (score / 100 % 10), 104, 478);
g.drawString("" + (score / 1000 % 10), 81, 478); //分数是一个个处理显示的
g.drawString("" + (score / 10000 % 10), 58, 478);
g.drawString("" + (score / 100000 % 10), 36, 478);
//画子弹个数
g.setColor(Color.white);
g.setFont(new Font("楷体", Font.BOLD, 15));
g.drawString("BULLET-NUM:", 10, 20);
g.drawString(""+bullet_num, 120, 20);
//画鱼
for(int i=0;i<fishs.size();i++){
Fish f=(Fish)fishs.get(i); //ArrayList
f.drawFish(g);
}
// 画子弹
for (int i = 0; i < bullets.size(); i++) {
Bullet b = bullets.get(i);
b.drawBullet(g);
}
//画鱼网
for (int i = 0; i < nets.size(); i++) {
FishNet n = nets.get(i);
n.drawNet(g);
}
//画炮筒
Image barrel=new ImageIcon("images/level.png").getImage();
int x=barrelX+19; //barrelX
int y=barrelY+39;
double y1=(double)mouseY-y;
double x1=(double)mouseX-x; //mouseX
double f=-Math.atan(x1/y1);
if(f<=-Math.PI/2)
f=-Math.PI/2;
if(f>=Math.PI/2)
f=Math.PI/2;
roate=f;
Graphics2D gp=(Graphics2D)g;
gp.rotate(f,x,y);
gp.drawImage(barrel,barrelX,barrelY,this);
}
定义好要用的变量
依次画上背景图 分数 显示子弹个数的图 鱼 子弹 画鱼网 炮筒
3.3类设计
如上所述,本游戏的实现,主要依靠以下一些类:MainFrame.java,FishPanel.java,Fish.java,FishThread.java,Bullet.Java,BulletThread.java,FishNet.java,LocationUtil.java,Demo.java
3.3.1游戏主界面的设计
主要依靠MainFrame.java来实现
创建游戏的运行界面的框架,带有main()函数的类,MainFrame.java类,专门用来承载游戏的运行,在主函数中创建MainFrame类的一个对象(而MainFrame类是继承自JFrame类的),即创建window视窗,利用构造函数初始化,实现ActionListener接口,动作事件监听器的重写,JFrame 的paint()方法重写,具体代码如下(去掉了方法体里面的具体实现方法):
public class MainFrame extends JFrame implements ActionListener {
JLabel mainLabel;
FishPanel panel; // 鱼池
JButton min, close;// 最小化,关闭按钮
public MainFrame(){}
public void addFish(){}
public void paint(Graphics g) {}
public static void main(String args[]){}
public void actionPerformed(ActionEvent e) {}
}
3.3.2游戏鱼池的设计
主要依靠FishPanel.java来实现
建的FishPanel.java类要继承JPanel类,用构造方法初始化,用paint()的方法画出游戏运行的效果出来,首先画出游戏的鱼池背景来。实现Runnable接口,实现多线程操作。mouseMoved()和mousePressed()重写监听处理事件的发生,由于是实现MouseListener()与MouseMotionListener()接口,所以不得不写上全部的方法,导致显得有些有些累赘。具体代码如下(去掉了方法体里面的具体实现方法):
public class FishPanel extends JPanel implements MouseListener,MouseMotionListener,Runnable{
int score=666666;
int bullet_num=99999; //子弹数无限
public int width; //窗口的宽度
public int height; //窗口的高度
public int barrelX; //炮筒的图片横坐标
public int barrelY; // 炮筒的纵坐标
double roate; //炮筒的角度
int mouseX=0;
int mouseY=0;static boolean isOver=false; //游戏是否结束的标致
ArrayList fishs=new ArrayList();
ArrayList bullets = new ArrayList();
ArrayList nets=new ArrayList();
public FishPanel(int width,int height){} //构造方法初始化
public void paint(Graphics g) {}
public void mouseMoved(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited (MouseEvent e) {}
public void mousePressed (MouseEvent e) {}
public void mouseReleased (MouseEvent e) {}
public void mouseDragged (MouseEvent e) {}}
运行后的游戏背景图3所示:
图3
也可以根据自己爱好换背景图,如图4所示:
图4
3.3.3游戏中鱼的设计
主要依靠Fish.java来实现
在这个类之中,我们要创建鱼的对象,但是为了方便启动每一条鱼,我们在MainFrame类的addFish()方法里,增加了一个匿名的线程对象,这样,每一条鱼就相当于一个线程,每一个线程我们就可以对它分别进行控制,比如移动,速度,血量,当然还有鱼游动的效果。在玩家所看见鱼儿游动的效果其实就是鱼儿游动的一张张图片在不停的变换。我们所做的鱼儿素材只有静态的图片。根据研究,只要换的图片帧数达到20张每秒我们就会认为图片就是动的 。所以在鱼儿游动的时候我们只需要设置f.timeThread=40就行了,时间一到40ms就会切换鱼的形态图,相当于1秒钟换了25张图片。速度上已经相当快了。这里我们在构造方法里面写出ImageIcon类的一个数组对象,将鱼游动的不同图片用缓冲技术将图片加载到游戏中去,加快游戏运行速度。
具体代码如下(去掉了方法体里面的具体实现方法):
鱼的移动,鱼有几个属性如下:x,y代表鱼的左上角的坐标,width,height代表当前鱼的图片的一个宽和高。
public class Fish { //实现鱼的显示
public int x;//横坐标
public int y;//纵坐标
public int imgNum = 0;//当前显示的图片下标
public ImageIcon imgs[];//鱼的图片
public boolean isCatch = false;//是否被捕
boolean isMove = true; //被捕的时候鱼会有停下来抽搐的效果
public ImageIcon[] imgsCatch;
public int road=0;//移动路线
public int step = 2;//移动的速度
public int timeThread = 30; //
FishPanel panel;
public static int right_to_left = 1;
public static int up_to_buttom = 2;
public static int left_to_right = 3;
public int money;//每种鱼获得的金币
public int hp;//血量 //改血量
public Fish(FishPanel panel){}
public void drawFish(Graphics g) {}
public void move(){}
}
其实这样的话鱼已经可以移动了,但是,在运行出来以后,其实鱼还没动,这是因为,画面定格了,所以我们需要调用系统的repaint()方法,将鱼游动之后的样子画出来,通过不停的重画,使鱼儿游动起来。在每一次使用move方法最后都会调用repaint (),重画鱼,不停的重画。然后线程停顿在FishThread中,判定好后Thread.sleep(fish.timeThread),fish.timeThread是在MainFrame类中设置好的。
3.3.4游戏渔网的设计
主要依靠FishNet.java来实现。
具体代码如下(去掉了方法体里面的具体实现方法):
public class FishNet {
int x;
int y;
int count = 0;
public boolean isLive = true;
ImageIcon icon;
FishPanel panel;
ArrayList catchF = new ArrayList();
public FishNet(int x,int y,ImageIcon icon,FishPanel panel)
public void graw(){}
public void caFish(){} //网捕鱼
public void drawNet(Graphics g) {} //画网
}
创建FishNet.java类。与创建鱼的方法类似。渔网有x,y,表示渔网左上角的坐标,
将本地的渔网图片加载进来,在构造方法中写如下主要代码:
this.panel = panel;
this.x = x;
this.y = y;
this.icon = icon;
同样的道理,在FishNet类中的piant()方法中画出渔网:
g.drawImage(icon.getImage(), x, y, panel);
3.4功能实现
3.4.1程序流程图
本游戏的整体流程如图5所示
图5 本游戏的整体流程
3.4.2 主要代码及注释
- MainFrame类中(由于篇幅限制,只贴出成员方法)
1. public MainFrame() {
// 定义构造方法来初始化窗口
ImageIcon bg=new ImageIcon(“images/bg.png”);//从图像绘制图标的图标界面的实现
ImageIcon bg_03=new ImageIcon(“images/1.png”);
setUndecorated(true);
setIconImage(new ImageIcon(“images/icon.jpg”).getImage());
mainLabel = new JLabel(bg);
setBounds(100, 100, bg.getIconWidth(), bg.getIconHeight());
// 设置鱼池面板
panel = new FishPanel(bg_03.getIconWidth(), bg_03.getIconHeight());
panel.setBounds(14, 50, bg_03.getIconWidth(), bg_03.getIconHeight());
mainLabel.add(panel);
// 设置窗体可以跟随鼠标移动和透明
LocationUtil util = new LocationUtil(this);
util.setOp();
min = new JButton(new ImageIcon("images/min.png"));
min.setBounds(740, 13, 32, 32);
min.setBorderPainted(false);// 去掉按钮的边框
min.setContentAreaFilled(false); // 去掉按钮的填充区域
mainLabel.add(min);
close = new JButton(new ImageIcon("images/close.png"));
close.setBounds(780, 13, 32, 32);
close.setBorderPainted(false);
close.setContentAreaFilled(false);
mainLabel.add(close);
add(mainLabel);
setVisible(true);
min.addActionListener(this);
close.addActionListener(this);
panel.addMouseMotionListener(panel);
panel.addMouseListener(panel);
Thread th=new Thread(panel);
th.start();//启动多线程
}
public void addFish() {
new Thread() { //new 一个新线程去跑
public void run() {
while(!FishPanel.isOver) {
Fish f = new Fish(panel);
ImageIcon[] imgs = new ImageIcon[10]; //十个动作切片 合成动图
ImageIcon[] caImgs = new ImageIcon[2]; //被捕
int road = (int) (Math.random() * 3) + 1; //1-4
int kind = (int) (Math.random() * 9) + 1; //1-10
if(kind1){
f.timeThread=40;
f.money = 2;
f.hp=1;
}else if(kind2){
f.timeThread=60;
f.money = 2;
f.hp = 1;
}else if(kind3){
f.timeThread=60;
f.money = 2;
f.hp = 1;
}else if(kind4){
f.timeThread=60;
f.money = 2;
f.hp=1;
}else if(kind5){
f.timeThread=60;
f.money = 4; //金钱数翻倍 不过血量也翻倍
f.hp=2;
}else if(kind2){
f.timeThread=60;
f.money = 4;
f.hp=2;
}else if(kind6){
f.timeThread=60;
f.money = 4;
f.hp=2;
}else if(kind7){
f.timeThread=60;
f.money = 6;
f.hp=3;
}else if(kind8){
f.timeThread=70;
f.money = 6;
f.hp = 3;
}else if(kind9){
f.timeThread=60;
f.money = 8; //到最后情形捕获得4倍金币
f.hp=4;
}
String path = “”;
if (road == 1) {
path = “images/right_to_left/fish”
+ (kind < 10 ? “0” + kind : kind + “”) + “_”;
int height = (int) (Math.random() * panel.getHeight()) - 100; //100减掉是不让它从积分横条那里出来
//这几句代码的意思是随机出鱼出场的位置在右边,且高度不能超过游戏界面的高度
f.x = 1000;
f.y = height;
} else if (road == 2) {
path = "images/up_to_bottom/fish" //黑心 up_to_bottom up_to_buttom 文件夹名字都要改一改的
+ (kind < 10 ? "0" + kind : kind + "") + "_";
int width = (int) (Math.random() * panel.getWidth());
f.x = width;
f.y = -100;
} else if (road == 3) {
path = "images/left_to_right/fish"
+ (kind < 10 ? "0" + kind : kind + "") + "_";
int height = (int) (Math.random() * panel.getHeight()) - 100;
f.y = height;
f.x = -300;
}
for (int i = 1; i <= 2; i++) {
String path1 = path + "catch_0"
+ i + ".png";
ImageIcon icon = new ImageIcon(path1);
caImgs[i - 1] = icon;
}
for (int i = 1; i <= 10; i++) {
String path1 = path + (i < 10 ? "0" + i : i + "")
+ ".png";
ImageIcon icon = new ImageIcon(path1);
imgs[i - 1] = icon;
}
f.imgs = imgs;
f.imgsCatch = caImgs;
f.road = road; //存进去
panel.fishs.add(f);
FishThread t = new FishThread(f);
t.start();
try {
Thread.sleep(1000); //停一秒 出来一个 改成10毫秒出来 舒服 快100倍
//每隔一秒出来一只鱼,同方向上位置随机,可表现为鱼杂乱无章得游出来
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
//画时间
public void paint(Graphics g){
g.setFont(new Font("隶书",Font.BOLD,30));
g.setColor(Color.white);
Date date=new Date();repaint();
super.paint(g);
g.drawString(date.toLocaleString(), 300, 40);
}
public static void main(String args[]) {
MainFrame mframe=new MainFrame();
mframe.addFish();
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == min) {// 最小化窗口
this.setState(JFrame.ICONIFIED);
} else if (e.getSource() == close) {
System.exit(0);// 退出窗口
}
}
- Fish类(由于篇幅限制,只贴出成员方法)
2. public Fish(FishPanel panel){
this.panel = panel; //这个类里面也有鱼塘了
}
//画鱼的图片
public void drawFish(Graphics g) {
if(isCatch){
g.drawImage(imgsCatch[imgNum%2].getImage(), x, y,panel);
}else{
g.drawImage(imgs[imgNum%10].getImage(), x, y,panel);
}
}
//鱼游动
public void move(){
//如果没被捕
if(!isCatch){
switch(road){
case 1:
x = x-step;
if(x<-imgs[imgNum].getIconWidth()){
panel.fishs.remove(this);
}
break;
case 2:
y = y + step;
if(y>panel.getHeight()){
panel.fishs.remove(this);
}
break;
case 3:
x = x+step;
if(x>panel.getWidth()){
panel.fishs.remove(this);
}
break;
}
}else{//抓住就移除
panel.fishs.remove(this); //好 哈哈哈哈哈
}
imgNum++;
if(imgNum>=imgs.length && !isCatch){
imgNum = 0;
}
if(imgNum>=imgsCatch.length && isCatch){
imgNum = 0;
}
panel.repaint();
}2. FishPanel类(由于篇幅限制,只贴出成员方法)
public FishPanel(int width,int height){ //构造方法初始化
this.width=width;
this.height=height;
barrelX=416;
barrelY=this.height-62;
}
//重写paint()方法
public void paint(Graphics g){
super.paint(g);
Image bgImg=null;
Image bgImg1=null;
Image bgImg2=null;
bgImg=new ImageIcon(“images/1.png”).getImage(); //鱼塘背景图
bgImg1=new ImageIcon(“images/bg1.jpg”).getImage(); //显示积分图片
bgImg2=new ImageIcon(“images/solider.png”).getImage(); //横条
//画背景图
g.drawImage(bgImg,0,0,this);
g.drawImage(bgImg1,15,this.height-72,this);
Image rightAdd=new ImageIcon(“images/ui_button_65_hv.png”).getImage(); //加号图标
g.drawImage(rightAdd,498,this.height-35,50,40,this);
Image lefeSub=new ImageIcon(“images/ui_button_63_hv.png”).getImage(); //减号图标
g.drawImage(lefeSub,338,this.height-35,50,40,this);
g.drawImage(bgImg2,560,453,this);
// 画分数
g.setFont(new Font(“微软雅黑”, Font.BOLD, 28));
g.setColor(Color.red);
g.drawString("" + score % 10 + “”, 150, 478); //先宽后高
g.drawString("" + (score / 10 % 10), 127, 478);
g.drawString("" + (score / 100 % 10), 104, 478);
g.drawString("" + (score / 1000 % 10), 81, 478);
g.drawString("" + (score / 10000 % 10), 58, 478);
g.drawString("" + (score / 100000 % 10), 36, 478);
//画子弹个数
g.setColor(Color.white);
g.setFont(new Font(“楷体”, Font.BOLD, 15));
g.drawString(“BULLET-NUM:”, 10, 20);
g.drawString(""+bullet_num, 120, 20);
//画鱼
for(int i=0;i<fishs.size();i++){
Fish f=(Fish)fishs.get(i); //ArrayList
f.drawFish(g);
}
// 画子弹
for (int i = 0; i < bullets.size(); i++) {
Bullet b = bullets.get(i);
b.drawBullet(g);
}
//画鱼网
for (int i = 0; i < nets.size(); i++) {
FishNet n = nets.get(i);
n.drawNet(g);
}
//画炮筒
Image barrel=new ImageIcon(“images/level.png”).getImage();
int x=barrelX+19; //barrelX
int y=barrelY+39;
double y1=(double)mouseY-y;
double x1=(double)mouseX-x; //mouseX
double f=-Math.atan(x1/y1);if(f<=-Math.PI/2)
f=-Math.PI/2;
if(f>=Math.PI/2)
f=Math.PI/2;
roate=f;
Graphics2D gp=(Graphics2D)g;
gp.rotate(f,x,y);
gp.drawImage(barrel,barrelX,barrelY,this);
if(f<=-Math.PI/2)
f=-Math.PI/2;
if(f>=Math.PI/2)
f=Math.PI/2;
roate=f;
Graphics2D gp=(Graphics2D)g;
gp.rotate(f,x,y);
gp.drawImage(barrel,barrelX,barrelY,this);
}
@Override
public void mouseMoved(MouseEvent e) {
this.mouseX=e.getX();//横坐标 //补上了
this.mouseY=e.getY();//纵坐标
FishPanel.this.repaint();}
@Override
public void run() {
while(!isOver){
if(bullet_num<=0){
isOver=true;
this.removeMouseListener(this); //结束鼠标移动
this.removeMouseMotionListener(this);
JOptionPane.showMessageDialog(null,“您的子弹已用完,游戏结束!”);
}
}
try{
Thread.sleep(30);
}catch(Exception e){
e.printStackTrace();
}}
@Override
public void mouseClicked(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub}
@Override
public void mousePressed(MouseEvent e) {
if (e.getY() < this.height - 50) { //当处在显示积分图片以上的时候
Bullet bullet = new Bullet(FishPanel.this);
bullet.img = new ImageIcon(“images/bullet.png”);
bullet.x = barrelX + 19 - 10;
bullet.y = barrelY + 39 - 17;
bullet.roate = roate; //子弹的角度就是炮口的角度
bullet.p = new Point(barrelX + 19, barrelY + 39); //Point
bullets.add(bullet); //集合中加入一个子弹
BulletThread t = new BulletThread(bullet);
t.start();
bullet_num–;
score-=10;
}}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub}
@Override
public void mouseDragged(MouseEvent e) {}
4. FishNet类(由于篇幅限制,只贴出成员方法)
public FishNet(int x,int y,ImageIcon icon,FishPanel panel){
this.panel = panel;
this.x = x;
this.y = y;
this.icon = icon;
}
public void graw(){
count++;
if(count>=5){
x = -100;
y=-100;
isLive = false;
}
}
//网捕鱼
public void caFish(){
for(int j=0;j<panel.fishs.size();j++){
Fish f = panel.fishs.get(j);
if(f.x>this.x-(f.imgs[0].getIconWidth()/2) && f.x+f.imgs[f.imgNum].getIconWidth()/2<this.x+this.icon.getIconWidth() //+5减去命中率会高很多
&& f.y>this.y-(f.imgs[0].getIconHeight()/2) && f.y+f.imgs[f.imgNum].getIconHeight()/2<this.y +this.icon.getIconHeight())
{
f.hp–;
if(f.hp==0){
panel.fishs.get(j).isCatch = true;
catchF.add(f);
// Money m = new Money(f.x,f.y,panel,f);
// panel.ms.add(m);
panel.score+=f.money;
// new MoneMoveThread(m).start();
}
}
}
}
//画网
public void drawNet(Graphics g) {
g.drawImage(icon.getImage(), x, y, panel);
}5. Bullet类(由于篇幅限制,只贴出成员方法)
public Bullet(FishPanel panel) {
this.panel = panel;
}
public void move() throws InterruptedException {
y -= 20;
// 转换x和y坐标
int yy = Math.round((float) Math.cos(roate) * (p.y - 17 - y));
yyy = p.y - 17 - Math.abs(yy);
System.out.println(yyy);
int xx = Math.round((float) Math.sin(roate) * (p.y - 17 - y));
xxx = p.x - 10 + xx;
System.out.println(xxx);
if (yyy<=-10 || xxx <= -10) {
isLive = false;
panel.bullets.remove(this);
}
hint();
panel.repaint();
}
// 处理击中某条鱼 撒网
public void hint() throws InterruptedException{
for(int i=0;i<panel.fishs.size();i++){
Fish f = panel.fishs.get(i);
if(xxx>f.x-img.getIconWidth() && xxx<f.x
+f.imgs[f.imgNum].getIconWidth() && y<f.y
+f.imgs[f.imgNum].getIconHeight()){
FishNet net = new FishNet(xxx-30, yyy-50,
new ImageIcon(“images/web.png”),panel);
panel.bullets.remove(this);//子弹消失
panel.nets.add(net);//子弹消失后撒网
isLive = false;
net.caFish();
/*******************/
Thread.sleep(500);//等2秒之后让网消失
panel.nets.remove(net);
}
}}
public void drawBullet(Graphics g) {
Graphics2D gp = (Graphics2D) g.create();
gp.rotate(roate, p.x, p.y);
gp.drawImage(this.img.getImage(), x, y, panel);
}
3.5界面
进入如图6所示的游戏界面,开始游戏,可以看到: 游动的鱼,左上角当前的剩余子弹数,左下角为现有的金币数(也就是积分),右上角从右至左依次为关闭游戏、最小化和本游戏的标题,界面下方正中是炮台,此外炮台会跟随鼠标的移动变换方向。并且由于日期转为本地时间的刷新频率太低,无法被截屏捕捉到,是个遗憾。
图6界面显示
第四章:运行调试与分析讨论
4.1 运行调试
4.1.1发射炮弹没捕到鱼
发射炮弹没捕到鱼,金币数量每次减10(嗯,对,炮弹就有这么贵,不过每人初始化都有666666金币,99999个炮弹,对,就是这么土豪)炮弹数量减1。
图7 发射炮弹没捕到鱼
4.1.2发射炮弹捕到鱼
发射炮弹捕到鱼,金币每次加根据内置规则加金币,炮弹数量减1。
图8 发射炮弹捕到鱼
4.1.3其他功能
游戏中还设计了其他功能,无法用图片的形式描述,如游动的鱼,炮台会跟随鼠标的移动变换方向等。
4.2分析运行结果
经过以上的测试、运行与调试,游戏的效果已经很好了,至于其他的问题暂时还没有找到。
第五章:设计体会与小结
5.1 设计体会与小结
这一个新星期的课程设计我相信学到了很多。通过老师给我带入门,终于看到了游戏编程的过程。一个星期,5天的时间,经过老师的悉心教导,完成了捕鱼达人这个游戏,其进度不可谓不快。在学习的过程中,充分利用了所学的知识,将之融汇,贯通,最后得到这样的成品出来,心中不免有点小激动。
在试验过程中对于java的swing组建的应用更是如鱼得水,游戏的各个板块都与之相关,比如JFrame类创建游戏运行的视窗也是相当于画板,将游戏的效果显示出来,Pannel类画出背景,鱼,网等。
还有,我对于面向对象的理念更加的清晰,以前对于面向过程,面向对象的概念还不是很懂,如今明白,面向对象,简而言之是万物皆对象,在编程的过程中,鱼是一个对象,渔网也是一个对象,还有里面的鱼池也是一个对象,甚至是游戏运行的视窗也是JFrame的一个对象。每个对象都有自己的属性,方法,当然还有构造方法,对对象进行初始化。
在程序设计过程中,我充分的体会到了“实践出真知”这一点,书本上的知识是不够的,只有把理论与实践相结合才能够真正的学到知识。一个游戏的设计,不可能一步到位,还需要不断的完善和补充。同时,游戏中还存在许多问题,有待在日后的使用中发现和解决。编程前的深思熟虑是减少程序调试工作量的重要方法,只有进行充分考虑,才会减少调试过程中的工作量。虽然在开始写程序之前我们做了多的准备工作,但在真正的写程序时仍然发现许多问题,有些问题是分析时的疏漏,有些则是如果不做无论如何也想不到的。
同样的,让我亲身感受到了“纸上得来终觉线”这句话的含义,可能听老师讲的时候都能听懂,等到自己操作的时候就总是眼高手低,不知如何下手,但最终我们通过分工合作、相互请教解决了在开发过程中,从需求分析到功能实现的一些问题,完成了这个项目。
在开发过程中,我也查找了不少资料,从实际出发儿次修改了需求分析,使最终开发出的游戏具有一定趣味性,并且基本实现需求。通过本次课设我意识到, 我们对知识的学习,不能光停留在书本上,还要开拓眼界,主动学习,才能让自己 跟得上时代的步伐。