我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java实现年兽大作战游戏详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java实现年兽大作战游戏详解

前言

春节要到了,看惯了前端各种小游戏,确实做得很好,很精致。但是我也要为后端程序员稍微做一点贡献,做一款java版本的【年兽大作战】。

这个游戏加上编写文章,上班摸鱼时间加上回家的空闲时间,大概花了三天多。

java写这玩应真的很痛苦,各种状态位,各种图片和逻辑判断,脑袋都快炸了。而且肯定没有前端的精致,效果一般,偶尔会有卡顿,各位就图一乐,随便捧捧场啊。过程大于结果。

源码gitee地址

一、玩法介绍

进入初始界面,会看到一只大年兽位于正中间,然后是一直小老虎,也就是我们的玩家,点击【空格】即可开始游戏:

敲击空格,将进入游戏。从上至下分别是:

  • 年兽的血量【NIAN'S HP】
  • 移动的年兽
  • 最下方的小老虎【玩家】

玩家通过【←】【→】键移动小老虎方向,使用【S】键发射炮弹:

当击中年兽后,会有烟花出现在背景:

每击中年兽三次,年兽会扔下炸弹:

如果玩家被击中,则直接【game over】 ,通过【空格】键重新开始:

当每击中年兽10次,其血量就会减少一个,年兽会随机扔下不同种类的爆竹,当前是11种,玩家可以移动方向键获取:

当玩家成功接到炮弹后,再次击中年兽,会更换背景烟花的种类。原本我想把子弹也换了,后来是实在整不动了。我玩了半天,想截个图,半天没成功,给自己心态玩崩了,就是下面的烟火:

当把年兽击败后,会出现新年快乐的字样:

上述就是全部玩法了,其实可以有更多扩展的,java写这东西实在写的太痛苦了。

二、代码介绍

效果不太好,但是学学代码实现总是好的吧,下面我简单说说怎么实现的。

2.1 程序入口【Frame】

使用Frame作为界面的基础和入口,可以设置大小,标题,展示位置等等,最主要的再次基础上添加一个面板,是我们游戏的实现:

public static void main(String[] args) {
    //1.创建窗口对象
    Frame frame = new Frame("年兽大作战");
    // 设置窗体大小为900x800
    frame.setSize(900, 800);
    // 设置窗体为居中格式
    frame.setLocationRelativeTo(null);
    // 设置窗体不可改变
    frame.setResizable(false);
    // 在窗体中添加一个面板
    frame.add(new GamePanel());
    // 设置窗体可见
    frame.setVisible(true);

    // 窗口点击关闭
    frame.addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosing(WindowEvent arg0) {
            System.exit(0);
        }
    });
}

2.2 构造器【GamePanel】

第一步,定义一个空参构造,需要添加焦点事件,和键盘事件监听,定时器启动页面刷新,后面后有定时器的创建:

public GamePanel() {
    // 获取焦点事件
    this.setFocusable(true);
    // 添加键盘监听事件
    this.addKeyListener(this);
    // 启动定时器
    timer.start();
}

2.3 游戏逻辑实现【GamePanel】

在启动类当中,我们在Frame当中添加了一个GamePanel,作用是后面游戏的所有内容展现都在其中,包括页面,游戏逻辑等。

代码较为复杂,我只说关键点,全部代码在全篇开头的gitee链接,感兴趣自己获取。

class GamePanel extends JPanel implements KeyListener, ActionListener

如上所示,GamePannel继承了JPanel,同时实现了KeyListener和ActionListener。

  • JPanel

这是jdk提供的,使用java进行绘图的基础容器。面板不会向除其自身背景以外的任何内容添加颜色。但是,您可以轻松地为它们添加边框,并以其他方式自定义它们的绘画。

  • KeyListener

这个接口是用来监听键盘事件的接口,提供一下几个方法:

