【mie haha的博客】转载请注明出处(万分感谢!):
https://blog.csdn.net/qq_40315080/article/details/82823954
写代码前分析下游戏的各个部分
1.游戏构成:
大鱼吃小鱼=主角鱼+其他各种鱼(自由鱼)+游戏按钮+游戏背景
2.游戏过程:
主角鱼遇到比自己小的鱼:吃掉–>自身size变大
主角鱼遇到比自己大的鱼:被吃掉–>自由鱼size变大,游戏结束
接下来开始写代码:
事前准备:
1.画图片(比如在网上下载了鱼的图片,想要在界面上画不同的鱼,而不是画什么简单的○,□):举例格式如下:
Toolkit tool = Toolkit.getDefaultToolkit();
Image myfishshape = tool.getImage(Fish.class.getResource("myfish_zheng.png"));
首先显示游戏的界面:
界面中需要有主角鱼,一堆自由鱼,游戏按钮和游戏背景
这里主角鱼和自由鱼其实都是鱼,只是主角鱼受鼠标控制,游的速度不同,可以把所有的鱼看成一个整体队列,这样写很方便
```public class Framefish extends JFrame {
//当前画布的画笔
Graphics g;
//画布里所有要移动的小?
ArrayList list = new ArrayList();
//函数名字不能叫show
public void showui(){
//设置窗体
this.setTitle("大鱼吃小鱼");
this.setSize(1200,1000);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
this.setBackground(Color.white);
FlowLayout flow = new FlowLayout();
this.setLayout(flow);
//设置按钮
Dimension demension = new Dimension(80,80);
JButton button1 = new JButton("开始");
JButton button2 = new JButton("暂停");
JButton button3 = new JButton("结束");
JButton button4 = new JButton("恢复");
button1.setPreferredSize(demension);
button2.setPreferredSize(demension);
button3.setPreferredSize(demension);
button4.setPreferredSize(demension);
this.add(button1);
this.add(button2);
this.add(button3);
this.add(button4);
//按钮添加监听器,实现开始暂停恢复结束
buttonlisenenr bl = new buttonlisenenr(this,this.list,this.paopaolist);
button1.addActionListener(bl);
button2.addActionListener(bl);
button3.addActionListener(bl);
button4.addActionListener(bl);
//设置窗体可见
this.setVisible(true);
g=this.getGraphics();
}
鱼队列中的每一条鱼都有游的速度,大小的区别,同时这些鱼需要判断是否被吃掉获吃掉其他鱼,并且要出现在屏幕上(即被画下来),在屏幕上游动(即变化位置运动)对这些属性进行定义,创建鱼类:
public class Fish {
// 出现的位置和本身的大小
int x, y, size, speedx, speedy;
// 在次画布上画下,再把次画布画在界面上
Graphics g;
Framefish frame;
// 在鱼队列的生成中对每一个生成的小鱼初始化
public Fish(int x, int y, int speedx, int speedy, int size, Graphics g, Framefish frame) {
this.x = x;
this.y = y;
this.speedx = speedx;
this.speedy = speedy;
this.size = size;
this.g = g;
this.frame = frame;
}
// 还活着就画下来,否则马上把这条鱼从队列中移除
public void draw(Graphics g,ArrayList list) {
}
// 从队列中第一个鱼开始判断,大的鱼吃掉小的鱼
public void eat(ArrayList list) {
}
// 每条鱼都有横纵速度,只要再界面内就继续移动,但不一定画出
public void move(ArrayList list) {
}
}
上面的Fish类中用来画下鱼的draw函数,用来判断鱼吃掉或被吃掉的函数eat函数,用来让鱼游动起来的函数move都没有写内容,接下来将它们一一补全:
最简单的move函数用来使鱼游动:即不断变化鱼的位置x,y,每条鱼都有自己的速度,只要x按照speedx移动,y按照speedy移动即可,主角鱼例外,它只受鼠标控制,自己没有速度,即初始速度为0
public void move(ArrayList list) {
if (this.x>=0 && this.x+this.size<= frame.getWidth())
x = x + speedx;
if (this.y >= 0 && this.y+this.size<= frame.getHeight())
y = y + speedy;
}
画鱼其实是把鱼图案放在一个矩形内,将这个矩形画下(g.drawImage(图片名称,矩形左上角横坐标x,矩形左上角纵坐标y,矩形长,矩形宽,位置【一般写为null即可】)(因为试想按照鱼的不规则轮廓来画太困难了,我没有找到这样的方法),在画鱼的时候如果这条鱼按照速度已经游出了界面,这条鱼就不再出现在屏幕中,不再考虑这条鱼,就把这条鱼从所有要出现的鱼列中删掉(这里使用list.remove()来删除),这样就避免出现在看不到的界面外还在进行大鱼吃小鱼而游戏者却因为看不到被吃鱼的过程只看到吃鱼结果而莫名其妙的情况。
Toolkit tool = Toolkit.getDefaultToolkit();
Image myfishshape = tool.getImage(Fish.class.getResource("myfish_zheng.png"));
Image myfishshape_to_right = tool.getImage(Fish.class.getResource("myfish_fan.png"));
Image fishshape1 = tool.getImage(Fish.class.getResource("knewfish1.png"));
Image fishshape2 = tool.getImage(Fish.class.getResource("koufish3.png"));
Image fishshape4 = tool.getImage(Fish.class.getResource("knewfish4.png"));
// 还活着就画下来,否则马上把这条鱼从队列中移除
public void draw(Graphics g,ArrayList list) {
if (this.size > 0) {
// 不是我的鱼
if (this != list.get(0)) {
//超出画面就删掉这个鱼
if(this.x+this.size>=frame.getWidth())list.remove(this);
else if(this.y+this.size>=frame.getHeight())list.remove(this);
else if (this.size <= 60)
g.drawImage(fishshape1, x, y, size-10, size-10, null);
else if (this.size > 60 && this.size <= 120)
g.drawImage(fishshape2, x, y, size+20, size+20, null);
else if (this.size > 120)
g.drawImage(fishshape4, x, y, size, size, null);
System.out.println(this.size);
}
//是我的鱼
else if (this == list.get(0)){
g.drawImage(myfishshape, x, y, size, size, null);
}
else if (this.size <= 0&&this!=list.get(0))
{
list.remove(this);
}
}
接下来就是游戏的关键–判断鱼是吃掉其他鱼还是被其他鱼吃掉。每一时刻,每条在屏幕上出现的鱼,都可以吃其他鱼或者被吃掉(假设没有无敌鱼),对每条鱼,都需要判断它与屏幕上其他所有鱼的size关系:(注意:只需要统一判断每条鱼能否吃掉其他的鱼即可,被吃掉的鱼删掉不再考虑)
public void eat(ArrayList list) {
//Music musiceat = new Music();
System.out.println(">>>>>"+list.size());
for (int i = 0; i < list.size(); i++) {
// 矩形相交就判断size
Fish fish1 = this;
Fish fish2 = list.get(i);
if (fish1 != fish2
&& ((fish1.x >= fish2.x && fish1.x <= fish2.x + fish2.size && fish1.y >= fish2.y
&& fish1.y <= fish2.y + fish2.size)
|| (fish1.x >= fish2.x && fish1.x <= fish2.x + fish2.size && fish1.y + fish1.size >= fish2.y
&& fish1.y + fish1.size <= fish2.y + fish2.size)
|| (fish1.x + fish1.size >= fish2.x && fish1.x + fish1.size <= fish2.x + fish2.size
&& fish1.y >= fish2.y && fish1.y <= fish2.y + fish2.size)
|| (fish1.x + fish1.size >= fish2.x && fish1.x + fish1.size <= fish2.x + fish2.size
&& fish1.y + fish1.size >= fish2.y && fish1.y + fish1.size <= fish2.y + fish2.size))) {
if (fish1.size > fish2.size) {
fish1.size += fish2.size / 4;
fish2.size = 0;
//musiceat.playEatMusic();
} else if (fish1.size < fish2.size) {
fish2.size += fish1.size / 4;
fish1.size = 0;
//musiceat.playEatMusic();
}
}
}
}
判断能否吃掉其他的鱼时,因为画鱼其实是画鱼图案所在的矩形,所以只需要判断两个矩形是否相交(在纸上稍稍画一下,可以发现只有4种情况,列举一下即可),如果相交就大的吃掉小的,所画矩形的大小可表示鱼的size(drawImage函数的第3,4个参数)。
每条鱼都已经写好,现在只需把所有这些鱼都放进一个队列里面,让它们隔一段时间就出现一个即可,此时需要用线程来控制鱼出现的速度(如果不用线程控制,所有的鱼将同时出现,屏幕上全都是鱼,影响游戏效果)
public class FishList extends Thread{
ArrayList list;
Graphics g;
Framefish frame;
Random random;
int sleeptime = 2000;
public FishList(Graphics g,Framefish frame,ArrayList list){
this.g=g;
this.frame=frame;
this.list=list;
random = new Random();
}
public void run(){
while(Framefish.over==0&&true){
//设置其他鱼的各种参数随机,创建鱼,加入鱼队列
//从左边产生,最小size=20,最大size=200
int x=0;
int y=random.nextInt(800)+20;
int speedx=random.nextInt(40)+20;
int speedy=random.nextInt(5)+0;
int size=random.nextInt(200)+20;
Fish fish = new Fish(x,y,speedx,speedy,size,g,frame);
list.add(fish);
//慢慢产生,每休眠3000时添加一条鱼
try {
Thread.sleep(sleeptime);//休眠3000
} catch (InterruptedException e) {
}
}
}
}
以上在产生每条鱼时借用了随机数random来使鱼在随机位置,以随机速度游动。
这样所有的自由鱼在产生时都有了自己的初始位置,速度,可以游动了,但主角鱼还需要鼠标来控制,在鼠标监听器中写鼠标控制主角鱼的方法:
public class mouselisener extends MouseAdapter{
Graphics g;
Frame frame;
Fish myfish;
ArrayList list;
float x,y;
public mouselisener(Graphics g, Frame mf, ArrayList list) {
this.g = g;
this.frame = mf;
this.list = list;
this.myfish = list.get(0);
}
public void mouseClicked(MouseEvent e) {}
public void mouseDragged(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseMoved(MouseEvent e) {
myfish.x = e.getX();
myfish.y = e.getY();
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseWheelMoved(MouseWheelEvent e) {}
public void actionPerformed(ActionEvent e) {}
}
现在,所有的鱼都已经就绪,它们都可以按照需要进行活动(移动,吃鱼或被吃)了,但它们都是只能在某一时刻完成,游戏需要的效果是让这些鱼连续地游动,产生类似动画的效果,这就需要间隔很短的时间就在屏幕上画一次所有的鱼,时间越短,画面越连贯(视觉暂留),这同样需要用线程控制【注意:每隔一段时间画一次所有的鱼,之前所画的鱼如果不擦掉,那么你将看到鱼运动的“轨迹”(一条线),而不是动画的感觉,这就需要画所有的鱼时,把底层的背景图片画布也再画一次,这样就覆盖了之前所画的鱼的痕迹,就不会出现一条线的情况了】—它有名字哒:次画布方法
public class Threadfish extends Thread{
Graphics g;
Frame frame;
ArrayList list;
public static boolean start = false,pause = false;
public Threadfish(Graphics g,Frame frame,ArrayList list,ArrayList paopaolist){
this.g=g;
this.frame=frame;
this.list=list;
this.paopaolist=paopaolist;
}
public void run(){
//创建次画布
Image im = frame.createImage(frame.getWidth(),frame.getHeight());
Graphics g2 = im.getGraphics();
Toolkit tool = Toolkit.getDefaultToolkit();
Image fishbackground= tool.getImage(Fish.class.getResource("sea.png"));
if(start){
System.out .println("222");
while(pause){
g2.drawImage(fishbackground,0,0,frame.getWidth(), frame.getHeight(),0,0,400,400,null);
for(int i=0;i=180){
String a = new String("胜利");
g.setFont(new Font("", Font.BOLD, 50));
g.drawString(a,500,500);
start=false;pause=false;
}
}
}
}
}
好的,一切就绪,已经把所有的步骤都统一在了线程Threadfish中,只需要在main函数中启动即可啦:
public static void main(String[] args){
//创建界面,界面出现
Framefish frame = new Framefish();
frame.showui();
//产生自己的鱼,初始size=60
Random random = new Random();
Fish myfish = new Fish(600,600,0,0,60,frame.g,frame);
frame.list.add(myfish);
//界面添加鼠标监听器
mouselisener ml = new mouselisener(frame.g,frame,frame.list);
System.out.println(frame.list.size());
frame.addMouseMotionListener(ml);
frame.addMouseListener(ml);
//产生鱼队列
FishList fishlist = new FishList(frame.g,frame,frame.list);
fishlist.start();
Threadfish threadfish = new Threadfish(frame.g,frame,frame.list,frame.paopaolist);
threadfish.start();
}
大鱼吃小鱼简易版完成!
运行图片:
(以上代码中有借线程开始,结束,暂停,恢复的实现,没有细说,感兴趣的朋友可以看下一篇)
ps:如果你还想加入音乐,想让主角鱼的头方向像4399小游戏里那样随你的鼠标移动而变化,想加入一些小效果使游戏背景看起来更自然逼真,可以看看下一篇
仍在入门,写得匆忙,若有错误,欢迎指出