Java大鱼吃小鱼游戏,今天你吃了吗?

1,373 阅读6分钟

作者简介

作者名:编程界明世隐 简介:CSDN博客专家,从事软件开发多年,精通Java、JavaScript,博主也是从零开始一步步把学习成长、深知学习和积累的重要性,喜欢跟广大ADC一起打野升级,欢迎您关注,期待与您一起学习、成长、起飞!

系列目录

1. Java俄罗斯方块
2. Java五子棋小游戏
3. 老Java程序员花一天时间写了个飞机大战
4. Java植物大战僵尸
5. 老Java程序员花2天写了个连连看
6. Java消消乐(天天爱消除)
7. Java贪吃蛇小游戏
8. Java扫雷小游戏
9. Java坦克大战
10. Java迷宫小游戏

引言:

前几天回乡下老家,看到小鱼在小河里游来游去,手痒就一度想下河去捞,但我女儿提醒我不要吃野生动物,这一下子让我回到了现实,原来我已经30多岁了,还真是老程序员,那既然老程序员不能去下河捞鱼,我自己做个大鱼吃小鱼的游戏不过分吧!于是它来了,它来了,它扛着电脑和 jdk 就来了。

效果图

在这里插入图片描述

实现思路

因为做的是简单版本的,所以思路也很简单。

  1. 绘制窗口。
  2. 创建菜单
  3. 创建我方鱼
  4. 创建敌鱼
  5. 键盘事件监听
  6. 创建主线程,重新绘制界面,并且定时创建敌鱼。
  7. 处理其他细

代码实现

创建窗口

首先创建一个游戏窗体类GameFrame,继承至JFrame,用来显示在屏幕上(window的对象),每个游戏都有一个窗口,设置好窗口标题、尺寸、布局等就可以。

package com.view;

import java.awt.BorderLayout;
import javax.swing.JFrame;

public class FishFrame extends JFrame {
    public int window_Width = 1180, window_Height = 640;
    public FishFrame() {
        setTitle("大鱼吃小鱼");//窗口标题
        setSize(window_Width, window_Height);//窗口分辨率
        setLayout(new BorderLayout());
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击关闭按钮是关闭程序
        setLocationRelativeTo(null);   //设置居中
    	setResizable(false); //不允许修改界面大小
        setVisible(true);//窗口是否可以显示 true---false
    }
}

创建面板容器GamePanel继承至JPanel

package com.view;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class GamePanel extends JPanel{
	GamePanel gamePanel=this;
	private JFrame mainFrame=null;
	//构造里面初始化相关参数
	public GamePanel(JFrame frame){
		this.setLayout(null);
		mainFrame = frame;
	}
	

}

再创建一个Main类,来启动这个窗口,用来启动。

package com.main;

import com.view.FishFrame;
import com.view.GamePanel;

public class Main {
    public static void main(String[] args) {
    	FishFrame frame = new FishFrame();
		GamePanel panel = new GamePanel(frame);
		frame.add(panel);
		frame.setVisible(true);//设定显示
    }
}

右键执行这个Main类,窗口建出来了 在这里插入图片描述

绘制背景图

创建Picture 类,负责加载所有的图片,方便调用

package com.utils;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

public class Picture {
    public static BufferedImage bg0,FishL,FishR;
    
