作者简介
作者名:编程界明世隐 简介: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 就来了。
效果图
实现思路
因为做的是简单版本的,所以思路也很简单。
- 绘制窗口。
- 创建菜单
- 创建我方鱼
- 创建敌鱼
- 键盘事件监听
- 创建主线程,重新绘制界面,并且定时创建敌鱼。
- 处理其他细
代码实现
创建窗口
首先创建一个游戏窗体类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);
}
}
创建面板容器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);
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);
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;
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;
private int type = 0;
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) ;
}
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) {
}
};
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) {
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;
}
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();
}
}
}
运行效果: 做到这里就基本完成了,加入其他一下辅助的东西就行了,比如重新开始什么的,也就不多说了。
看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏,能【 关注 】一波就更好了。
代码获取方式:
帮忙文章【点赞】 +【 收藏】+【关注】+【评论】 后,加我主页左边的微信 或者 私聊我,我发给你!
更多精彩
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在线考试系统
另外
为了帮助更多小白从零进阶 Java 工程师,从CSDN官方那边搞来了一套 《Java 工程师学习成长知识图谱》,尺寸 870mm x 560mm,展开后有一张办公桌大小,也可以折叠成一本书的尺寸,原件129元现价 29 元,先到先得,有兴趣的小伙伴可以了解一下!
|