public interface KeyListener extends EventListener {

    
    public void keyTyped(KeyEvent e);

    
    public void keyPressed(KeyEvent e);

    
@SneakyThrows
@Override
public void keyPressed(KeyEvent e) {
    // 添加按钮下压事件到set
    InitProcessor.keys.add(e.getKeyCode());
    // 遍历执行按钮事件
    multiKeys();
}

在执行一个遍历方法,不断地去执行业务逻辑判断:

public void multiKeys() {
    for (Integer key : InitProcessor.keys) {
        int keyCode = key;
        //空格键
        if (keyCode == KeyEvent.VK_SPACE) {
            
        }
        // 方向左键
        else if (keyCode == KeyEvent.VK_LEFT) {
            
        } 
        // 射击
        else if (keyCode == KeyEvent.VK_S) {
                
        }
    }
}

然后在我们释放按键的时候,使用如下的方式将这个set的key释放掉:


@Override
public void keyReleased(KeyEvent e) {
    //按钮释放,则将该事件移除
    InitProcessor.keys.remove(e.getKeyCode());
}

动作监听器【ActionListener】

这个是整个画面能够动态呈现的引擎,我们使用定时器的方式,每到定时时间则会监听到动作事件,进行数据逻辑判断。

其接口如下:

public interface ActionListener extends EventListener {

    
    public void actionPerformed(ActionEvent e);

}

定时器定义:


private Timer timer = new Timer(15, this);

接口actionPerformed部分代码展示:

    
    @Override
    public void actionPerformed(ActionEvent e) {

        // 当前年兽向右移动的情况
        if (InitProcessor.LEFT.equals(InitProcessor.moveDirection)) {
            // 被击中,换方向
            if (InitProcessor.hit) {
                InitProcessor.moveDirection = InitProcessor.RIGHT;
            }
            // 判断移动到边界
            if (InitProcessor.nian_x > 30) {
                InitProcessor.nian_x -= InitProcessor.moveSpeed * 2;
            } else {
                InitProcessor.moveDirection = InitProcessor.RIGHT;
                InitProcessor.nian_x += InitProcessor.moveSpeed * 2;
            }

        } else {
            // 被击中,换方向
            if (InitProcessor.hit) {
                InitProcessor.moveDirection = InitProcessor.LEFT;
            }
            // 当前年兽向左移动的情况
            // 判断移动到边界
            if (InitProcessor.nian_x < 640) {
                InitProcessor.nian_x += InitProcessor.moveSpeed * 2;
            } else {
                InitProcessor.moveDirection = InitProcessor.LEFT;
                InitProcessor.nian_x -= InitProcessor.moveSpeed * 2;
            }
        }
        
        
        //设置烟火的展示时间,定时器刷新50次,不准确,但是至少能明显感受到烟花存在
        if (InitProcessor.hitShow == 50) {
            InitProcessor.hit = false;
            InitProcessor.hitShow = 0;
        }
        // 自增展示次数
        InitProcessor.hitShow++;
        // 刷新页面
        repaint();
        timer.start();//启动计时器
    }

到以上为止,按键事件,和定时器事件都完成了,可以说全部的逻辑判断都在上面去实现。下面我们关注在图像是如何出现的。

图像展示基础【JComponent】

前面我们似乎没有看到这个组件的身影,那么它是在哪里呢?看下面的类图:

如上所示,GamePanel继承JPanel,而JPanel又继承了JComponent。JComponent有一个方法我们需要重写,这也就是我们实现图像展示的方法,其提供了绘制UI的能力,我们重写即可,部分代码如下:


@Override
protected void paintComponent(Graphics g) {
    // 清屏效果
    super.paintComponent(g);
    // 游戏未开始
    if (!InitProcessor.isStared) {
        background.paintIcon(this, g,0,0);
        InitProcessor.nian.paintIcon(this, g, 250, 130);
        InitProcessor.tiger.paintIcon(this, g, 220, 470);
        // 绘制首页
        // 设置游戏文字
        g.setColor(Color.ORANGE);
        g.setFont(new Font("幼圆", Font.BOLD, 50));
        g.drawString("年兽大作战", 325, 550);
        // 设置开始提示
        g.setColor(Color.GREEN);
        g.setFont(new Font("幼圆", Font.BOLD, 30));
        g.drawString("按【空格】键开始游戏", 300, 620);
        g.drawString("按【←】【→】键移动", 300, 660);
        g.drawString("按【S】键发射炮弹", 300, 700);
    } else if (isGameOver) {
        //输出gameover
        InitProcessor.gameOver.paintIcon(this, g, 10, 10);
        // 设置开始提示
        g.setColor(Color.GREEN);
        g.setFont(new Font("幼圆", Font.BOLD, 20));
        g.drawString("按【空格】再次开始游戏", 340, 600);
    }
}

关键点是使用Graphics绘制文字,背景,颜色等等内容。

图片需要使用ImageIcon类来进行绘画,我将ImageIcon初始化部分封装了,所以上面没显示,常规使用如下:

ImageIcon nian = new ImageIcon(PATH_PREFIX + "nian.png");
nian.paintIcon(this, g, 250, 130);

2.4 游戏的血液【InitProcessor】

为什么这么说是血液呢?因为这个类是我自己实现的一个初始化类,其中的内容是串联整个游戏的关键点,像身体的血液一样。

通过写这个游戏,我发现最关键的点在于【状态】,可以说全部的页面动画展示都在于一个状态,无论是子弹的运动,年兽的运动,包括礼花图片的切换,以及各种图片的坐标等等。

所以我专门抽象了这个类,用于各种状态的初始化,部分代码如下:


public class InitProcessor {

    
    public static Boolean isStared = false;

    
    public static Boolean isStopped = false;


    
    public static int youWillBeKill_x = 0;

    
    public static int youWillBeKill_y = nian_y + 200;


    
    public static Boolean showYouWillBeKill = false;