    public static List badFishImages =new ArrayList() ;
    public static List badFishImages1 =new ArrayList() ;
    //静态代码块---程序启动,在加载类的时候,就会执行
    static {
        try {
            bg0 = ImageIO.read(Picture.class.getClassLoader().getResourceAsStream("img/bg0.jpg"));
            FishL = ImageIO.read(Picture.class.getClassLoader().getResourceAsStream("img/fishIcon0.png"));
            FishR = ImageIO.read(Picture.class.getClassLoader().getResourceAsStream("img/fishIcon0R.png"));
            BufferedImage temp;
            for (int i = 1; i <= 18; i++) {
            	temp = ImageIO.read(Picture.class.getClassLoader().getResourceAsStream("img/fishIcon"+i+".png"));
            	badFishImages.add(temp);
            	temp = ImageIO.read(Picture.class.getClassLoader().getResourceAsStream("img/fishIconL"+i+".png"));
            	badFishImages1.add(temp);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在GamePanel中重写paint方法,并绘制背景图。

@Override
public void paint(Graphics g) {
	super.paint(g);
	//绘制背景图
	g.drawImage(Picture.bg0, 0,0,null);
}

运行如下: 在这里插入图片描述

创建菜单

//初始化按钮
private void  initMenu(){
	// 创建菜单及菜单选项
	JMenuBar jmb = new JMenuBar();
	JMenu jm1 = new JMenu("游戏");
	jm1.setFont(new Font("思源宋体", Font.BOLD, 15));// 设置菜单显示的字体
	
	JMenuItem jmi1 = new JMenuItem("开始新游戏");
	JMenuItem jmi2 = new JMenuItem("退出");
	jmi1.setFont(new Font("思源宋体", Font.BOLD, 15));
	jmi2.setFont(new Font("思源宋体", Font.BOLD, 15));
	
	jm1.add(jmi1);
	jm1.add(jmi2);
	
	jmb.add(jm1);
	mainFrame.setJMenuBar(jmb);// 菜单Bar放到JFrame上
	
	jmi1.addActionListener(this);
	jmi1.setActionCommand("Restart");
	jmi2.addActionListener(this);
	jmi2.setActionCommand("Exit");
}

此时直接把这个代码加入到GamePanel中,发现是会报错的,需要实现ActionListener,并重写actionPerformed 方法。 在这里插入图片描述 此时GamePanel的代码如下:

package com.view;

import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.plaf.FontUIResource;

import com.utils.Picture;

public class GamePanel extends JPanel implements ActionListener{
	GamePanel gamePanel=this;
	private JFrame mainFrame=null;
	//构造里面初始化相关参数
	public GamePanel(JFrame frame){
		this.setLayout(null);
		mainFrame = frame;
		
		//菜单
		initMenu();
	}
	
	@Override
	public void paint(Graphics g) {
    	super.paint(g);
    	//绘制背景图
    	g.drawImage(Picture.bg0, 0,0,null);
    }
	//初始化按钮
	private void  initMenu(){
		// 创建菜单及菜单选项
		JMenuBar jmb = new JMenuBar();
		JMenu jm1 = new JMenu("游戏");
		jm1.setFont(new Font("思源宋体", Font.BOLD, 15));// 设置菜单显示的字体
		
		JMenuItem jmi1 = new JMenuItem("开始新游戏");
		JMenuItem jmi2 = new JMenuItem("退出");
		jmi1.setFont(new Font("思源宋体", Font.BOLD, 15));
		jmi2.setFont(new Font("思源宋体", Font.BOLD, 15));
		
		jm1.add(jmi1);
		jm1.add(jmi2);
		
		jmb.add(jm1);
		mainFrame.setJMenuBar(jmb);// 菜单Bar放到JFrame上
		
		jmi1.addActionListener(this);
		jmi1.setActionCommand("Restart");
		jmi2.addActionListener(this);
		jmi2.setActionCommand("Exit");
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();
		UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("宋体", Font.ITALIC, 18)));
		UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("宋体", Font.ITALIC, 18)));
		if ("Exit".equals(command)) {
			Object[] options = { "确定", "取消" };
			int response = JOptionPane.showOptionDialog(this, "您确认要退出吗", "",
					JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
					options, options[0]);
			if (response == 0) {
				System.exit(0);
			} 
		}else if("Restart".equals(command)){
			restart();
		}		
	}
	//重新开始
	private void restart() {
		
	}
}

在这里插入图片描述

创建我方鱼

package com.model;

import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import com.utils.Dir;
import com.utils.Picture;
import com.view.GamePanel;

public class Fish extends ArrayList<Fish> {
    private int x, y,/*主角鱼出生坐标*/
            width = 50, height = 30,
            speed = 10;

	private Dir dir;
    private boolean isMoving = false, isLiving = true;
    private GamePanel panel;//相互持有
    private String group;
    private Graphics g;
    private BufferedImage image=Picture.FishL;
    private int type = 1;//1 右, -1 左 

    public Fish(int x, int y, Dir dir, GamePanel panel, String group) {
        this.x = x;
        this.y = y;
        this.dir = dir;
        this.panel = panel;
        this.group = group;
    }

    public void paint(Graphics g) {
        this.g = g;
        switch (dir) {
            case UP:
            	image = type==1?Picture.FishR:Picture.FishL;
                break;
            case DOWN:
            	image = type==1?Picture.FishR:Picture.FishL;
                break;
            case LEFT:
            	type = -1;
            	image = type==1?Picture.FishR:Picture.FishL;
                break;
            case RIGHT:
            	type = 1;
            	image = type==1?Picture.FishR:Picture.FishL;
                break;
        }
        
        g.drawImage(image, x, y,width,height, null);
    }

	public void setDir(Dir dir) {
        this.dir = dir;
    }

