菜鸟之路day02-04拼图小游戏开发一一JAVA基础综合项目

简介: 本项目基于黑马程序员教程,涵盖面向对象进阶、继承、多态等知识,历时约24小时完成。项目去除了登录和注册模块,专注于单机游戏体验。使用Git进行版本管理,代码托管于Gitee。项目包含窗体搭建、事件监听、图片加载与打乱、交互逻辑实现、菜单功能及美化界面等内容。通过此项目,巩固了Java基础并提升了实际开发能力。仓库地址:[https://gitee.com/zhang-tenglan/puzzlegame.git](https://gitee.com/zhang-tenglan/puzzlegame.git)

菜鸟之路day02-04拼图小游戏开发一一JAVA基础综合项目

作者:blue

时间:2025.1.20-1.22

[TOC]

0.概述

1.项目来源:黑马程序员:BV17F411T7Ao,感谢阿玮老师,讲的实在是太详细了

2.菜鸟之路day02-day04:

​ 第一天我学习了面向对象的进阶知识,继承,多态,接口,内部类,等知识

​ 第二天我开始做项目,项目完成了85%

​ 第三天上午完善了项目,总耗时估计在24h左右,包括学习知识,代码编写,版本管理,文档编写

3.在项目中,我删除了登录和注册模块,因为我认为在一个单机游戏中,这两个模块有些许突兀(不是懒,听我狡辩!)

4.由于我在菜鸟之路day01时学习了Git,所以在这个项目中,我运用了git来管理这个项目,并将源码上传到了Gitee,读者可根据我的11次提交循序渐进的学习此项目

5.在本文中我仅展示部分核心代码,读者想获取完整代码,应该去我的仓库里clone,仓库是public的

6.图片均为视频提供,里面的美女我一个都不认识(真的!!!!)

7.本项目仅为java基础练习项目,此类项目并不是java语言的特长,用前端知识也确实能写出比这个项目更好的拼图游戏,但是我们毕竟是在练习java这个后端语言嘛,所以项目有它存在的意义,理解万岁。

仓库地址:https://gitee.com/zhang-tenglan/puzzlegame.git

在第11次提交时,我才删掉了注册和登录界面,所以若读者对这两个界面有兴趣,可以git第10次提交

image-20250122104803581.png

此为我项目的结构图:

image-20250122103819089.png

1.界面搭建

1.1创建窗体

创建ui包,在包内新建三个JavaBean类,三个类均继承JFrame(窗体类)这个父类,分别代表游戏的三个界面。

定义空参构造方法:1.设置窗口宽高 2.将窗口显示设置为true,表示可视

package com.bluening.ui;

import javax.swing.*;
import java.awt.*;

//游戏主界面
public class GameJFrame extends JFrame{
   

    //空参构造,做初始化
    public GameJFrame() {
   
        setSize(603,680);
        setVisible(true);
    }
}

1.2窗体基础设置

还是以GameJFrame这个类为例

package com.bluening.ui;

import javax.swing.*;
import java.awt.*;

//游戏主界面
public class GameJFrame extends JFrame{
   

    //空参构造,做初始化
    public GameJFrame() {
   
        //设置界面宽高
        this.setSize(603,680);
        //设置界面的标题
        this.setTitle("拼图游戏单机版");
        //设置界面置顶
        this.setAlwaysOnTop(true);
        //设置界面居中
        this.setLocationRelativeTo(null);
        //设置关闭模式
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //让窗体显示
        this.setVisible(true);
    }
}

1.3创建主界面菜单

主界面与菜单结构,先建立各自对象,然后再建立对象间的联系

image-20250121090752588.png

private void initJMenuBar() {
   
        JMenuBar jMenuBar = new JMenuBar();

        JMenu functionJMenu = new JMenu("功能");
        JMenu aboutJMenu = new JMenu("关于我");


        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");

        JMenuItem myBlogItem = new JMenuItem("我的博客");

        //给菜单添加条目
        functionJMenu.add(replayItem);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);

        aboutJMenu.add(myBlogItem);

        //给菜单栏添加JMenu
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);

        //给窗体添加菜单栏
        this.setJMenuBar(jMenuBar);
    }

1.4添加图片素材,并将图片素材加载到GameJFrame中

结构:

image-20250121102953472.png

private void initImage() {
   
        int number = 1;
        for (int i=0;i<4;i++){
   
            for (int j=0;j<4;j++){
   
                //创建ImageIcon对象
                ImageIcon icon = new ImageIcon("image/animal/animal3/"+number+".jpg");

                //创建JLabel对象(JLabel一个管理容器)
                JLabel jLabel = new JLabel(icon);

                //指定图片位置
                jLabel.setBounds(105*j,105*i,105,105);

                //把JLabel添加到界面当中
                getContentPane().add(jLabel);

                //加完一张添加下一个图片
                number++;
            }
        }
    }