    public static Boolean isGameOver = false;

    
    public final static String PATH_PREFIX = "class="lazy" data-src/main/java/com/wjbgn/nianfight/pic/";


    public static ImageIcon nian = new ImageIcon(PATH_PREFIX + "nian.png");

    public static ImageIcon tiger = new ImageIcon(PATH_PREFIX + "tiger\tiger2.png");

    public static ImageIcon heart = new ImageIcon(PATH_PREFIX + "blood\heart.png");

    
    public static List<FireworksDO> fireworksDOS = initFireworks();

    
    public static List<FlowersDO> flowersDOS = initFlowers();


    
    private static List<FlowersDO> initFlowers() {
        List<FlowersDO> list = new ArrayList<>();
        list.add(new FlowersDO(1, PATH_PREFIX + "flowers\flower1.png"));
        list.add(new FlowersDO(2, PATH_PREFIX + "fireworks\flower2.png"));
        list.add(new FlowersDO(3, PATH_PREFIX + "fireworks\flower3.png"));
        list.add(new FlowersDO(4, PATH_PREFIX + "fireworks\flower4.png"));
        list.add(new FlowersDO(5, PATH_PREFIX + "fireworks\flower5.png"));
        list.add(new FlowersDO(6, PATH_PREFIX + "fireworks\flower6.png"));
        list.add(new FlowersDO(7, PATH_PREFIX + "fireworks\flower7.png"));
        list.add(new FlowersDO(8, PATH_PREFIX + "fireworks\flower8.png"));
        list.add(new FlowersDO(9, PATH_PREFIX + "fireworks\flower9.png"));
        list.add(new FlowersDO(10, PATH_PREFIX + "fireworks\flower10.png"));
        list.add(new FlowersDO(11, PATH_PREFIX + "fireworks\flower11.png"));
        return list;
    }


    
    public static List<FireworksDO> initFireworks() {
        List<FireworksDO> list = new ArrayList<>();
        list.add(new FireworksDO(1, PATH_PREFIX + "fireworks\fireworks1.png"));
        list.add(new FireworksDO(2, PATH_PREFIX + "fireworks\fireworks2.png"));
        list.add(new FireworksDO(3, PATH_PREFIX + "fireworks\fireworks3.png"));
        list.add(new FireworksDO(4, PATH_PREFIX + "fireworks\fireworks4.png"));
        list.add(new FireworksDO(5, PATH_PREFIX + "fireworks\fireworks5.png"));
        list.add(new FireworksDO(6, PATH_PREFIX + "fireworks\fireworks6.png"));
        list.add(new FireworksDO(7, PATH_PREFIX + "fireworks\fireworks7.png"));
        list.add(new FireworksDO(8, PATH_PREFIX + "fireworks\fireworks8.png"));
        list.add(new FireworksDO(9, PATH_PREFIX + "fireworks\fireworks9.png"));
        list.add(new FireworksDO(10, PATH_PREFIX + "fireworks\fireworks10.png"));
        list.add(new FireworksDO(11, PATH_PREFIX + "fireworks\fireworks11.png"));
        return list;
    }


    
    public static void init() {
        isStared = false;
        isStopped = false;
        attack = false;
        nian_x = 325;
        nian_y = 50;
        tiger_x = 325;
        tiger_y = 660;
        bullet_x = tiger_x + 20;
        bullet_y = tiger_y - 20;
        moveSpeed = 1;
        moveDirection = LEFT;
        hit = false;
        hitCount = 0;
        hitShow = 0;
        nianBlood = 10;
        success = false;
        keys = new HashSet<>();
        fireworks_x = 0;
        fireworks_y = nian_y + 200;
        showFireworks = false;
        currentFireworks = null;
        takeFireworks = false;
        currentFlowers = null;
        youWillBeKill = 0;
        youWillBeKill_x = 0;
        youWillBeKill_y = nian_y + 200;
        showYouWillBeKill = false;
        isGameOver = false;
    }
}

2.5 实体类【FireworksDO】【FlowersDO】

这是两个实体类,分别定义的爆竹和礼花样式,用于初始化,代码如下:

import javax.swing.*;

import static com.wjbgn.nianfight.nianshou.InitProcessor.PATH_PREFIX;


public class FlowersDO {