    public void setMoving(boolean moving) {
        isMoving = moving;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    public String getGroup() {
        return group;
    }

    public void setGroup(String group) {
        this.group = group;
    }

    public boolean isLiving() {
        return isLiving;
    }

    public void setLiving(boolean living) {
        isLiving = living;
    }

    public int size() {
        return 0;
    }
    
    public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public int getHeight() {
		return height;
	}

	public void setHeight(int height) {
		this.height = height;
	}

}

实例化这个鱼

private void initMyFish() {
	myFish = new Fish(0, 320, Dir.RIGHT, this, "GOOD");		
}

在构造方法中调用

//构造里面初始化相关参数
public GamePanel(JFrame frame){
	this.setLayout(null);
	mainFrame = frame;
	
	//菜单
	initMenu();
	
	//创建我的鱼
	initMyFish();
}

在paint方法中绘制

@Override
public void paint(Graphics g) {
	super.paint(g);
	//绘制背景图
	g.drawImage(Picture.bg0, 0,0,null);
	//绘制鱼
    if(myFish!=null) myFish.paint(g);
}

运行如下(左方出现我方小鱼): 在这里插入图片描述

绘制敌方鱼

创建坏鱼类

package com.model;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import com.utils.Picture;
import com.view.GamePanel;

public class BadFish {
	private int x, y, width = 50, height = 50, speed = 1;
	private boolean isMoving = false, isLiving = true;
	private GamePanel panel;// 相互持有
	private String group;
	private BufferedImage image;
	private int large = 0;// 0表示小的,1表示大一号
	private int type = 0;// 1-18 表示不同的鱼
	private List imgList = null;

	public BadFish(GamePanel panel, String group) {
		this.panel = panel;
		this.group = group;
		
		init();

	}

	private void init() {
		this.x=1180;
    	Random random = new Random();
    	this.type= random.nextInt(18)+1;
    	this.large = random.nextInt(2)==0? 0:1;
    	if(large==1){
			imgList = Picture.badFishImages1;
		}else {
			imgList = Picture.badFishImages;
		}
    	image = (BufferedImage)imgList.get(type-1);
        width = image.getWidth();
        height = image.getHeight();
      	y = random.nextInt(590-height);
	}

	public void paint(Graphics g) {
		if (!isLiving) {
			 ArrayList<BadFish> badFishs = panel.badFishss;
			 badFishs.remove(this);
			return;
		}
		g.drawImage(image, x, y, width, height, null);
	}
	

	public void remove() {
		this.isMoving=false;
		this.isLiving=false;
		panel.badFishss.remove(this);
	}
	
	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public int getHeight() {
		return height;
	}

	public void setHeight(int height) {
		this.height = height;
	}
	public boolean isLiving() {
		return isLiving;
	}