1.5打乱图片

创建了随机生成二维数组的方法,利用随机二维数组,打乱图片

private void initData() {
   
        int[] tempArr = {
   0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        //打乱
        Random rd = new Random();
        for(int i=0;i<tempArr.length;i++){
   
            int index = rd.nextInt(tempArr.length);
            int temp = tempArr[index];
            tempArr[index] = tempArr[i];
            tempArr[i] = temp;
        }

        for (int i = 0; i < tempArr.length; i++) {
   
            data[i/4][i%4] = tempArr[i];
        }
    }

2.如何实现交互

2.1事件

​ ①事件源:按钮 图片 窗体

​ ②事件:某些操作 如:鼠标单击,鼠标划入

​ ③绑定监听:当事件源上发生了某件事,则执行某段代码

​ KeyListener:键盘监听

​ MouseListener:鼠标监听

​ ActionListener:动作监听

2.4实现事件监听的两种方式

2.4.1匿名内部类
package Test;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

/*
    在这个代码中,我将以匿名内部类的方式实现事件监听
*/
public class ActionListenerTest {
   
    public static void main(String[] args) {
   
        //创建一个窗体对象
        JFrame jFrame = new JFrame();
        //设置界面宽高
        jFrame.setSize(603, 680);
        //设置界面的标题
        jFrame.setTitle("拼图游戏单机版");
        //设置界面置顶
        jFrame.setAlwaysOnTop(true);
        //设置界面居中
        jFrame.setLocationRelativeTo(null);
        //设置关闭模式
        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //取消默认的居中放置
        jFrame.setLayout(null);

        //设置按钮
        JButton jb1 = new JButton("点我吧");
        jb1.setBounds(0,0,105,105);

        jb1.addActionListener(
                //匿名内部类
                new ActionListener() {
   
                    @Override
                    public void actionPerformed(ActionEvent e) {
   
                        Random r = new Random();
                        jb1.setLocation(r.nextInt(500),r.nextInt(500));
                    }
                }
        );

        jFrame.getContentPane().add(jb1);

        //让窗体显示
        jFrame.setVisible(true);
    }
}
2.4.2本类作为事件监听接口的实现类
package Test;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

//本类用来测试事件监听
//我们重写接口ActionListener中的actionPerformed,自己就是addActionListener的实现类
public class MyJFrame extends JFrame implements ActionListener {
   

    //设置一个按钮
    JButton jb1 = new JButton("点我啊");
    JButton jb2 = new JButton("我是另一个按钮");

    public MyJFrame(){
   
        //设置界面宽高
        this.setSize(603,680);
        //设置界面的标题
        this.setTitle("拼图游戏单机版");
        //设置界面置顶
        this.setAlwaysOnTop(true);
        //设置界面居中
        this.setLocationRelativeTo(null);
        //设置关闭模式
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //取消默认的居中放置
        this.setLayout(null);

        jb1.setBounds(0,0,105,105);
        jb1.addActionListener(this);//自己就是addActionListener的实现类

        jb2.setBounds(200,200,105,105);
        jb2.addActionListener(this);

        //将按钮加入窗体中
        this.getContentPane().add(jb1);
        this.getContentPane().add(jb2);

        //让窗体显示
        this.setVisible(true);
    }

    //重写接口中的方法
    @Override
    public void actionPerformed(ActionEvent e) {
   
        //判断当前按钮是谁

        //获取被点击的按钮对象
        Object jb = e.getSource();
        if(jb==jb1){
   
            jb1.setSize(200,200);
        } else if (jb==jb2) {
   
            Random rd = new Random();
            jb2.setLocation(rd.nextInt(500),rd.nextInt(500));
        }
    }
}

3.美化界面

针对GameJFrame类中的initImage方法做一个界面美化

private void initImage() {
   

        //先加载的图片在上面,后加载的图片在下面
        for (int i=0;i<4;i++){
   
            for (int j=0;j<4;j++){
   

                //获取图片编号
                int number = data[i][j];

                //创建ImageIcon对象
                ImageIcon icon = new ImageIcon("image/animal/animal3/"+number+".jpg");

                //创建JLabel对象(JLabel一个管理容器)
                JLabel jLabel = new JLabel(icon);

                //指定图片位置
                jLabel.setBounds(105*j+83,105*i+134,105,105);

                //给图片添加边框
                jLabel.setBorder(new BevelBorder(BevelBorder.LOWERED));

                //把JLabel添加到界面当中
                this.getContentPane().add(jLabel);

            }
        }

        //添加背景图片
        JLabel Background = new JLabel(new ImageIcon("image/background.png"));
        Background.setBounds(40,40,508,560);
        this.getContentPane().add(Background);
    }

4.实现图片移动业务逻辑

4.1图片根据方向键移动

步骤:

​ 1.本类实现KeyListener接口,并重写所有抽象方法

​ 2.给整个界面添加键盘监听事件

​ 3.统计一下空白方块对应的数字0在二维数组中的位置

​ 4.在KeyReleased方法当中实现移动的逻辑

@Override
    public void keyReleased(KeyEvent e) {
   
        //实现键盘方向键控制移动,其实就是交换[0][0]位置,及其周边位置的图片
        int code = e.getKeyCode();
        if(code==37){
   //左
            if(y+1<4){
   
                data[x][y]=data[x][y+1];
                data[x][y+1]=0;
                y++;
                initImage();
            }
        }
        else if(code==38){
   //上
            if(x+1<4){
   
                data[x][y]=data[x+1][y];
                data[x+1][y]=0;
                x++;
                initImage();
            }
        }
        else if(code==39){
   //右
            if(y-1>=0){
   
                data[x][y]=data[x][y-1];
                data[x][y-1]=0;
                y--;
                initImage();
            }
        }
        else if(code==40){
   //下
            if(x-1>=0){
   
                data[x][y]=data[x-1][y];
                data[x-1][y]=0;
                x--;
                initImage();
            }
        }
    }

4.2查看全图功能

在代码中,我设置按住ctrl键不松,就可以查看原图

@Override
    //这里我们实现的逻辑是按住ctrl不松,查看完整的图片
    public void keyPressed(KeyEvent e) {
   
        if(victory()){
   
            return;
        }

        int code = e.getKeyCode();
        if(code==17){
   
            //清空图片
            this.getContentPane().removeAll();
            //创建对象
            JLabel jLabel = new JLabel(new ImageIcon(path+"all.jpg"));
            jLabel.setBounds(83,134,420,420);
            this.getContentPane().add(jLabel);
            //添加背景图片
            JLabel Background = new JLabel(new ImageIcon("image/background.png"));
            Background.setBounds(40,40,508,560);
            this.getContentPane().add(Background);

            //刷新
            this.getContentPane().repaint();
        }
    }

4.3作弊码

我设置了按m,一键通关,原理是改变data数组,然后重新初始化图片

//作弊码,一件通关
        else if(code==77){
   
            data = new int[][]{
   
                    {
   1,2,3,4},
                    {
   5,6,7,8},
                    {
   9,10,11,12},
                    {
   13,14,15,0}
            };
            initImage();
        }

4.4判断胜利

利用一个按顺序的win数组和data数组比对,如果完全一样的话,则打印出胜利,并禁止移动

//判断胜利
    public boolean victory(){
   
        for(int i=0;i<data[0].length;i++){
   
            for(int j=0;j<data.length;j++){
   
                if(win[i][j]!=data[i][j]){
   
                    return false;
                }
            }
        }
        return true;
    }

4.4计数器

设置step变量,每按一步自增一次,让其显示出来

//计数器
        JLabel stepCount = new JLabel("步数:"+step);
        stepCount.setBounds(50,30,100,20);
        this.getContentPane().add(stepCount);

5.菜单业务实现

为条目设置动作监听,类继承ActionListener接口,重写其中方法,针对不同条目执行对应代码

@Override
    public void actionPerformed(ActionEvent e) {
   
        Object item = e.getSource();
        if(item==replayItem){
   //重新游戏
            initData();
            step=0;
            initImage();
        } else if (item==reLoginItem) {
   //重新登录
            this.setVisible(false);
            new LoginJFrame();
        } else if (item==closeItem) {
   //关闭
            System.exit(0);//直接关闭虚拟机
        }else if(item==myBlogItem) {
   //博客
            //创建一个弹框对象
            JDialog jDialog = new JDialog();

            JLabel jLabel = new JLabel(new ImageIcon("image/about.png"));
            //设置宽高
            jLabel.setBounds(0,0,380,539);
            //把图片添加到弹框中
            jDialog.getContentPane().add(jLabel);
            //给弹框设置大小
            jDialog.setSize(420,580);
            //让弹框置顶
            jDialog.setAlwaysOnTop(true);
            //让弹框居中
            jDialog.setLocationRelativeTo(null);
            //弹框不关闭无法操作下面界面
            jDialog.setModal(true);
            //让弹框显示出来
            jDialog.setVisible(true);
        }
    }

6.完成图片切换功能

设置更换条目,添加事件监听,随机生成,改变路径,重新加载图片

else if (item==girl) {
   //更换美女图片
            //生成随机路径
            Random rd = new Random();
            int num = rd.nextInt(8)+1;
            path = "image/girl/girl"+num+"/";
            initData();
            step = 0;
            initImage();
        }
目录
相关文章
|
16天前
|
供应链 监控 安全
对话|企业如何构建更完善的容器供应链安全防护体系
阿里云与企业共筑容器供应链安全
171339 13
|
18天前
|
供应链 监控 安全
对话|企业如何构建更完善的容器供应链安全防护体系
随着云计算和DevOps的兴起,容器技术和自动化在软件开发中扮演着愈发重要的角色,但也带来了新的安全挑战。阿里云针对这些挑战,组织了一场关于云上安全的深度访谈,邀请了内部专家穆寰、匡大虎和黄竹刚,深入探讨了容器安全与软件供应链安全的关系,分析了当前的安全隐患及应对策略,并介绍了阿里云提供的安全解决方案,包括容器镜像服务ACR、容器服务ACK、网格服务ASM等,旨在帮助企业构建涵盖整个软件开发生命周期的安全防护体系。通过加强基础设施安全性、技术创新以及倡导协同安全理念,阿里云致力于与客户共同建设更加安全可靠的软件供应链环境。
150296 32
|
26天前
|
弹性计算 人工智能 安全
对话 | ECS如何构筑企业上云的第一道安全防线
随着中小企业加速上云,数据泄露、网络攻击等安全威胁日益严重。阿里云推出深度访谈栏目,汇聚产品技术专家,探讨云上安全问题及应对策略。首期节目聚焦ECS安全性,提出三道防线:数据安全、网络安全和身份认证与权限管理,确保用户在云端的数据主权和业务稳定。此外,阿里云还推出了“ECS 99套餐”,以高性价比提供全面的安全保障,帮助中小企业安全上云。
201962 14
对话 | ECS如何构筑企业上云的第一道安全防线
|
4天前
|
机器学习/深度学习 自然语言处理 PyTorch
深入剖析Transformer架构中的多头注意力机制
多头注意力机制(Multi-Head Attention)是Transformer模型中的核心组件,通过并行运行多个独立的注意力机制,捕捉输入序列中不同子空间的语义关联。每个“头”独立处理Query、Key和Value矩阵,经过缩放点积注意力运算后,所有头的输出被拼接并通过线性层融合,最终生成更全面的表示。多头注意力不仅增强了模型对复杂依赖关系的理解,还在自然语言处理任务如机器翻译和阅读理解中表现出色。通过多头自注意力机制,模型在同一序列内部进行多角度的注意力计算,进一步提升了表达能力和泛化性能。
|
8天前
|
存储 人工智能 安全
对话|无影如何助力企业构建办公安全防护体系
阿里云无影助力企业构建办公安全防护体系
1255 10
|
11天前
|
机器学习/深度学习 自然语言处理 搜索推荐
自注意力机制全解析:从原理到计算细节,一文尽览!
自注意力机制(Self-Attention)最早可追溯至20世纪70年代的神经网络研究,但直到2017年Google Brain团队提出Transformer架构后才广泛应用于深度学习。它通过计算序列内部元素间的相关性,捕捉复杂依赖关系,并支持并行化训练,显著提升了处理长文本和序列数据的能力。相比传统的RNN、LSTM和GRU,自注意力机制在自然语言处理(NLP)、计算机视觉、语音识别及推荐系统等领域展现出卓越性能。其核心步骤包括生成查询(Q)、键(K)和值(V)向量,计算缩放点积注意力得分,应用Softmax归一化,以及加权求和生成输出。自注意力机制提高了模型的表达能力,带来了更精准的服务。
|
9天前
|
人工智能 自然语言处理 程序员
通义灵码2.0全新升级,AI程序员全面开放使用
通义灵码2.0来了,成为全球首个同时上线JetBrains和VSCode的AI 程序员产品!立即下载更新最新插件使用。
1364 24
|
9天前
|
消息中间件 人工智能 运维
1月更文特别场——寻找用云高手,分享云&AI实践
我们寻找你,用云高手,欢迎分享你的真知灼见!
694 30
1月更文特别场——寻找用云高手,分享云&AI实践
|
14天前
|
人工智能 自然语言处理 API
阿里云百炼xWaytoAGI共学课DAY1 - 必须了解的企业级AI应用开发知识点
本课程旨在介绍阿里云百炼大模型平台的核心功能和应用场景,帮助开发者和技术小白快速上手,体验AI的强大能力,并探索企业级AI应用开发的可能性。
|
8天前
|
机器学习/深度学习 人工智能 自然语言处理