Java制作拼图小游戏——基础编程实战(详细代码注释与流程讲解)

news2024/9/21 10:58:50

目录

前言

涉及知识点

准备工具

Java开发环境

图片资源

最终效果 ——需求分析

登录界面 

功能描述

需求分析

功能需求

 游戏主界面

功能描述

需求分析

功能需求

游戏菜单

游戏胜利界面

框架搭建

总结

编码

 编码顺序

搭建App实现程序的入口

完成User用户类和CodeUtil工具类的搭建

创建User用户类

创建CodeUtil工具类 

搭建登录界面

搭建游戏主界面 

关于String变量

总结


顶置有项目源代码以及图片资源,可以自行下载

前言


在学习了Java的基础编程知识之后,制作一个简单的拼图小游戏是一个很好的实战项目,它不仅能帮助我们巩固所学的内容,还能进一步提升编程逻辑和界面设计能力。本文将带领大家一步步用Java创建一个功能完整的拼图小游戏。

涉及知识点

通过这个项目,我们将复习和应用以下编程基础知识:

  • Java的面向对象编程:如何通过类和对象设计拼图的各个组件。
  • 事件处理机制:如何响应用户的拖拽操作并实时更新拼图界面。
  • 图形用户界面(GUI)编程:使用Java的Swing或JavaFX库创建用户友好的图形界面。
  • 基本算法:如何打乱拼图并判断拼图是否完成。

其中详细的知识点如下表:

运算符判断
循环数组
字符串集合
封装继承
多态接口
抽象类内部类

虽然这个小游戏没有复杂的用户注册功能,但其核心逻辑和界面设计已经能充分展示Java在桌面应用开发中的强大能力。通过这篇博文,你不仅可以熟练掌握Java编程,还能收获成就感,看到一个亲手完成的小项目成形。

准备工具


Java开发环境

  • JDK (Java Development Kit):确保你已经安装了最新的Java开发工具包(JDK),建议使用JDK 8或更高版本,因为它包含了编译和运行Java应用程序所需的所有工具。本文采用JDK17
  • IDE (集成开发环境):选择一个适合的Java开发环境,比如:
    • IntelliJ IDEA:功能强大,支持广泛的插件和代码自动补全。(推荐使用)
    • Eclipse:老牌Java IDE,社区支持丰富,适合初学者。
    • NetBeans:Oracle官方支持的IDE,内置很多Java工具,适合快速上手。