	public void setLiving(boolean isLiving) {
		this.isLiving = isLiving;
	}
	
}

实例化,并调用

//构造里面初始化相关参数
public GamePanel(JFrame frame){
	this.setLayout(null);
	mainFrame = frame;
	
	//菜单
	initMenu();
	
	//创建我的鱼
	initMyFish();
	
	//创建敌鱼
	initBadFishs();
}
//创建坏鱼
private void createBadFish(){
	BadFish badFish = new BadFish(this,"BAD");
    badFishss.add(badFish) ;
}
//初始创建6只
private void initBadFishs() {
    for (int i = 0; i < 6; i++) {
    	createBadFish();
    }
}

在paint方法中绘制

@Override
	public void paint(Graphics g) {
    	super.paint(g);
    	//绘制背景图
    	g.drawImage(Picture.bg0, 0,0,null);
    	//绘制鱼
        if(myFish!=null) myFish.paint(g);
        
        //绘制敌方鱼
        for (int i = 0; i < badFishss.size(); i++) {
        	 BadFish fish = badFishss.get(i);
        	 if(fish!=null)
        		 fish.paint(g);
		}
    }

在坏鱼类中加入移动方法move

private void move() {
	new Thread(new Runnable() {
		@Override
		public void run() {
			while (isLiving) {
				
				
				x -= speed;
				if (x + width < 0) {
					x = 1180;
					y = new Random().nextInt(590 - height);
				}
				
				try {
					Thread.sleep(20);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				if(!isLiving){
					break;
				}
			}
		}
	}).start();
}

加入主线程新绘制

//游戏线程,用来自动下移
private class GameThread implements Runnable {
	@Override
	public void run() {
		while (true) {
			if("start".equals(gameFlag)){
				repaint();
			}
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

记得构造中调用哦

//启动主线程
new Thread(new GameThread()).start();

运行如下: 在这里插入图片描述

加入更多的鱼

在paint加入代码,定时的创建鱼,并且控制鱼的数量不超过60

if("start".equals(gameFlag)){
	if(time==30){
		time=0;
		if(badFishss.size()<60){
			createBadFish();
		}
	}else {
		time++;
	}
}

运行如下图: 在这里插入图片描述

加入键盘移动事件

记得在构造中调用这个方法哦。

//添加键盘监听
private void createKeyListener() {
	KeyAdapter l = new KeyAdapter() {
		//按下
		@Override
		public void keyPressed(KeyEvent e) {
			int key = e.getKeyCode();
			switch (key) {
				//向上
				case KeyEvent.VK_UP:
				case KeyEvent.VK_W:
					if(myFish!=null){
						myFish.move(Dir.UP);
					}
					break;
					
				//向右	
				case KeyEvent.VK_RIGHT:
				case KeyEvent.VK_D:
					if(myFish!=null){
						myFish.move(Dir.RIGHT);
					}
					break;
					
				//向下
				case KeyEvent.VK_DOWN:
				case KeyEvent.VK_S:
					if(myFish!=null){
						myFish.move(Dir.DOWN);
					}
					break;
					
				//向左
				case KeyEvent.VK_LEFT:
				case KeyEvent.VK_A:
					if(myFish!=null){
						myFish.move(Dir.LEFT);
					}
					break;
			}
		
		}
		//松开
		@Override
		public void keyReleased(KeyEvent e) {
		}
		
	};
	//给主frame添加键盘监听
	mainFrame.addKeyListener(l);
}

在我方鱼 Fish类 加入move方法

public void move(Dir dir) {
    	this.setDir(dir);
    	this.setMoving(true);
         
        if (!isMoving) {
            return;
        }
        
        switch (dir) {
            case UP:
                y -= speed;
                if(y<=0){
                	y=0;
                }
                break;
            case DOWN:
                y += speed;
                if(y>=590-height){
                	y=590-height;
                }
                break;
            case LEFT:
                x -= speed;
                if(x<=0){
                	x=0;
                }
                break;
            case RIGHT:
                x += speed;
                if(x>=1180-width){
                	x=1180-width;
                }
        }
    }

此时我方小鱼用键盘可以控制移动。 在这里插入图片描述

处理鱼碰撞的问题

判断我鱼与敌鱼的碰撞,因为图片是方形的,我们拿出其中一个鱼的4个顶点坐标,分别去与另外一个鱼做判断,只要有一个点在范围内就表示已经碰撞了。 注意:碰撞的问题,在 飞机大战 讲的很清楚,也画了图来说明,可以去翻一下,原理是一样的。

public boolean isHit(Fish fish,BadFish badFish) {
	if(isHit1(fish, badFish)){//如果在碰撞范围内
		if(fish.getX()<badFish.getX()){//鱼在敌鱼的左边
			if(fish.getY()>badFish.getY()){//鱼在敌鱼的下方
				if((fish.getY()+10<badFish.getY()+badFish.getHeight())
						&& (fish.getX() + fish.getWidth()>badFish.getX()+10)){
					return true;
				}
			}else if(fish.getY()<badFish.getY()){//鱼在敌鱼的上方
				if(badFish.getY()+10<fish.getY()+fish.getHeight()
						&&fish.getX() + fish.getWidth()>badFish.getX()+10){
					return true;
				}
			}
		}else if(fish.getX()>badFish.getX()){//鱼在敌鱼的左边
			if(fish.getY()>badFish.getY()){//鱼在敌鱼的下方
				if(fish.getY()+10<badFish.getY()+badFish.getHeight()
						&& badFish.getX() + badFish.getWidth()>fish.getX()+10){
					return true;
				}
			}else if(fish.getY()<badFish.getY()){//鱼在敌鱼的上方
				if(badFish.getY()+10<fish.getY()+fish.getHeight()
						&& badFish.getX() + badFish.getWidth()>fish.getX()+10){
					return true;
				}
			}
		}
	}
	return false;
}
//判断鱼是否碰撞
public boolean isHit1(Fish fish,BadFish badFish) {
	//方式1
	//左上角
	int x1 = badFish.getX();
	int y1 = badFish.getY();
	//右上角
	int x2 = badFish.getX()+badFish.getWidth();
	int y2 = badFish.getY();
	//右下角
	int x3 = badFish.getX()+badFish.getWidth();
	int y3 = badFish.getY()+badFish.getHeight();
	//左下角
	int x4 = badFish.getX();
	int y4 = badFish.getY()+badFish.getHeight();
	//只要有一个点在范围内,则判断为碰撞
	if(comparePointFish(x1,y1,fish)|| comparePointFish(x2,y2,fish)
			||comparePointFish(x3,y3,fish)||comparePointFish(x4,y4,fish) ){
		return true;
	}
	
	//方式1没成立则用方式2判断
	//方式2
	x1 = fish.getX();
	y1 = fish.getY();
	//右上角
	x2 = fish.getX()+fish.getWidth();
	y2 = fish.getY();
	//右下角
	x3 = fish.getX()+fish.getWidth();
	y3 = fish.getY()+fish.getHeight();
	//左下角
	x4 = fish.getX();
	y4 = fish.getY()+fish.getHeight();
	if(comparePoint(x1,y1,badFish)|| comparePoint(x2,y2,badFish)||comparePoint(x3,y3,badFish)||comparePoint(x4,y4,badFish) ){
		return true;
	}
	return false;
}
//用坐标来判断
private boolean comparePointFish(int x,int y,Fish fish){
	//大于左上角,小于右下角的坐标则肯定在范围内
	if(x>fish.getX() && y >fish.getY()
		&& x<fish.getX()+fish.getWidth() && y <fish.getY()+fish.getHeight()	){
		return  true;
	}
	return false;
}
//用坐标来判断
private boolean comparePoint(int x,int y,BadFish badFish){
	//大于左上角,小于右下角的坐标则肯定在范围内
	if(x>badFish.getX() && y >badFish.getY()
		&& x<badFish.getX()+badFish.getWidth() && y <badFish.getY()+badFish.getHeight()){
		return  true;
	}
	return false;
}

另外,上述代码中有看到 +10 像素这样的代码,是为了让碰撞看起来更真实,做的处理,因为鱼对于方形的图片来说,边角还是有很多空缺的。

吃敌鱼处理

在Fish类中加入 eat方法,吃了后会变大。

private void eat() {
	ArrayList<BadFish> badFishList = panel.badFishss;
	BadFish badFish;
	for (int i = 0; i < badFishList.size(); i++) {
		badFish = badFishList.get(i);
		if(!badFish.isLiving()) continue ;
		if(panel.isHit(this, badFish)){//鱼碰撞
			if(this.height>=badFish.getHeight()){//吃掉敌鱼
				System.out.println("吃掉敌鱼");
				System.out.println("this.width==="+this.width);
				//鱼变大一点
				if(this.width<150){
					this.height+=2;
    				this.width +=2*1.66;
				}
				//敌鱼消失
				badFish.remove();
			}else {//被吃掉弹出失败
				System.out.println("被敌鱼吃掉");
				
				panel.myFish.isMoving=false;
				panel.myFish.isLiving=false;
				panel.myFish=null;
				
				panel.gameOver();
			}
			break;
		}
	}
}

在 BadFish类中加入碰撞方法,如果敌鱼较大,则游戏失败,反过来我鱼要变大。

private void hit(){
	Fish fish = panel.myFish;
	if(fish==null) return;
	if(!isLiving) return ;
	
	if(panel.isHit(fish, this)){//鱼碰撞
		if(fish.getHeight()>=this.getHeight()){//吃掉敌鱼
			System.out.println("吃掉敌鱼....");
			//鱼变大一点
			if(fish.getWidth()<150){
				fish.setHeight(fish.getHeight()+2);
				fish.setWidth(fish.getWidth()+3);
			}
			//敌鱼消失
			this.remove();
		}else {//被吃掉弹出失败
			System.out.println("被敌鱼吃掉....");
			fish.setMoving(false);
			fish.setLiving(false);
			panel.myFish=null;
			
			panel.gameOver();
		}
	}
}

运行效果: 在这里插入图片描述 做到这里就基本完成了,加入其他一下辅助的东西就行了,比如重新开始什么的,也就不多说了。

看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏,能【 关注 】一波就更好了。

代码获取方式:

帮忙文章【点赞】 +【 收藏】+【关注】+【评论】 后,加V:qq283582761,我发给你!

更多精彩

1. Java俄罗斯方块
2. Java五子棋小游戏
3. 老Java程序员花一天时间写了个飞机大战
4. Java植物大战僵尸
5. 老Java程序员花2天写了个连连看
6. Java消消乐(天天爱消除)
7. Java贪吃蛇小游戏
8. Java扫雷小游戏
9. Java坦克大战
10. Java迷宫小游戏

相关阅读

1. JavaWeb图书管理系统
2. JavaWeb学生宿舍管理系统
3. JavaWeb在线考试系统