    private Integer id;

    private String path;

    private ImageIcon flower;

    public ImageIcon getFlower() {
        return flower;
    }

    public void setFlower(ImageIcon flower) {
        this.flower = flower;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public FlowersDO(Integer id, String path) {
        this.id = id;
        this.path = path;
        this.flower = new ImageIcon(PATH_PREFIX + "flowers\flower" + id + ".png");
    }
}

public class FireworksDO {

    
    private Integer id;

    
    private String path;

    public ImageIcon getFirework() {
        return firework;
    }

    public void setFirework(ImageIcon firework) {
        this.firework = firework;
    }

    private ImageIcon firework;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public FireworksDO(Integer id, String path) {
        this.id = id;
        this.path = path;
        this.firework = new ImageIcon(PATH_PREFIX + "fireworks\fireworks" + id + ".png");
    }
}

2.6 图片素材

游戏中使用了大量的图片素材,我在网上找了两个网站不错,一个是png图片网站,是免费的,还有一个是免费切图的,挺好用,都分享给大家

免费png图片网址(英文):www.cleanpng.com/

  • 免费切图网址(简单版直接微信关注):www.uupoop.com/

我使用的素材都在项目的pic目录下:

三、总结

写java好几年了,其实从没有使用 javax.swing 和 java.awt 包下面的内容开发过代码,对于现在用户体验为前提的大环境下,综合编码体验,和游戏运行体验来看,确实是不太友好,不太符合环境背景。但是也是一次不错的学习过程。

问题总结

目前整个游戏还是存在一些bug的,后面有时间再翻出来调试吧,此处先记录一下:

  • 子弹有时并不是从老虎正前方射出的,与实际的坐标存在偏差:此问题出现的原因我推测来自线程间共享变量的同步问题。子弹的初始横坐标取决于小老虎当前所在的横坐标,这个坐标同步没做好。
  • 关于按键切换、同时两个按键等情况造成卡顿的问题:前面解决两个按键同时按下的方案可能不是最优解,后面还需要优化。
  • 怪兽扔炸弹、爆竹随着年兽移动存在偏移:炸弹和爆竹的初始横坐标,是年兽当前的横坐标,需要一个变量记录当前年兽的位置,作为炸弹和爆竹的初始横坐标。

关于游戏就说这么多了,感兴趣的去文章开篇的gitee下载源码就行了。

以上就是Java实现年兽大作战游戏详解的详细内容,更多关于Java年兽大作战游戏的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java实现年兽大作战游戏详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java如何实现年兽大作战游戏

这期内容当中小编将会给大家带来有关Java如何实现年兽大作战游戏,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。前言春节要到了,看惯了前端各种小游戏,确实做得很好,很精致。但是我也要为后端程序员稍微做一点贡
2023-06-26

java如何实现飞机大战小游戏

本篇内容介绍了“java如何实现飞机大战小游戏”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!MyPanel类package P;impor
2023-07-01

使用java怎么实现潜艇大战游戏

使用java怎么实现潜艇大战游戏?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。代码如下package com.util;import java.awt.BorderLayo
2023-06-15

怎么用JAVA实现经典游戏坦克大战

这篇文章主要介绍“怎么用JAVA实现经典游戏坦克大战”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么用JAVA实现经典游戏坦克大战”文章能帮助大家解决问题。主要设计1、要有难度关卡:第一关,第二关
2023-06-29

Android 实现抖音小游戏潜艇大挑战的思路详解

《潜水艇大挑战》是抖音上的一款小游戏,以面部识别来驱动潜艇通过障碍物,最近特别火爆,相信很多人都玩过。一时兴起自己用Android自定义View也撸了一个,发现只要有好的创意,不用高深的技术照样可以开发出好玩的应用。开发过程现拿出来与大家分
2022-06-06

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录