图片资源

  • 你需要制作一些图片,将其切割成若干块作为拼图的碎片。可以使用任意图像编辑工具,如 Photoshop 或 在线切割工具,将图片切割为相同大小的几部分。
  • 如果想要自己制作,准备好相应尺寸的照片,这里推荐一个图片剪切网站图片分割-九宫格切图 - 在线工具 (wqtool.com)
  • 如果不想自己制作图片,本文上方的资源image文件夹内为大家准备好了所需的图片资源(图片来源:黑马程序员

最终效果 ——需求分析


登录界面 

功能描述

  • 这是一个简单的拼图小游戏的登录界面,用户需要输入用户名密码以及验证码来进行登录。界面上包含登录和注册(本文不做实现)按钮,分别提供用户登录和新用户注册的功能。
  • 登录界面设计简洁,UI风格可爱,符合小游戏的整体风格。

需求分析

功能需求
  1. 用户输入
    • 用户名:允许用户输入字母、数字等常见字符,用于标识登录用户。(不做输入限制)
    • 密码:用于保护用户的账号安全,采用密文显示输入,防止他人窥视。
    • 验证码:随机生成一个四个字母和一个数字的验证码显示在界面右侧,用户需要正确输入显示的字符才能继续。
  2. 按钮功能
    • 登录按钮
      • 验证用户输入的用户名、密码是否正确。
      • 校验验证码输入是否与显示的验证码匹配。
      • 若匹配成功,进入游戏主界面。
      • 若用户名或密码错误,提示错误信息,用户需重新输入。
    • 注册按钮:(暂时不作实现
  3. 验证码刷新
    • 验证码生成需要是随机的,并在每次页面加载或用户点击验证码时重新生成。
    • 如果验证码输入错误,提示错误信息,用户需重新输入。

 游戏主界面

功能描述

  • 该界面是拼图游戏的主游戏界面。游戏通过将图片切割成多个方块并随机打乱,玩家通过点击或拖动方块使图片复原。
  • 游戏显示了一个4x4的拼图,其中一个空白区域允许方块移动。
  • 顶部有一个功能菜单栏,包括“功能”“关于作者”两个选项,便于玩家了解游戏信息。
  • 左上角显示了玩家当前的操作步数,方便记录和优化游戏。

需求分析

功能需求
  1. 拼图逻辑

    • 随机打乱:游戏开始时,图片应自动随机打乱成小块,保持一个空白块位置。打乱后的图片依然可被复原(不应是无解状态)。
    • 拼图复原:玩家通过按下上下左右键与空白位置相邻的方块来移动该方块,最终目标是将所有方块移动到正确的位置,还原完整图片。
  2. 用户交互

    • 移动方块:按下上下左右键移动空白位置周围的方块,方块应当移动到空白处。每次移动仅能操作空白区域周围的一个方块。
    • 步数记录:游戏界面左上角记录玩家的操作步数,每次有效的方块移动应当增加步数计数。
  3. 功能菜单

    • 功能菜单:提供不同的游戏设置或功能选项,如重新开始游戏、更改图片、重新登录、退出游戏。
    • 关于作者:点击后可以显示关于游戏作者或团队的信息。
  4. 游戏结束检测

    • 检测玩家是否成功完成拼图。一旦所有方块排列成完整的图片,游戏应提示玩家成功。
  5. 快捷键
    • 长按A键:显示完整图片,松开改键后回到游戏界面。
    • 按下W键:一键完成游戏。

游戏菜单

功能如图

游戏胜利界面

框架搭建


为了实现上述功能,我们先创建一个新的项目

进入新建好的项目,搭建如下项目框架

image文件夹直接复制粘贴到puzzlegame模块下

框架搭建好了后,思路是不是清晰了很多呢?

总结

  • 我们在GameJFrame中实现游戏的主界面搭建
  • LoginJFrame中搭建登录界面及其功能
  • RegisterJFrame实现注册界面及其功能搭建,本文不作实现
  • 最后把这些类的内容集成到App中实现游戏程序的运行(封装思想)

下面我们就要来到最最最激动人心的环节了!准备好你颤抖的双手 

编码


 编码顺序

  1. 首先搭建App实现程序的入口
  2. 完成User用户类CodeUtil工具类的搭建,方便后续登录界面搭建的使用
  3. 搭建登录界面
  4. 搭建游戏主界面

搭建App实现程序的入口

创建LoginJFrame对象,进入登录界面

实现代码:

import com.joe.ui.LoginJFrame;

public class App {
    public static void main(String[] args) {
        //创建登录界面
        LoginJFrame loginJFrame = new LoginJFrame();
    }
}

是不是超级简单呢!那下面我们将逐渐加大难度~

完成User用户类CodeUtil工具类的搭建

创建User用户类

类中定义了两个私有属性 username(用户名)password(密码)

实现代码:

package com.joe.ui;

public class User {
    private String username;
    private String password;


    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    /**
     * 获取
     * @return username
     */
    public String getUsername() {
        return username;
    }

    /**
     * 设置
     * @param username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 获取
     * @return password
     */
    public String getPassword() {
        return password;
    }

    /**
     * 设置
     * @param password
     */
    public void setPassword(String password) {
        this.password = password;
    }

}

创建CodeUtil工具类 

在这个类中我们将随机生成一个5位的验证码,包含4个字母(包含大小写)以及一个数字,排列位置随机(如:WDq6a)

  • 该方法的实现涉及静态方法、循环、集合、StringBuilder容器、生成随机数、变量数据交换、String字符串构造方法的内容,非常适合锻炼之前学习过的知识点

实现代码:

package com.joe.ui;

import java.util.ArrayList;
import java.util.Random;

public class CodeUtil {
    public static String getCode() {
        //创建一个集合容器存放字母
        ArrayList<Character> list = new ArrayList<>();
        //将小写英文字母存放进集合中
        for (int i = 0; i < 26; i++) {
            list.add((char)('a' + i));
        }
        //将大写英文字母存放进集合中
        for (int i = 0; i < 26; i++) {
            list.add((char)('A' + i));
        }

        //新建一个随机类
        Random r = new Random();

        //创建一个容器拼接字符串
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 4; i++) {
            //调用随机类方法生成随机索引
            int index = r.nextInt(list.size());
            //拼接到容器
            sb.append(list.get(index));
        }

        //最后生成一个数字
        int num = r.nextInt(10);
        //char n = (char)(num+48);
        sb.append(num);//拼接会把数字转换成字符串

        //把字符串变成数组,将数字打乱顺序
        char[] c = sb.toString().toCharArray();
        int newIndex = r.nextInt(c.length);
        char temp = c[newIndex];
        c[newIndex] = c[c.length-1];
        c[c.length-1] = temp;

        return new String(c);//参考String的构造方法
    }
}

 下面我们将继续搭建游戏登录界面,难度开始有提升了,但是不要放弃哦!

搭建登录界面

由于没有注册功能,在此类中,我们添加两个静态代码块实现用户数据的初始化,在这里你可以手动提供你自己与好朋友的账号

该界面及其功能的实现,涉及了封装、接口、多态等知识点

 实现代码:

package com.joe.ui;

import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;

public class LoginJFrame extends JFrame implements MouseListener {

    //初始用户
    static ArrayList<User> allUsers = new ArrayList<>();
    static {
        allUsers.add(new User("乔没乔见","123"));
        allUsers.add(new User("sunshi","123"));
        allUsers.add(new User("yulu","123"));
        allUsers.add(new User("leyu","123"));
        allUsers.add(new User("jixin","123"));
        allUsers.add(new User("yongzhao","123"));
    }

    //创建登录按钮
    JButton login = new JButton();
    //创建注册按钮
    JButton register = new JButton();
    

    //创建用户名输入栏
    JTextField username = new JTextField();
    //创建密码输入栏
    JPasswordField password = new JPasswordField();
    //创建验证码输入栏
    JTextField code = new JTextField();

    //正确的验证码容器
    JLabel rightCode = new JLabel();

    
    //核心:构造方法(无参)
    public LoginJFrame() {
        //初始化界面
        initJFrame();

        //在这个界面中添加内容
        initView();
        
        //让当前界面显示出来
        this.setVisible(true);
    }


    //在这个界面中添加内容
    public void initView() {
        //1. 添加用户名文字
        JLabel usernameText = new JLabel(new ImageIcon("D:\\PersonalFile\\javaProject\\puzzlegame\\image\\login\\用户名.png"));
        usernameText.setBounds(116, 135, 47, 17);
        this.getContentPane().add(usernameText);
        //添加用户名输入框
        username.setBounds(195, 134, 200, 30);
        this.getContentPane().add(username);


        //2.添加密码文字
        JLabel passwordText = new JLabel(new ImageIcon("D:\\PersonalFile\\javaProject\\puzzlegame\\image\\login\\密码.png"));
        passwordText.setBounds(130, 195, 32, 16);
        this.getContentPane().add(passwordText);
        //密码输入框
        password.setBounds(195, 195, 200, 30);
        this.getContentPane().add(password);


        //3.添加验证码提示
        JLabel codeText = new JLabel(new ImageIcon("D:\\PersonalFile\\javaProject\\puzzlegame\\image\\login\\验证码.png"));
        codeText.setBounds(133, 256, 50, 30);
        this.getContentPane().add(codeText);
        //验证码的输入框
        code.setBounds(195, 256, 100, 30);
        this.getContentPane().add(code);
        //获取验证码
        String codeStr = CodeUtil.getCode();
        //设置内容
        rightCode.setText(codeStr);
        //绑定鼠标事件
        rightCode.addMouseListener(this);
        //位置和宽高
        rightCode.setBounds(300, 256, 50, 30);
        //添加到界面
        this.getContentPane().add(rightCode);


        //4.添加登录按钮
        login.setBounds(123, 310, 128, 47);
        login.setIcon(new ImageIcon("D:\\PersonalFile\\javaProject\\puzzlegame\\image\\login\\登录按钮.png"));
        //去除按钮的边框
        login.setBorderPainted(false);
        //去除按钮的背景
        login.setContentAreaFilled(false);
        //给登录按钮绑定鼠标事件
        login.addMouseListener(this);
        //把按钮添加到背景中
        this.getContentPane().add(login);


        //5.添加注册按钮
        register.setBounds(256, 310, 128, 47);
        register.setIcon(new ImageIcon("D:\\PersonalFile\\javaProject\\puzzlegame\\image\\login\\注册按钮.png"));
        //去除按钮的边框
        register.setBorderPainted(false);
        //去除按钮的背景
        register.setContentAreaFilled(false);
        //给注册按钮绑定鼠标事件
        register.addMouseListener(this);
        //把按钮添加到背景中
        this.getContentPane().add(register);


        //7.添加背景图片
        JLabel background = new JLabel(new ImageIcon("D:\\PersonalFile\\javaProject\\puzzlegame\\image\\login\\background.png"));
        background.setBounds(0, 0, 470, 390);
        this.getContentPane().add(background);
    }


    //初始化界面(窗口)
    public void initJFrame() {
        this.setSize(488, 430);//设置宽高
        this.setTitle("Joe拼图游戏 V1.0登录");//设置标题
        this.setDefaultCloseOperation(3);//设置关闭模式
        this.setLocationRelativeTo(null);//居中
        this.setAlwaysOnTop(true);//置顶
        this.setLayout(null);//取消内部默认布局
    }



    //鼠标点击
    @Override
    public void mouseClicked(MouseEvent e) {
        //登录按钮
        if (e.getSource() == login) {
            System.out.println("点击了登录按钮");
            //获取两个文本输入框中的内容
            String usernameInput = username.getText();
            String passwordInput = password.getText();
            //获取用户输入的验证码
            String codeInput = code.getText();

            //创建一个User对象
            User userInfo = new User(usernameInput, passwordInput);
            System.out.println("用户输入的用户名为" + usernameInput);
            System.out.println("用户输入的密码为" + passwordInput);

            //输入内容为空(如果没有输入,代码中获取的是长度为0的字符串,不是null)
            if (codeInput.length() == 0) {
                //调用showJDialog方法并展示弹框
                showJDialog("验证码不能为空");
            } else if (usernameInput.length() == 0 || passwordInput.length() == 0) {
                //校验用户名和密码是否为空
                System.out.println("用户名或者密码为空");
                //调用showJDialog方法并展示弹框
                showJDialog("用户名或者密码为空");


            } else if (!codeInput.equalsIgnoreCase(rightCode.getText())) {
                showJDialog("验证码输入错误");
            } else if (contains(userInfo)) {
                System.out.println("用户名和密码正确可以开始玩游戏了");
                //关闭当前登录界面
                this.setVisible(false);
                //打开游戏的主界面
                //需要把当前登录的用户名传递给游戏界面
                new GameJFrame();
            } else {
                System.out.println("用户名或密码错误");
                showJDialog("用户名或密码错误");
            }
        } else if (e.getSource() == register) {
            System.out.println("点击了注册按钮");
        } else if (e.getSource() == rightCode) {
            System.out.println("更换验证码");
            //获取一个新的验证码
            String code = CodeUtil.getCode();
            rightCode.setText(code);
        }
    }


    public void showJDialog(String content) {
        //创建一个弹框对象
        JDialog jDialog = new JDialog();
        //给弹框设置大小
        jDialog.setSize(200, 150);
        //让弹框置顶
        jDialog.setAlwaysOnTop(true);
        //让弹框居中
        jDialog.setLocationRelativeTo(null);
        //弹框不关闭永远无法操作下面的界面
        jDialog.setModal(true);

        //创建Jlabel对象管理文字并添加到弹框当中
        JLabel warning = new JLabel(content);
        warning.setBounds(0, 0, 200, 150);
        jDialog.getContentPane().add(warning);

        //让弹框展示出来
        jDialog.setVisible(true);
    }

    //按下不松
    @Override
    public void mousePressed(MouseEvent e) {
        if (e.getSource() == login) {
            login.setIcon(new ImageIcon("D:\\PersonalFile\\javaProject\\puzzlegame\\image\\login\\登录按下.png"));
        } else if (e.getSource() == register) {
            register.setIcon(new ImageIcon("D:\\PersonalFile\\javaProject\\puzzlegame\\image\\login\\注册按下.png"));
        }
    }


    //松开按钮
    @Override
    public void mouseReleased(MouseEvent e) {
        if (e.getSource() == login) {
            login.setIcon(new ImageIcon("D:\\PersonalFile\\javaProject\\puzzlegame\\image\\login\\登录按钮.png"));
        } else if (e.getSource() == register) {
            register.setIcon(new ImageIcon("D:\\PersonalFile\\javaProject\\puzzlegame\\image\\login\\注册按钮.png"));
        }
    }

    //鼠标划入
    @Override
    public void mouseEntered(MouseEvent e) {

    }

    //鼠标划出
    @Override
    public void mouseExited(MouseEvent e) {

    }

    //判断用户在集合中是否存在
    public boolean contains(User userInput){
        for (int i = 0; i < allUsers.size(); i++) {
            User rightUser = allUsers.get(i);
            if(userInput.getUsername().equals(rightUser.getUsername()) && userInput.getPassword().equals(rightUser.getPassword())){
                //有相同的代表存在,返回true,后面的不需要再比了
                return true;
            }
        }
        //循环结束之后还没有找到就表示不存在
        return false;
    }
}

搭建游戏主界面 (含胜利界面)

在这个类的搭建中,我们将实现游戏的核心功能以及游戏菜单的制作

难度有点大,代码量也很大,代码中有详细的注释,如果没有头绪可以仔细阅读一遍代码

实现代码:

package com.joe.ui;

import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

public class GameJFrame extends JFrame implements KeyListener, ActionListener {
    int[][] newArr = new int[4][4];//方便initImages()方法中能使用这个二维数组

    //创建一个顺序二维数组,用来判断游戏胜利
    int[][] winArr = new int[][]{
            {1,2,3,4},
            {5,6,7,8},
            {9,10,11,12},
            {13,14,15,0}
    };

    int x; int y;//记录空格位置的二位索引值(地址)

    int step = 0;//创建一个数字记录游戏步数

    //创建一个JMenuItem选项的条目对象
    JMenuItem jMenuItem1 = new JMenuItem("重新游戏");
    JMenuItem jMenuItem2 = new JMenuItem("重新登录");
    JMenuItem jMenuItem3 = new JMenuItem("关闭游戏");
    JMenuItem jMenuItem4 = new JMenuItem("简介");
    JMenuItem jMenuItem5 = new JMenuItem("动物");
    JMenuItem jMenuItem6 = new JMenuItem("美女");
    JMenuItem jMenuItem7 = new JMenuItem("运动");
    JMenuItem jMenuItem8 = new JMenuItem("礼物");

    //默认图片
    String type = "animal";
    int typeNum = 4;

    //type 可以改变,因为它是一个变量,指向不同的字符串对象时只是改变了引用。
    //path 不会自动更新,因为它是一个不可变字符串的组合,字符串一旦创建就不会自动反映其他变量的变化,必须手动更新。
    //path 也是变量:它也是指向一个字符串对象。但它可能是用其他不可变对象(如字符串字面量)组合生成的。
    String path = "..\\puzzlegame\\image\\"+type+"\\" + type + typeNum + "\\";
    String path2 = "..\\puzzlegame\\image\\";

    //创建一个Random类
    Random r = new Random();

    //无参构造方法
    public GameJFrame(){
        //初始化界面
        initJFrame();

        //初始化菜单
        initJMenuBar();

        //初始化数据(打乱数组)
        initData();

        //初始化图片(把打乱的数组安插进图片以及胜利图片、游戏步数展示)
        initImages();

        System.out.println(x+" "+y);

        //让界面显示出来--#必须在最后一行书写
        this.setVisible(true);
    }

    //打乱数据并添加到二维数组中
    private void initData() {
        //新建一个数组,填入16个数字
        int[] arr = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        //打乱顺序
        Random r = new Random();
        for (int i = 0; i < arr.length; i++) {
            //接收随机变量
            int index = r.nextInt(arr.length);
            int temp = arr[i];
            arr[i] = arr[index];
            arr[index] = temp;
        }

        //按照四个一组的方法添加到二维数组当中
        //创建一个二维数组
        //定义一个计数器
        int count = 0;
        for (int i = 0; i < newArr.length; i++) {
            for (int j = 0; j < newArr[i].length; j++) {
                if (arr[count] == 0){
                    x = i;
                    y = j;
                }
                newArr[i][j] = arr[count];
                count++;
            }
        }
    }

    //加载图片
    private void initImages() {
        //清空之前的图片
        getContentPane().removeAll();

        //判断胜利
        if (winJudgment()){
            //展示胜图片
            JLabel winLabel = new JLabel(new ImageIcon(path2+"win.png"));
            winLabel.setBounds(203,283,197,73);
            getContentPane().add(winLabel);
        }

        //展示游戏步数
        JLabel stepLabel = new JLabel("步数:" + step);
        stepLabel.setBounds(50,30,100,20);
        getContentPane().add(stepLabel);

        //方法内越往上,显示的图层就越往上
        for (int j = 0; j < 4; j++) {
            for (int i = 0; i < 4; i++) {
                //接收打乱的数组值
                int num = newArr[j][i];
                //创建一个图片描述对象
                //创建一个存储容器的对象,用来存储图片

                JLabel jLabel1 = new JLabel(new ImageIcon(path + num + ".jpg"));
                //设置图片放置坐标
                jLabel1.setBounds(105 * i + 84,105*j + 134,105,105);

                //给图片添加边框
                //传入Border类型,其是个接口类
                jLabel1.setBorder(new BevelBorder(0));

                //将JLabel添加到界面(获取隐藏容器)
                this.getContentPane().add(jLabel1);
            }
        }


        //添加背景图片
        JLabel jLabel = new JLabel(new ImageIcon(path2+"background.png"));
        //设置背景图片位置
        jLabel.setBounds(40,40,508,560);
        //把背景图片添加到容器中
        getContentPane().add(jLabel);


        //刷新图片
        getContentPane().repaint();
    }

    //创建菜单栏
    private void initJMenuBar() {
        //创建一个菜单栏对象
        JMenuBar jMenuBar = new JMenuBar();
        //创建JMenu选项对象(功能,关于作者)
        JMenu jMenu1 = new JMenu("功能");
        JMenu jMenu2 = new JMenu("关于作者");
        JMenu jMenu3 = new JMenu("更换图片");

        //将条目对象添加到选项中
        jMenu1.add(jMenu3);
        jMenu1.add(jMenuItem1);
        jMenu1.add(jMenuItem2);
        jMenu1.add(jMenuItem3);

        jMenu2.add(jMenuItem4);

        jMenu3.add(jMenuItem5);
        jMenu3.add(jMenuItem6);
        jMenu3.add(jMenuItem7);
        jMenu3.add(jMenuItem8);


        //再将选项对象添加到菜单中
        jMenuBar.add(jMenu1);
        jMenuBar.add(jMenu2);

        //给整个界面设置菜单
        this.setJMenuBar(jMenuBar);
    }

    //判断游戏是否胜利
    private boolean winJudgment(){
        //遍历newArr
        for (int i = 0; i < newArr.length; i++) {
            for (int j = 0; j < newArr[i].length; j++) {
                if (newArr[i][j] != winArr[i][j]){
                    //存在不相等的情况
                    return false;
                }
            }
        }
        //完全匹配,游戏胜利
        return true;
    }

    //设置界面
    private void initJFrame() {
        //设置界面宽高
        this.setSize(603,680);
        //设置标题
        this.setTitle("Joe拼图单机版 v1.0");
        //设置居中显示
        this.setLocationRelativeTo(null);
        //让界面总是在最上方显示
        this.setAlwaysOnTop(true);
        //界面关闭操作——当有一个界面点击关闭时结束运行
        //(因为开发为一个窗口出现其他窗口隐藏)
        this.setDefaultCloseOperation(3);
        //取消隐藏容器默认居中放置
        this.setLayout(null);

        //添加键盘监听
        addKeyListener(this);

        //添加动作监听
        jMenuItem1.addActionListener(this);
        jMenuItem2.addActionListener(this);
        jMenuItem3.addActionListener(this);
        jMenuItem4.addActionListener(this);

        jMenuItem5.addActionListener(this);
        jMenuItem6.addActionListener(this);
        jMenuItem7.addActionListener(this);
        jMenuItem8.addActionListener(this);
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //按下键盘
    @Override
    public void keyPressed(KeyEvent e) {
        //按下图片的操作
        //接收按下的键值
        int code = e.getKeyCode();
        if (code == 65){
            //清空图片
            getContentPane().removeAll();
            //创建容器和图片,把图片放进容器
            JLabel all = new JLabel(new ImageIcon(path + "all.jpg"));
            //设置容器位置和大小
            all.setBounds(83,134,420,420);
            //把容器放进窗口
            getContentPane().add(all);

            //添加背景
            JLabel jLabel = new JLabel(new ImageIcon("..\\puzzlegame\\image\\background.png"));
            //设置背景图片位置
            jLabel.setBounds(40,40,508,560);
            //把背景图片添加到容器中
            getContentPane().add(jLabel);

            //刷新界面
            getContentPane().repaint();
        }
    }

    //松开键盘
    @Override
    public void keyReleased(KeyEvent e) {
        int code = e.getKeyCode();

        if (code == 65) {
            //展示完整图片后要回到原界面
            initImages();
        }

        //如果胜利则不在执行
        if (winJudgment()){
            return;
        }

        if (code == 38){
            if (x == 3){
                //移动至尽头
                return;
            }
            System.out.println("向上移动");
            newArr[x][y] = newArr[x+1][y];
            newArr[x+1][y] = 0;
            x++;
            step++;//得先添加步数再显示
            initImages();
        } else if (code == 40) {
            if (x == 0){
                //移动至尽头
                return;
            }
            System.out.println("向下移动");
            newArr[x][y] = newArr[x-1][y];
            newArr[x-1][y] = 0;
            x--;
            step++;
            initImages();
        } else if (code == 37) {
            if (y == 3){
                //移动至尽头
                return;
            }
            System.out.println("向左移动");
            newArr[x][y] = newArr[x][y+1];
            newArr[x][y+1] = 0;
            y++;
            step++;
            initImages();
        } else if (code == 39) {
            if (y == 0){
                //移动至尽头
                return;
            }
            System.out.println("向右移动");
            newArr[x][y] = newArr[x][y-1];
            newArr[x][y-1] = 0;
            y--;
            step++;
            initImages();
        } else if (code == 87) {
            //把数组顺序排列整齐
            newArr = new int[][]{
                    {1,2,3,4},
                    {5,6,7,8},
                    {9,10,11,12},
                    {13,14,15,0}
            };
            x = 3;
            y = 3;
            initImages();
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        //获取调用的对象
        Object source = e.getSource();
        if (source == jMenuItem1){
            System.out.println("重新游戏");
            //打乱数字
            initData();
            //重置步数
            step = 0;
            //重新加载图片
            initImages();
        } else if (source == jMenuItem2) {
            System.out.println("重新登录");
            //关闭当前对象
            setVisible(false);
            //创建一个登陆界面的对象
            new LoginJFrame();
        } else if (source == jMenuItem3) {
            System.out.println("关闭游戏");
            //直接关闭虚拟机
            System.exit(0);
        } else if (source == jMenuItem4) {
            System.out.println("作者简介");
            //创建一个弹窗对象
            JDialog jDialog = new JDialog();
            //创建容器和图片,把图片放进容器中
            JLabel jLabel = new JLabel(new ImageIcon(path2+"about.png"));
            //设置容器位置和大小
            jLabel.setBounds(0,0,258,258);
            //把容器放进弹窗中
            jDialog.getContentPane().add(jLabel);
            //设置弹窗大小
            jDialog.setSize(344,344);
            //置顶弹窗
            jDialog.setAlwaysOnTop(true);
            //居中放置
            jDialog.setLocationRelativeTo(null);
            //弹窗不关闭则无法进入其他操作
            jDialog.setModal(true);
            //显示弹窗
            jDialog.setVisible(true);
        } else if (source == jMenuItem5){
            System.out.println("动物");
            //修改路径值
            type = "animal";
            typeNum = r.nextInt(8)+1;
            System.out.println(typeNum);
            path = "..\\puzzlegame\\image\\" + type + "\\" + type + typeNum + "\\";
            //重开
            //打乱数字
            initData();
            //重置步数
            step = 0;
            //重新加载图片
            initImages();
        } else if (source == jMenuItem6) {
            System.out.println("美女");
            //修改路径值
            type = "girl";
            typeNum = r.nextInt(13)+1;
            System.out.println(typeNum);
            path = "..\\puzzlegame\\image\\" + type + "\\" + type + typeNum + "\\";
            //重开
            //打乱数字
            initData();
            //重置步数
            step = 0;
            //重新加载图片
            initImages();
        } else if (source == jMenuItem7) {
            System.out.println("运动");
            //修改路径值
            type = "sport";
            typeNum = r.nextInt(10)+1;
            System.out.println(typeNum);
            path = "..\\puzzlegame\\image\\" + type + "\\" + type + typeNum + "\\";
            //重开
            //打乱数字
            initData();
            //重置步数
            step = 0;
            //重新加载图片
            initImages();
        } else if (source == jMenuItem8) {
            System.out.println("礼物");
            //修改路径值
            type = "present";
            typeNum = r.nextInt(1)+1;
            System.out.println(typeNum);
            path = "..\\puzzlegame\\image\\" + type + "\\" + type + typeNum + "\\";
            //重开
            //打乱数字
            initData();
            //重置步数
            step = 0;
            //重新加载图片
            initImages();
        }
    }
}

这样我们的游戏就完成制作啦!

但是在最后我们再做一个小拓展与提示点


关于String变量

在上述代码中,我们用String变量去浅入图片路径,改变图片的选择

 如果我们通过菜单栏去更改图片,需要在动作监听的重写方法里重新写一遍path变量的赋值

因为path是String类型变量,字符串一旦赋值,不可修改,我们之所以能对其进行所谓的修改,只是其又创建了一个新的String对象,如下图所示:

为什么 type 可以被重新赋值而 path 不行呢?

- type 可以改变,因为它是一个变量,指向不同的字符串对象时只是改变了引用。
- path 不会自动更新,因为它是一个不可变字符串的组合,字符串一旦创建就不会自动反映其他变量的变化,必须手动更新。
- path 也是变量:它也是指向一个字符串对象。但它可能是用其他不可变对象(如字符串字面量)组合生成的。

 因此,我们需要在动作监听的重写方法里重新写一遍path变量的赋值,这样路径才会改变,不然一直都会是成员位置定义的默认路径,点击更换图片后得不到想要的反应

总结


  • 在这篇文章中,我们实现了如何使用Java开发一个拼图小游戏。
  • 通过详细的需求分析和功能描述,我们逐步构建了游戏的各个界面,包括登录界面、游戏主界面、游戏菜单和胜利界面。文章不仅涵盖了游戏开发的框架搭建,还详细介绍了编码的过程和关键步骤。
  • 从准备工具到实现程序的入口,再到创建用户类和工具类,本文全面展示了如何将游戏的各个功能模块逐一实现。
  • 通过本项目,我们练习了Java编程中的关键技能。

心得体会

在编写这篇文章的过程中,我对Java编程有了更深刻的理解。拼图小游戏的开发不仅加深了我对Java语言的掌握,也提升了我在需求分析、框架搭建和功能实现方面的能力。每一步的编码和测试都是对编程技能的挑战和提升。

通过这个项目,我们认识到系统化地进行需求分析和功能规划的重要性。合理的框架搭建和清晰的编码顺序是成功实现功能的关键。此外,用户界面的设计也极大地影响了游戏的整体体验。这个项目不仅是一个编程练习,也是一个展示如何将复杂问题拆解并逐步解决的实践。

总的来说,这次拼图小游戏的开发经验让我们在实践中积累了宝贵的技能,并为未来的编程挑战奠定了坚实的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2152139.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算总体方差statistics.pvariance()

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 计算总体方差 statistics.pvariance() [太阳]选择题 根据给定的Python代码&#xff0c;运行结果为&#xff1f; import statistics data [1, 2, 3, 4, 5] print(f"【显示】data{…

【C++掌中宝】深入解析C++命名空间:有效管理代码的利器

文章目录 前言1. namespace 的价值2. namespace 的定义3. 命名空间的本质4. 嵌套的命名空间5. 命名空间的使用6. using 指令7. 补充结语 前言 假设这样一种情况&#xff0c;当一个班上有两个名叫 Zara 的学生时&#xff0c;为了明确区分它们&#xff0c;我们在使用名字之外&am…

2.Seata 1.5.2 集成Springcloud-alibaba

一.Seata-server搭建已完成前提下 详见 Seata-server搭建 二.Springcloud 项目集成Seata 项目整体测试业务逻辑是创建订单后&#xff08;为了演示分布式事务&#xff0c;不做前置库存校验&#xff09;&#xff0c;再去扣减库存。库存不够的时候&#xff0c;创建的订单信息数…

攻防世界Web新手练习区题目(view_source到simple_php)WP

目录 view_source​ robots​ Training-WWW-Robots PHP2​ get_post​ backup​ cookie​ disabled_button​ simple_js​ xff_referer​ weak_auth​ command_execution​ simple_php​ view_source 获取在线场景后访问题目场景 在右键不管用的情况下&#xff0…

尚品汇-秒杀成功下单接口、秒杀结束定时任务-清空缓存数据(五十四)

目录&#xff1a; &#xff08;1&#xff09;下单页面 &#xff08;2&#xff09;service-activity-client添加接口 &#xff08;3&#xff09;web-all 编写去下单控制器 &#xff08;4&#xff09;service-order模块提供秒杀下单接口 &#xff08;5&#xff09;service-or…

1份可以派上用场丢失数据恢复的应用程序列表

无论如何&#xff0c;丢失您的宝贵数据是可怕的。您的 Android 或 iOS 设备可能由于事故、硬件损坏、存储卡问题等而丢失了数据。这就是为什么我们编制了一份可以派上用场以恢复丢失数据的应用程序列表。 如果您四处走动&#xff0c;您大多会随身携带手机或其他移动设备。这些…

豆包Python SDK接入流程

模型与价格 豆包的模型介绍可以看豆包大模型介绍&#xff0c;模型价格可以看豆包定价文档里的“模型推理” - “大语言模型” - “字节跳动”部分。 推荐使用以下模型&#xff1a; Doubao-lite-32k&#xff1a;每百万 token 的输入价格为 0.3 元&#xff0c;输出价格为 0.6 元…

vue组件($refs对象,动态组件,插槽,自定义指令)

一、ref 1.ref引用 每个vue组件实例上&#xff0c;都包含一个$refs对象&#xff0c;里面存储着对应dom元素或组件的引用。默认情况下&#xff0c;组件的$refs指向一个空对象。 2.使用ref获取dom元素的引用 <template><h3 ref"myh3">ref组件</h3&g…

手机切换IP简单方法:掌握技巧,轻松实现IP变换‌

在当今的数字化时代&#xff0c;IP地址作为网络身份的重要标识&#xff0c;对于网络访问、数据传输等方面都起着至关重要的作用。然而&#xff0c;在某些特定场景下&#xff0c;我们可能需要切换手机的IP地址&#xff0c;以满足不同的网络需求。本文将为大家介绍几种手机切换IP…

Linux —— 多线程

一、本篇重点 1.了解线程概念&#xff0c;理解线程与进程区别与联系 2.理解和学会线程控制相关的接口和操作 3.了解线程分离与线程安全的概念 4.学会线程同步。 5.学会互斥量&#xff0c;条件变量&#xff0c;posix信号量&#xff0c;以及读写锁 6.理解基于读写锁的读者写…

Kafka 3.0.0集群部署教程

1、集群规划 主机名 ip地址 node.id process.roles kafka1 192.168.0.29 1 broker,controller Kafka2 192.168.0.30 2 broker,controller Kafka3 192.168.0.31 3 broker,controller 将kafka包上传以上节点/app目录下 mkdir /app 解压kafka包 tar -zxvf kafka_…

BLE 协议之链路层

目录 一、前言二、状态和角色三、Air Interface Packets1、Preamble 字段2、Access Address 字段2.1 静态地址2.2 私有地址 3、PDU 字段3.1 Advertising Channel PDU3.1.1 Header 字段3.1.2 Payload 字段 3.2 Data Channel PDU3.2.1 Header 字段3.2.2 Payload 字段 4、CRC 字段…

【UE5】将2D切片图渲染为体积纹理,最终实现使用RT实时绘制体积纹理【第一篇-原理】

如果想直接制作&#xff0c;请看【第二篇】内容 这次做一个这样的东西&#xff0c;通过在2DRT上实时绘制&#xff0c;生成动态的体积纹理&#xff0c;也就是可以runtime的VDB 设想的文章流程: 对原理进行学习制作体积渲染制作实时绘制 第一篇&#xff08;本篇&#xff09;是对“…

Spring MVC 基础 : 文件、cookies的接收 ,REST响应

一、接受文件 在 Spring MVC 中&#xff0c;可以使用 RequestPart 注解来接收文件。这个注解常用于处理复杂的请求&#xff0c;如同时发送 JSON 数据和文件。RequestPart 非常适用于多部分请求&#xff08;multipart requests&#xff09;&#xff0c;这在单个请求中同时发送文…

法定退休年龄计算器,看看你还有多少年退休?

最近延迟退休上了微博热搜&#xff0c;从2025年开始实行。 分享个法定退休年龄计算器&#xff0c;看看你还有多少年退休&#xff1f; 官方出品的计算器&#xff0c;网站地址 https://si.12333.gov.cn/304647.jhtml 输入出生日期和性别就可以。 当然也可以在微信的城市服务。…

基于asp.net固定资产管理系统设计与实现

博主介绍&#xff1a;专注于Java vue .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的…

JZ2440开发板——S3C2440的UART

以下内容源于韦东山课程的学习与整理&#xff0c;如有侵权请告知删除。 一、UART硬件简介 UART&#xff0c;全称是“Universal Asynchronous Receiver Transmitter”&#xff0c;即“通用异步收发器”&#xff0c;也就是我们日常说的“串口”。 它在嵌入式中用途非常广泛&…

【计算机网络篇】物理层

本文主要介绍计算机网络第二章节的物理层&#xff0c;文中的内容是我认为的重点内容&#xff0c;并非所有。参考的教材是谢希仁老师编著的《计算机网络》第8版。跟学视频课为河南科技大学郑瑞娟老师所讲计网。 文章目录 &#x1f3af;一.基本概念及公式 &#x1f383;基本概念…

力扣-1035不相交的线(Java详细题解)

题目链接&#xff1a;力扣-1035不相交的线 前情提要&#xff1a; 因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。 dp五部曲。 1.确定dp数组和i下标的含义。 2.确定递推公式。 3.dp初始化。 4.确定dp的遍历顺序。 5.如果没有ac打印dp数组 利于debug。 每一…

安全基础学习-AES128加密算法

前言 AES&#xff08;Advanced Encryption Standard&#xff09;是对称加密算法的一个标准&#xff0c;主要用于保护电子数据的安全。AES 支持128、192、和256位密钥长度&#xff0c;其中AES-128是最常用的一种&#xff0c;它使用128位&#xff08;16字节&#xff09;的密钥进…