阅读 393

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

作者简介

作者名:编程界明世隐

简介: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在线考试系统

文章分类
后端
文章标签