黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)

1、主界面分析

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第1张图片

 1、练习:创建主界面1

到IDEA中创建一个宽603像素,高680像素的游戏主界面

到IDEA中创建一个宽488像素,高430像素的登录界面

到IDEA中创建一个宽488像素,高500像素的注册界面

解题思路分析:因为程序主要分为三块(即游戏界面、登陆界面、注册界面),每一个界面内又有很多操作,需要写很多代码,因此要把三个界面分开写在三个class当中,而不是只写在一个main方法中,这样也有利于以后的代码找错和维护。在以后的开发过程中,main方法通常作为程序的启动入口,里面是不会有逻辑代码的。

创建main方法的操作如下:

在IDEA中右击src--> new --> javaclass --> 新建类名为App --> 在App类中书写main方法

package YouXiUI;

/**主界面业务逻辑
 * 上下左右移动的代码逻辑
 * 统计步数的代码逻辑
 * 一键通关、查看最终效果、恶搞好友等等
 */
import javax.swing.*;
//注意这个继承的操作!!!
public class GameJFrame extends JFrame {
    //JFrame 界面,窗体
    //它的子类也是界面,窗体
    //规定:GameJFrame这个界面表示的就是游戏的主界面(所以需要继承JFrame)
    //以后跟游戏相关的所有逻辑都写在这个类中
    public GameJFrame(){
        //JavaBean类描述界面的属性(宽 高)  行为(是否显示窗口)
        this.setSize(603,680);    //设置窗口的属性(宽 高)
        this.setVisible(true);    //设置窗口是否显示 true为显示 false为隐藏
    }
}
package YouXiUI;

/**登录界面的业务逻辑
 * 获取用户输入的用户名
 * 获取用户输入的密码
 * 生成一个验证码
 * 获取用户输入放入验证码
 * 比较用户名、密码、验证码等等
 */
import javax.swing.*;

public class LoginJFrame extends JFrame {
    //LoginJFrame表示登录界面
    //以后所有跟登录有关的代码,都写在这里

    //构造方法的作用,就是给对象设置默认属性值
    //设置默认属性值的时候,把设置的代码写在构造方法里面
    public LoginJFrame(){
        //在创建登陆界面的时候,同时给这些界面设置一些信息
        //比如:宽、高、直接展示出来
        this.setSize(488,430);  //this代表当前对象(当前的LoginJFrame对象)
        this.setVisible(true);
    }
}
package YouXiUI;

/**注册界面业务逻辑
 * 获取用户输入的用户名
 * 获取用户输入的密码(两次)
 * 比较两次的密码是否一致
 * 判断当前用户是否已经注册等等
 */
import javax.swing.*;

public class RegisterJFrame extends JFrame {
    //跟注册界面有关的代码,都写在这个界面中
    //利用构造方法设置初始化默认值
    public RegisterJFrame(){
        this.setSize(488,500);
        this.setVisible(true);
    }
}
import YouXiUI.GameJFrame;
import YouXiUI.LoginJFrame;
import YouXiUI.RegisterJFrame;

public class App {
    //开发过程中,main方法为程序的启动入口
    public static void main(String[] args) {


        //如果我们想要开启一个界面,就创建谁的对象就可以了
        new LoginJFrame();  //  创建登录界面对象
        new RegisterJFrame();   //创建注册界面
        new GameJFrame();   //创建游戏的主界面

    }
}

2、练习:创建主界面2和菜单制作

2.1、创建主界面2

用继承改写上述界面,并思考用继承改写的好处

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第2张图片

 创建主界面2代码如下:

package YouXiUI;

/**主界面业务逻辑
 * 上下左右移动的代码逻辑
 * 统计步数的代码逻辑
 * 一键通关、查看最终效果、恶搞好友等等
 */
import javax.swing.*;
//注意这个继承的操作!!!
public class GameJFrame extends JFrame {
    //JFrame 界面,窗体
    //它的子类也是界面,窗体
    //规定:GameJFrame这个界面表示的就是游戏的主界面(所以需要继承JFrame)
    //以后跟游戏相关的所有逻辑都写在这个类中
    public GameJFrame(){
        //JavaBean类描述界面的属性(宽 高)  行为(是否显示窗口)
        this.setSize(603,680);    //设置窗口的属性(宽 高)
        this.setTitle("拼图单机版 v1.0");    //设置界面标题

        /**设置界面置顶(即设置页面始终悬浮在其他页面之上)
         * 为true时该窗口会有“任何情况下都会悬浮在其他窗口之上”的效果
         * 为false时,点击其他界面时,该窗口就会被其他界面遮挡住
         */
        this.setAlwaysOnTop(true);

        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置


        /**设置关闭模式(设置程序结束运行的方式)
         * DO_NOTHING_ON_CLOSE = 0   什么都不做的默认窗口关闭工作(任何操作都关不了窗口)
         * HIDE_ON_CLOSE = 1  windows默认窗口关闭操作,窗口会被关闭,但程序不会结束运行
         * DISPOSE_ON_CLOSE = 2  开启多个界面之后,只有关闭所有开启的窗口,程序才会结束(用2时,需要所有的界面都做这样的设置,才会有效果)
         * EXIT_ON_CLOSE = 3    只要关闭一个界面,所有界面都会关闭,且程序会结束运行
         * 可以用 this.setDefaultCloseOperation(1);   //1可以替换成0、2、3
         * 也可以利用WindowConstants去调用对应的常量值,如下:(DISPOSE_ON_CLOSE可以替换成其他对应的英文)
         * this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
         */
        this.setDefaultCloseOperation(3);



        this.setVisible(true);    //设置窗口是否显示 true为显示 false为隐藏,建议写在最后
    }
}
package YouXiUI;

/**登录界面的业务逻辑
 * 获取用户输入的用户名
 * 获取用户输入的密码
 * 生成一个验证码
 * 获取用户输入放入验证码
 * 比较用户名、密码、验证码等等
 */
import javax.swing.*;

public class LoginJFrame extends JFrame {
    //LoginJFrame表示登录界面
    //以后所有跟登录有关的代码,都写在这里

    //构造方法的作用,就是给对象设置默认属性值
    //设置默认属性值的时候,把设置的代码写在构造方法里面
    public LoginJFrame(){
        //在创建登陆界面的时候,同时给这些界面设置一些信息
        //比如:宽、高、直接展示出来
        this.setSize(488,430);  //this代表当前对象(当前的LoginJFrame对象)
        this.setTitle("拼图 登录");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置界面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setVisible(true);  //设置窗口为显示
    }
}
package YouXiUI;

/**注册界面业务逻辑
 * 获取用户输入的用户名
 * 获取用户输入的密码(两次)
 * 比较两次的密码是否一致
 * 判断当前用户是否已经注册等等
 */
import javax.swing.*;

public class RegisterJFrame extends JFrame {
    //跟注册界面有关的代码,都写在这个界面中
    //利用构造方法设置初始化默认值
    public RegisterJFrame(){
        this.setSize(488,500);
        this.setTitle("拼图 注册");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置界面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setVisible(true);  //设置窗口为显示
    }
}
import YouXiUI.GameJFrame;
import YouXiUI.LoginJFrame;
import YouXiUI.RegisterJFrame;

public class App {
    //开发过程中,main方法为程序的启动入口
    public static void main(String[] args) {


        //如果我们想要开启一个界面,就创建谁的对象就可以了
        new LoginJFrame();  //  创建登录界面对象
        new RegisterJFrame();   //创建注册界面
        new GameJFrame();   //创建游戏的主界面

    }
}

2.2、菜单制作

分析过程图1

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第3张图片

 分析过程图2

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第4张图片

 分析过程图3

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第5张图片

 实现代码如下:

package YouXiUI;

/**主界面业务逻辑
 * 上下左右移动的代码逻辑
 * 统计步数的代码逻辑
 * 一键通关、查看最终效果、恶搞好友等等
 */
import javax.swing.*;

public class GameJFrame extends JFrame {

    //构造方法
    public GameJFrame(){
        //Ctrl + Alt + M  提取代码形成方法
        initJFrame();   //调用initJFrame()方法初始化界面
        initJMenuBar();     //调用initJMenuBar()方法,初始化菜单
        this.setVisible(true);    //让界面显示出来,建议写在最后
    }




    //创建初始化菜单的方法
    private void initJMenuBar() {
        //1、创建整个的菜单对象
        JMenuBar jMenuBar = new JMenuBar();

        //2、创建菜单上面的两个选项的对象(功能 关于我们)
        JMenu functionJMenu = new JMenu("功能");
        JMenu aboutJMenu = new JMenu("关于我们");

        //3、创建选项下面的条目对象(重新游戏 重新登录 关闭游戏)
        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");

        JMenuItem accountItem = new JMenuItem("公众号");

        //4、将每一个选项下面的条目对象添加到选项当中
        functionJMenu.add(replayItem);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);

        aboutJMenu.add(accountItem);

        //5、将菜单里面的两个选项添加到菜单当中
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);

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

    //创建初始化界面的方法
    private void initJFrame() {
        this.setSize(603,680);    //设置窗口的属性(宽 高)
        this.setTitle("拼图单机版 v1.0");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置页面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
    }
}
package YouXiUI;

/**登录界面的业务逻辑
 * 获取用户输入的用户名
 * 获取用户输入的密码
 * 生成一个验证码
 * 获取用户输入放入验证码
 * 比较用户名、密码、验证码等等
 */
import javax.swing.*;

public class LoginJFrame extends JFrame {

    public LoginJFrame(){

        this.setSize(488,430);  //this代表当前对象(当前的LoginJFrame对象)
        this.setTitle("拼图 登录");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置界面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setVisible(true);  //设置窗口为显示
    }
}
package YouXiUI;

/**注册界面业务逻辑
 * 获取用户输入的用户名
 * 获取用户输入的密码(两次)
 * 比较两次的密码是否一致
 * 判断当前用户是否已经注册等等
 */
import javax.swing.*;

public class RegisterJFrame extends JFrame {

    public RegisterJFrame(){
        this.setSize(488,500);
        this.setTitle("拼图 注册");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置界面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setVisible(true);  //设置窗口为显示
    }
}
import YouXiUI.GameJFrame;
import YouXiUI.LoginJFrame;
import YouXiUI.RegisterJFrame;

public class App {
    //开发过程中,main方法为程序的启动入口
    public static void main(String[] args) {


        //如果我们想要开启一个界面,就创建谁的对象就可以了
        //new LoginJFrame();  //  创建登录界面对象
        //new RegisterJFrame();   //创建注册界面
        new GameJFrame();   //创建游戏的主界面

    }
}

3、添加图片

说明1:

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第6张图片

 说明2:

找到下载好的image文件夹,然后ctrl+C,然后打开IDEA,在IDEA中选中模块名(切记:一定要选中模块名,不然会出错),然后ctrl+V,点击OK就可以了。

说明3:

坐标以左上角为原点,向右为X轴正方方向,向下为Y轴正方向,图片的所在位置实际上是指图片左上角的点所在的坐标位置,例如下面图片,如果图片的位置设置为(0,0),则图片就会在左上角显示。

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第7张图片

说明4:

窗口一共分为三个部分,如下图所示,JFrame只是一个大的架子,最下面的第三部分才是用来装载所有组件的(组件可以为图片、文字、按钮、进度条),第三部分是一个隐藏的容器,把需要显示的图片添加到隐藏容器中,如果对添加的图片没有特殊要求,图片就会默认添加到中间位置。利用setLayout(null)可以取消该默认方式,达到自由设置位置的效果。如果要取消默认。在创建窗口的时候就要书写setLayout(null)方法。取消隐藏容器中的居中默认方式后,里面添加的组件才会按照XY轴的形式来进行添加。

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第8张图片

 添加两个哈士奇拼图小图片代码如下:

(注意:实现此功能的时候,只需要修改主界面的代码,其他代码不需要修改)

package YouXiUI;

/**主界面业务逻辑
 * 上下左右移动的代码逻辑
 * 统计步数的代码逻辑
 * 一键通关、查看最终效果、恶搞好友等等
 */
import javax.swing.*;

public class GameJFrame extends JFrame {

    //构造方法
    public GameJFrame(){
        //Ctrl + Alt + M  提取代码形成方法
        initJFrame();   //调用initJFrame()方法初始化界面
        initJMenuBar();     //调用initJMenuBar()方法,初始化菜单

        //初始化图片
        //直接书写,会报错,然后利用alt+enter,然后选择Create menthod等等的那个选项,点击回车
        //就会自动生成下面的private void initImage() { }方法
        initImage();
        
        this.setVisible(true);    //让界面显示出来,建议写在最后
    }

    //初始化图片
    private void initImage() {
        //先创建一个图片ImageIcon的对象
        /**ImageIcon icon = new ImageIcon("图片路径");   说明
         * 括号里面的参数是需要添加的图片的地址
         * 找到IDEA左边的项目下的image,
         * image -> animal -> animal3 -> 3.jpg -> 右击3.jpg -> Copy -> Copy Path -> 选中Absolute Path并点击即可完成路径复制
         * 将复制的路径粘贴到括号内即可
         */
        ImageIcon icon = new ImageIcon("D:\\IDEA2020\\IDEAprocejts\\PinTuYouXi\\image\\animal\\animal3\\1.jpg");

        //然后创建一个JLabel的对象(即管理容器)(我们是需要把图片放到管理容器里面的)
        JLabel jLabel1 = new JLabel(icon);
        //指定图片位置  参数分别为(x坐标,y坐标,图片宽度,图片高度)  单位都是像素
        jLabel1.setBounds(0,0,105,105);

        //最后把管理容器添加到界面当中
        //this.add(jLabel);   //图片默认位置为居中正中心

        //利用getContentPane()获取到隐藏容器,然后用add()把图片添加到JFrame的隐藏容器中
        this.getContentPane().add(jLabel1);

        //添加第二张图片
        ImageIcon icon2 = new ImageIcon("D:\\IDEA2020\\IDEAprocejts\\PinTuYouXi\\image\\animal\\animal3\\2.jpg");
        JLabel jLabel2 = new JLabel(icon2);
        jLabel2.setBounds(105,0,105,105);
        this.getContentPane().add(jLabel2);
    }


    //创建初始化菜单的方法
    private void initJMenuBar() {
        //1、创建整个的菜单对象
        JMenuBar jMenuBar = new JMenuBar();

        //2、创建菜单上面的两个选项的对象(功能 关于我们)
        JMenu functionJMenu = new JMenu("功能");
        JMenu aboutJMenu = new JMenu("关于我们");

        //3、创建选项下面的条目对象(重新游戏 重新登录 关闭游戏)
        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");

        JMenuItem accountItem = new JMenuItem("公众号");

        //4、将每一个选项下面的条目对象添加到选项当中
        functionJMenu.add(replayItem);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);

        aboutJMenu.add(accountItem);

        //5、将菜单里面的两个选项添加到菜单当中
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);

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

    //创建初始化界面的方法
    private void initJFrame() {
        this.setSize(603,680);    //设置窗口的属性(宽 高)
        this.setTitle("拼图单机版 v1.0");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置页面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setLayout(null);   //取消图片默认的居中防放置,只有取消了才会按照XY轴的形式添加组件
    }
}

package YouXiUI;

/**登录界面的业务逻辑
 * 获取用户输入的用户名
 * 获取用户输入的密码
 * 生成一个验证码
 * 获取用户输入放入验证码
 * 比较用户名、密码、验证码等等
 */
import javax.swing.*;

public class LoginJFrame extends JFrame {

    public LoginJFrame(){

        this.setSize(488,430);  //this代表当前对象(当前的LoginJFrame对象)
        this.setTitle("拼图 登录");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置界面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setVisible(true);  //设置窗口为显示
    }
}
package YouXiUI;

/**注册界面业务逻辑
 * 获取用户输入的用户名
 * 获取用户输入的密码(两次)
 * 比较两次的密码是否一致
 * 判断当前用户是否已经注册等等
 */
import javax.swing.*;

public class RegisterJFrame extends JFrame {

    public RegisterJFrame(){
        this.setSize(488,500);
        this.setTitle("拼图 注册");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置界面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setVisible(true);  //设置窗口为显示
    }
}
import YouXiUI.GameJFrame;
import YouXiUI.LoginJFrame;
import YouXiUI.RegisterJFrame;

public class App {
    //开发过程中,main方法为程序的启动入口
    public static void main(String[] args) {


        //如果我们想要开启一个界面,就创建谁的对象就可以了
        //new LoginJFrame();  //  创建登录界面对象
        //new RegisterJFrame();   //创建注册界面
        new GameJFrame();   //创建游戏的主界面

    }
}

添加了拼图游戏中的15张小图片(一个哈士奇的拼图素材)后的代码如下:

(注意:相比于上一步,只改写了GameJFrame的代码)

package YouXiUI;

/**主界面业务逻辑
 * 上下左右移动的代码逻辑
 * 统计步数的代码逻辑
 * 一键通关、查看最终效果、恶搞好友等等
 */
import javax.swing.*;

public class GameJFrame extends JFrame {

    //构造方法
    public GameJFrame(){
        //Ctrl + Alt + M  提取代码形成方法
        //选中方法 按ctrl+B可以跳转到该方法的代码所在处
        initJFrame();   //调用initJFrame()方法初始化界面
        initJMenuBar();     //调用initJMenuBar()方法,初始化菜单
        initImage();    //初始化图片

        this.setVisible(true);    //让界面显示出来,建议写在最后
    }

    //初始化图片
    private void initImage() {
        int number = 1;
        //外循环--结合内循环的前提下,从上到下依次添加一行,一共添加四行,Y值依次增加105像素,X值不变
        for (int i = 0; i < 4; i++) {
            //内循环--在每一行从左到右依次添加一张图片,一共添加四张,X值依次增加105像素,Y值不变
            for (int j = 0; j < 4; j++) {
                //然后创建一个JLabel的对象(即管理容器)  注意留意路径中的变量number的书写方法
                JLabel jLabel = new JLabel(new ImageIcon("D:\\IDEA2020\\IDEAprocejts\\PinTuYouXi\\image\\animal\\animal3\\"+number+".jpg"));
                //根据坐标,设置图片的位置
                jLabel.setBounds(105*j,105*i,105,105);
                //利用getContentPane()获取到隐藏容器,然后用add()把图片添加到JFrame的隐藏容器中
                this.getContentPane().add(jLabel);
                //添加一次之后number需要自增,表示下一次加载后面的图片
                number++;
            }
        }

    }

    //创建初始化菜单的方法
    private void initJMenuBar() {
        //1、创建整个的菜单对象
        JMenuBar jMenuBar = new JMenuBar();

        //2、创建菜单上面的两个选项的对象(功能 关于我们)
        JMenu functionJMenu = new JMenu("功能");
        JMenu aboutJMenu = new JMenu("关于我们");

        //3、创建选项下面的条目对象(重新游戏 重新登录 关闭游戏)
        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");

        JMenuItem accountItem = new JMenuItem("公众号");

        //4、将每一个选项下面的条目对象添加到选项当中
        functionJMenu.add(replayItem);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);

        aboutJMenu.add(accountItem);

        //5、将菜单里面的两个选项添加到菜单当中
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);

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

    //创建初始化界面的方法
    private void initJFrame() {
        this.setSize(603,680);    //设置窗口的属性(宽 高)
        this.setTitle("拼图单机版 v1.0");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置页面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setLayout(null);   //取消图片默认的居中防放置,只有取消了才会按照XY轴的形式添加组件
    }
}

游戏主界面添加组件小结

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第9张图片

 4、打乱图片

每一张小的图片都是跟一个唯一的数字产生对应关系,分别是1~15和0(空白区域为0)如下图

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第10张图片

我们可以把1~15和0,放入数组容器中,然后在容器中打乱顺序,然后每4个数放入一个单独的一维数组当中,再把4个一维数组放入一个二维数组当中,方便管理。如下图:

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第11张图片

在继续开发这个小游戏之前,需要做一个小练习,来熟悉一下开发这个游戏需要用到的技术

小练习:打乱一维数组中的数据

int[] tempArr = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

要求:打乱一维数组中的数据,并按照4个一组的方式添加到二维数组中。

package TEST;

import java.util.Random;

public class Test {
    public static void main(String[] args) {
        //1、定义一个一维数组
        int[] tempArr ={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        //2、打乱数组中的数据的顺序
        //遍历数组,得到每一个元素,拿着每一个元素跟随即索引上的数据进行交换
        Random r = new Random();
        for (int i = 0; i < tempArr.length; i++) {
            //获取到随即索引
            int index = r.nextInt(tempArr.length);
            //拿着遍历到的每一个数据,跟随机索引上的数据进行交换
            int temp = tempArr[i];
            tempArr[i] = tempArr[index];
            tempArr[index] = temp;
        }
        //遍历数组,查看打乱顺序后的效果
        for (int i = 0; i < tempArr.length; i++) {
            System.out.print(tempArr[i] + " ");
        }
        System.out.println();

        //创建一个二维数组
        int[][] date = new int[4][4];

        //给二维数组添加数据
        /*//方法1:
        //遍历一维数组tempArr得到每一个元素,把每一个元素依次添加到二维数组当中
        for (int i = 0; i < tempArr.length; i++) {
            //在i依次增加的过程中date的右下标依次为
            // i为0~3时:[0][0] [0][1] [0][2] [0][3]
            // i为4~7时:[1][0] [1][1] [1][2] [1][3]
            // i为8~11时:[2][0] [2][1] [2][2] [2][3]
            // i为12~15时:[3][0] [3][1] [3][1] [3][3]
            date[i/4][i%4] = tempArr[i];
        }*/

        //方法2:
        //遍历二维数组,然后给里面的每一个数据赋值
        int index02 = 0;
        for (int i = 0; i < date.length; i++) {
            for (int j = 0; j < date[i].length; j++) {
                //等式左边依次为二维数组的每一个元素(由于嵌套循环的存在),等式右边依次为一维数组的每个元素(由于计数器index02的存在)
                date[i][j] = tempArr[index02];
                index02++;
            }
        }

        
        //打印添加到二维数组之后的效果
        for (int i = 0; i < date.length; i++) {
            for (int j = 0; j < date[i].length; j++) {
                System.out.print(date[i][j]+" ");
            }
            System.out.println();
        }


    }
}

下面我们继续开发小游戏,对游戏中的图片进行打乱。

(注意:在之前的基础上修改了GameJFrame的代码,其他模块代码不变)

package YouXiUI;

/**主界面业务逻辑
 * 上下左右移动的代码逻辑
 * 统计步数的代码逻辑
 * 一键通关、查看最终效果、恶搞好友等等
 */
import javax.swing.*;
import java.util.Random;

public class GameJFrame extends JFrame {

    //创建一个二维数组
    //目的:用来管理数据。加载图片的时候,会根据二维数组中的数据进行加载
    //因为初始化数据时,初始化图片时都会用到这个二维数组,所以把二维数组定义在成员变量的位置
    int[][] date = new int[4][4];

    //构造方法
    public GameJFrame(){
        //Ctrl + Alt + M  提取代码形成方法
        //选中方法 按ctrl+B可以跳转到该方法的代码所在处
        initJFrame();   //调用initJFrame()方法初始化界面
        initJMenuBar();     //调用initJMenuBar()方法,初始化菜单
        initDate();     //初始化数据(打乱)
        initImage();    //初始化图片(根据打乱之后的结果去加载图片)

        this.setVisible(true);    //让界面显示出来,建议写在最后
    }

    //初始化数据(打乱)
    private void initDate() {
        //1、定义一个一维数组
        int[] tempArr ={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        //2、打乱数组中的数据的顺序
        //遍历数组,得到每一个元素,拿着每一个元素跟随即索引上的数据进行交换
        Random r = new Random();
        for (int i = 0; i < tempArr.length; i++) {
            //获取到随即索引
            int index = r.nextInt(tempArr.length);
            //拿着遍历到的每一个数据,跟随机索引上的数据进行交换
            int temp = tempArr[i];
            tempArr[i] = tempArr[index];
            tempArr[index] = temp;
        }

        //3、给二维数组添加数据
        for (int i = 0; i < tempArr.length; i++) {
            date[i/4][i%4] = tempArr[i];
        }
    }

    //初始化图片
    //由于二维数组已经创建好,所以添加图片的时候,就需要按照二维数组中管理的数据进行添加图片了
    //因此之前的number就不需要了,就u、需要删掉了
    private void initImage() {
        //外循环--结合内循环的前提下,从上到下依次添加一行,一共添加四行,Y值依次增加105像素,X值不变
        for (int i = 0; i < 4; i++) {
            //内循环--在每一行从左到右依次添加一张图片,一共添加四张,X值依次增加105像素,Y值不变
            for (int j = 0; j < 4; j++) {
                //获取当前要加载图片的序号,date[i][j]的结果是这个位置上对应的数据值
                int num = date[i][j];
                //然后创建一个JLabel的对象(即管理容器)
                JLabel jLabel = new JLabel(new ImageIcon("D:\\IDEA2020\\IDEAprocejts\\PinTuYouXi\\image\\animal\\animal3\\"+num+".jpg"));
                //根据坐标,设置图片的位置
                jLabel.setBounds(105*j,105*i,105,105);
                //利用getContentPane()获取到隐藏容器,然后用add()把图片添加到JFrame的隐藏容器中
                this.getContentPane().add(jLabel);
            }
        }
    }

    //创建初始化菜单的方法
    private void initJMenuBar() {
        //1、创建整个的菜单对象
        JMenuBar jMenuBar = new JMenuBar();

        //2、创建菜单上面的两个选项的对象(功能 关于我们)
        JMenu functionJMenu = new JMenu("功能");
        JMenu aboutJMenu = new JMenu("关于我们");

        //3、创建选项下面的条目对象(重新游戏 重新登录 关闭游戏)
        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");

        JMenuItem accountItem = new JMenuItem("公众号");

        //4、将每一个选项下面的条目对象添加到选项当中
        functionJMenu.add(replayItem);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);

        aboutJMenu.add(accountItem);

        //5、将菜单里面的两个选项添加到菜单当中
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);

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

    //创建初始化界面的方法
    private void initJFrame() {
        this.setSize(603,680);    //设置窗口的属性(宽 高)
        this.setTitle("拼图单机版 v1.0");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置页面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setLayout(null);   //取消图片默认的居中防放置,只有取消了才会按照XY轴的形式添加组件
    }
}

5、事件

事件是可以被组件识别的操作。当你对组件干了某件事情之后,就会执行对应的代码。

事件源:通常是指产生事件的组件。例如:按钮、图片、窗体等等

事件:对组件进行的操作。例如:鼠标单击、鼠标划入等等

绑定监听:当事件源上发生了某个事件,监听器就会做出相应的处理,执行某段代码。

常用的三个监听如下:

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第12张图片

5.1 动作监听

动作监听实现的两种方式:

方式1:匿名内部类的实现方式

package TEST;

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

public class Test01 {
    public static void main(String[] args) {
        JFrame jFrame = new JFrame();
        jFrame.setSize(603,680);//设置界面宽高
        jFrame.setTitle("事件演示01");
        jFrame.setAlwaysOnTop(true);//设置界面置顶
        jFrame.setLocationRelativeTo(null);//设置界面居中
        jFrame.setDefaultCloseOperation(3);
        jFrame.setLayout(null);//取消组件居中放置

        //创建一按钮对象
        JButton jtb = new JButton("点我啊");
        //设置位置和宽高
        jtb.setBounds(0,0,100,50);
        /**给按钮添加监听动作
         * jtb:组件对象,表示要给哪个组件添加对象
         * addActionListener:表示我要给组件添加哪个监听事件(这里添加的是动作监听,包含鼠标左键点击、空格)
         * 参数:表示事件被触发之后要执行的代码
         */
        /**
         * 在实际开发当中,每一个按钮的业务逻辑不一样,所以我们书写的MyActionListener只能被
         * 用到一次,只能被这个jtb按钮使用,其他的按钮用不了这个业务逻辑。当一个接口的实现类
         * 只能被用一次的时候,我们就没必要单独定义这个类了,此时可以用匿名内部类去简化代
         * 码,省去了定义类的过程。
         */
        jtb.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("达咩~不要点我哟~");
            }
        });

        //不要忘了把按钮添加到界面当中
        jFrame.getContentPane().add(jtb);

        jFrame.setVisible(true);
    }
}

方式2:用本界面继承JFrame,调用ActionListener接口,把重写的方法写在本类当中,然后在传递的时候不用再new了,直接用this表示本类的对象就可以了。代码如下:

package TEST;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
//继承JFrame是为了创建窗口,调用ActionListener接口是为了给按钮设置监听
public class MyJFrame extends JFrame implements ActionListener {
    //创建一个按钮对象
    JButton jtb1 = new JButton("点我啊");
    //创建一个按钮对象
    JButton jtb2 = new JButton("再点我啊");

    //构造方法
    public MyJFrame(){
        this.setSize(603,680);    //设置窗口的属性(宽 高)
        this.setTitle("拼图单机版 v1.0");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置页面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setLayout(null);

        jtb1.setBounds(0,0,100,50);
        //给按钮添加事件
        //括号中的this表示的是本类的对象,作用是监听后让执行本类(MyJFrame类)中的代码
        jtb1.addActionListener(this);

        jtb2.setBounds(100,0,100,50);
        //给按钮添加事件
        jtb2.addActionListener(this);

        //把按钮添加到界面当中
        this.getContentPane().add(jtb1);
        this.getContentPane().add(jtb2);

        //让整个界面显现出来
        this.setVisible(true);

    }

    @Override
    public void actionPerformed(ActionEvent e) {
        //对当前按钮进行判断
        //获取当前被操作的那个按钮对象
        Object source = e.getSource();
        //
        if(source == jtb1){//让按钮变大
            jtb1.setSize(200,200);
        } else if(source == jtb2){//更改按钮的位置为随机位置
            Random r = new Random();
            jtb2.setLocation(r.nextInt(500),r.nextInt(500));
        }
    }
}
package TEST;

public class Test {
    public static void main(String[] args) {
        new MyJFrame();
    }
}

5.2 鼠标监听

分为四类:划入动作,按下动作、松开动作、划出动作。

按下动作和松开动作归为一组,称为单击动作。

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第13张图片

实践代码如下:

package TEST;

import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
//继承JFrame是为了创建窗口,调用MouseListener接口是为了给按钮设置鼠标监听
//调用接口的时候,需要重写接口中的所有方法,利用alt+回车的快捷方式书写
public class MyJFrame extends JFrame implements MouseListener {
    //创建一个按钮对象
    JButton jtb1 = new JButton("点我啊");

    //构造方法
    public MyJFrame(){
        this.setSize(603,680);    //设置窗口的属性(宽 高)
        this.setTitle("拼图单机版 v1.0");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置页面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setLayout(null);

        jtb1.setBounds(0,0,100,50);
        //给按钮绑定鼠标事件,参数this表示执行本类中的代码(即执行本类中的重写的方法)
        jtb1.addMouseListener(this);


        //把按钮添加到界面当中
        this.getContentPane().add(jtb1);

        //让整个界面显现出来
        this.setVisible(true);

    }

    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.println("单击");
    }

    @Override
    public void mousePressed(MouseEvent e) {
        System.out.println("按下不松");
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        System.out.println("松开");
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        System.out.println("划入");
    }

    @Override
    public void mouseExited(MouseEvent e) {
        System.out.println("划出");
    }
}
package TEST;

public class Test {
    public static void main(String[] args) {
        new MyJFrame();
    }
}

鼠标监听机制的方法摘要:

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第14张图片

 5.3 键盘监听

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第15张图片

 实践代码如下:

package TEST;

import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
//继承JFrame是为了创建窗口,调用MouseListener接口是为了给按钮设置鼠标监听
//调用接口的时候,需要重写接口中的所有方法,利用alt+回车的快捷方式书写
public class MyJFrame extends JFrame implements KeyListener {

    //构造方法
    public MyJFrame(){
        this.setSize(603,680);    //设置窗口的属性(宽 高)
        this.setTitle("拼图单机版 v1.0");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置页面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setLayout(null);

        //给整个窗体添加键盘监听
        //调用者this:本类对象,当前的界面对象,表示我要给当前整个界面添加监听
        //addKeyListener:表示要给本界面添加键盘监听
        //参数this:当事件被触发后,会执行本类中的对应代码(即执行本类中重写的方法)
        this.addKeyListener(this);


        //让整个界面显现出来
        this.setVisible(true);
    }


    @Override
    public void keyTyped(KeyEvent e) {

    }


    /**小细节
     * 细节1:如果我们按下一个按键没有松开,那么会重复调用keyPressed方法
     * 细节2:键盘里面那么多按键,如何进行区分?  答:每一个按键都有一个编号与之对应
     */
    @Override
    public void keyPressed(KeyEvent e) {
        System.out.println("按下不松");
    }

    @Override
    public void keyReleased(KeyEvent e) {
        System.out.println("松开按键");
        //获取键盘上每一个按键的编号
        int code = e.getKeyCode();
        //System.out.println(code);  打印按下的按键对应的编号
        if(code == 65){
            System.out.println("现在按的是A");
        } else if(code == 66){
            System.out.println("现在按的是B");
        }
    }
}
package TEST;

public class Test {
    public static void main(String[] args) {
        new MyJFrame();
    }
}

6 美化界面

图片美化业务逻辑:

1、让15张随机图片整体在中央偏下方显示。

2、给游戏添加一个背景图片。

3、给每一张小图片添加一个小边框。

实现代码如下:

package YouXiUI;

/**主界面业务逻辑
 * 上下左右移动的代码逻辑
 * 统计步数的代码逻辑
 * 一键通关、查看最终效果、恶搞好友等等
 */
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.util.Random;

public class GameJFrame extends JFrame {

    //创建一个二维数组
    //目的:用来管理数据。加载图片的时候,会根据二维数组中的数据进行加载
    //因为初始化数据时,初始化图片时都会用到这个二维数组,所以把二维数组定义在成员变量的位置
    int[][] date = new int[4][4];

    //构造方法
    public GameJFrame(){
        //Ctrl + Alt + M  提取代码形成方法
        //ctrl+alt+左键  可以回到我们刚刚查看的地方
        //选中方法 按ctrl+B可以跳转到该方法的代码所在处
        initJFrame();   //调用initJFrame()方法初始化界面
        initJMenuBar();     //调用initJMenuBar()方法,初始化菜单
        initDate();     //初始化数据(打乱)
        initImage();    //初始化图片(根据打乱之后的结果去加载图片)

        this.setVisible(true);    //让界面显示出来,建议写在最后
    }

    //初始化数据(打乱)
    private void initDate() {
        //1、定义一个一维数组
        int[] tempArr ={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        //2、打乱数组中的数据的顺序
        //遍历数组,得到每一个元素,拿着每一个元素跟随即索引上的数据进行交换
        Random r = new Random();
        for (int i = 0; i < tempArr.length; i++) {
            //获取到随即索引
            int index = r.nextInt(tempArr.length);
            //拿着遍历到的每一个数据,跟随机索引上的数据进行交换
            int temp = tempArr[i];
            tempArr[i] = tempArr[index];
            tempArr[index] = temp;
        }

        //3、给二维数组添加数据
        for (int i = 0; i < tempArr.length; i++) {
            date[i/4][i%4] = tempArr[i];
        }
    }

    
    //初始化图片
    //由于二维数组已经创建好,所以添加图片的时候,就需要按照二维数组中管理的数据进行添加图片了
    //因此之前的number就不需要了,就u、需要删掉了
    private void initImage() {
        //外循环--结合内循环的前提下,从上到下依次添加一行,一共添加四行,Y值依次增加105像素,X值不变
        for (int i = 0; i < 4; i++) {
            //内循环--在每一行从左到右依次添加一张图片,一共添加四张,X值依次增加105像素,Y值不变
            for (int j = 0; j < 4; j++) {
                //获取当前要加载图片的序号,date[i][j]的结果是这个位置上对应的数据值
                int num = date[i][j];
                //然后创建一个JLabel的对象(即管理容器)
                JLabel jLabel = new JLabel(new ImageIcon("image\\animal\\animal3\\"+num+".jpg"));
                //根据坐标,设置图片的位置
                //图片美化1:给每一张小图片分别添加83和134像素的位移,让它们做一个整体的偏移,使15张小图片在中央偏下方显示
                jLabel.setBounds(105*j + 83,105*i + 134,105,105);

                //图片美化3:给小图片添加边框
                //BevelBorder斜面边框对象
                //0:表示让图片凸起来,英文常量为RAISED        1:表示让图片凹下去,英文常量为LOWERED
                jLabel.setBorder(new BevelBorder(1));

                //利用getContentPane()获取到隐藏容器,然后用add()把图片添加到JFrame的隐藏容器中
                this.getContentPane().add(jLabel);
            }
        }

        /**路径分为两种:(绝对路径和相对路径)
         * 绝对路径:一定是从盘符开始的。例如:D:\IDEA2020\IDEAprocejts\PinTuYouXi\image\background.png
         * 相对路径:不是从盘符开始的。相对路径是相对当前项目而言。例如:aaa\\bbb 表示在当前项
         *          目下,去找aaa文件夹,里面再找bbb文件夹。下面image\\background.png
         *  运行之后如果图片消失不显示了,肯定是路径方面有问题,可以各种尝试修改代码中的路径,就可以解决掉这个问题
         */

        //图片美化2:添加背景图片
        //小细节:先加载的图片在上方,后加载的图片在下方,
        //所以得先加载小图片,再加载背景图片,即添加背景图片的代码添加在添加小图片代码的下面
        JLabel background = new JLabel(new ImageIcon("image\\background.png"));//利用相对路径优化了图片的路径
        background.setBounds(40,40,508,560);
        //把背景图片添加到界面当中
        this.getContentPane().add(background);
    }

    //创建初始化菜单的方法
    private void initJMenuBar() {
        //1、创建整个的菜单对象
        JMenuBar jMenuBar = new JMenuBar();

        //2、创建菜单上面的两个选项的对象(功能 关于我们)
        JMenu functionJMenu = new JMenu("功能");
        JMenu aboutJMenu = new JMenu("关于我们");

        //3、创建选项下面的条目对象(重新游戏 重新登录 关闭游戏)
        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");

        JMenuItem accountItem = new JMenuItem("公众号");

        //4、将每一个选项下面的条目对象添加到选项当中
        functionJMenu.add(replayItem);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);

        aboutJMenu.add(accountItem);

        //5、将菜单里面的两个选项添加到菜单当中
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);

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

    //创建初始化界面的方法
    private void initJFrame() {
        this.setSize(603,680);    //设置窗口的属性(宽 高)
        this.setTitle("拼图单机版 v1.0");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置页面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setLayout(null);   //取消图片默认的居中防放置,只有取消了才会按照XY轴的形式添加组件
    }
}
package YouXiUI;

/**注册界面业务逻辑
 * 获取用户输入的用户名
 * 获取用户输入的密码(两次)
 * 比较两次的密码是否一致
 * 判断当前用户是否已经注册等等
 */
import javax.swing.*;

public class RegisterJFrame extends JFrame {

    public RegisterJFrame(){
        this.setSize(488,500);
        this.setTitle("拼图 注册");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置界面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setVisible(true);  //设置窗口为显示
    }
}
package YouXiUI;

/**登录界面的业务逻辑
 * 获取用户输入的用户名
 * 获取用户输入的密码
 * 生成一个验证码
 * 获取用户输入放入验证码
 * 比较用户名、密码、验证码等等
 */
import javax.swing.*;

public class LoginJFrame extends JFrame {

    public LoginJFrame(){

        this.setSize(488,430);  //this代表当前对象(当前的LoginJFrame对象)
        this.setTitle("拼图 登录");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置界面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setVisible(true);  //设置窗口为显示
    }
}
import YouXiUI.GameJFrame;
import YouXiUI.LoginJFrame;
import YouXiUI.RegisterJFrame;

public class App {
    //开发过程中,main方法为程序的启动入口
    public static void main(String[] args) {

        //如果我们想要开启一个界面,就创建谁的对象就可以了
        //new LoginJFrame();  //  创建登录界面对象
        //new RegisterJFrame();   //创建注册界面
        new GameJFrame();   //创建游戏的主界面

    }
}

7 上下左右移动

向上移动实际上就是把空白方块下方的图片上移。

逻辑分析:每一张小图片其实都是跟一个唯一的数字对应,而这些数字都放在了二维数组当中,我们只需要找两个数字在二维数组中的位置,然后做一下数据交换,交换完之后。根据最新的数字加载相应的图片就可以实现图片的移动了。

黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)_第16张图片

 首先我们要实现的不是上下左右移动。而是需要先给整个界面添加键盘监听事件。因此要让GameJFrame调用键盘监听接口(调用一个接口时,记得要重写里面所有的抽象方法)

代码实现如下:(相比于上一步的代码,只修改了GameJFrame部分的代码)

package YouXiUI;

/**主界面业务逻辑
 * 上下左右移动的代码逻辑
 * 统计步数的代码逻辑
 * 一键通关、查看最终效果、恶搞好友等等
 */
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;

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

public class GameJFrame extends JFrame implements KeyListener {


    int[][] date = new int[4][4];
    //记录空白方块在二维数组中的位置
    int x = 0;
    int y = 0;

    //构造方法
    public GameJFrame(){
        initJFrame();   //调用initJFrame()方法初始化界面
        initJMenuBar();     //调用initJMenuBar()方法,初始化菜单
        initDate();     //初始化数据(打乱)
        initImage();    //初始化图片(根据打乱之后的结果去加载图片)

        this.setVisible(true);    //让界面显示出来,建议写在最后
    }

    //初始化数据(打乱)
    private void initDate() {
        //1、定义一个一维数组
        int[] tempArr ={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        //2、打乱数组中的数据的顺序
        //遍历数组,得到每一个元素,拿着每一个元素跟随即索引上的数据进行交换
        Random r = new Random();
        for (int i = 0; i < tempArr.length; i++) {
            //获取到随即索引
            int index = r.nextInt(tempArr.length);
            //拿着遍历到的每一个数据,跟随机索引上的数据进行交换
            int temp = tempArr[i];
            tempArr[i] = tempArr[index];
            tempArr[index] = temp;
        }
        //3、给二维数组添加数据
        for (int i = 0; i < tempArr.length; i++) {
            if(tempArr[i] == 0){    //找到0的时候,用x和y记录0的位置
                x = i / 4;
                y = i % 4;
            } else  {
                date[i/4][i%4] = tempArr[i];
            }
        }
    }

    //初始化图片
    //根据二维数组中的数字进行图片加载
    private void initImage() {
        //清空已经出现的所有图片,然后再运行循环,让图片进行重新加载
        this.getContentPane().removeAll();

        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                //获取当前要加载图片的序号,date[i][j]的结果是这个位置上对应的数据值
                int num = date[i][j];
                //然后创建一个JLabel的对象(即管理容器)
                JLabel jLabel = new JLabel(new ImageIcon("image\\animal\\animal3\\"+num+".jpg"));
                jLabel.setBounds(105*j + 83,105*i + 134,105,105);
                jLabel.setBorder(new BevelBorder(1));   //图片美化3:给小图片添加边框
                //利用getContentPane()获取到隐藏容器,然后用add()把图片添加到JFrame的隐藏容器中
                this.getContentPane().add(jLabel);
            }
        }

        JLabel background = new JLabel(new ImageIcon("image\\background.png"));//图片美化2:添加背景图片
        background.setBounds(40,40,508,560);
        //把背景图片添加到界面当中
        this.getContentPane().add(background);

        //刷新一下加载图片后的界面
        this.getContentPane().repaint();
    }

    //创建初始化菜单的方法
    private void initJMenuBar() {
        //1、创建整个的菜单对象
        JMenuBar jMenuBar = new JMenuBar();

        //2、创建菜单上面的两个选项的对象(功能 关于我们)
        JMenu functionJMenu = new JMenu("功能");
        JMenu aboutJMenu = new JMenu("关于我们");

        //3、创建选项下面的条目对象(重新游戏 重新登录 关闭游戏)
        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");

        JMenuItem accountItem = new JMenuItem("公众号");

        //4、将每一个选项下面的条目对象添加到选项当中
        functionJMenu.add(replayItem);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);

        aboutJMenu.add(accountItem);

        //5、将菜单里面的两个选项添加到菜单当中
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);

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

    //创建初始化界面的方法
    private void initJFrame() {
        this.setSize(603,680);    //设置窗口的属性(宽 高)
        this.setTitle("拼图单机版 v1.0");    //设置界面标题
        this.setAlwaysOnTop(true);  //设置页面置顶
        this.setLocationRelativeTo(null);   //设置界面居中,默认情况下页面是在左上角的位置
        this.setDefaultCloseOperation(3);   //设置程序关闭的方式
        this.setLayout(null);   //取消图片默认的居中防放置,只有取消了才会按照XY轴的形式添加组件
        //给整个界面添加监听事件
        this.addKeyListener(this);
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {

    }

    @Override
    public void keyReleased(KeyEvent e) {
        //对上、下、左、右进行判断
        //左:37  上:38  右:39  下:40
        //用code记录用户按下的键盘
        int code = e.getKeyCode();
        //对code的值进行判断,然后执行对应的操作
        if(code == 37){
            if(y == 3){
                //表示空白方块右面已经没有图片了,空白块就在最右面的一列
                return;
            }
            /**向左移动逻辑:[空白][数字]--->[数字][空白]
             * x,y 表示空白方块        x,y+1 表示空白块右边的数字
             * 把右边的数字赋值给空白方块,然后让右边的数值为0,
             * 交换数据之后,更新一下当前空白块对应的x,y值
             */
            date[x][y] = date[x][y+1];
            date[x][y+1] = 0;
            y++;//空白方块相当于向右移动了,更新一下y的值
            //调用方法按照最新的数字加载图片
            initImage();

        } else if(code == 38){
            if(x == 3){
                //表示空白方块下面已经没有图片了,空白块就在最下面一行
                return;
            }
            /**向上移动逻辑:[空白]    [数字]
             *             [数字]--->[空白]
             * x,y 表示空白方块        x+1,y 表示空白方块下的数字
             * 把下边的数字赋值给空白方块,然后让下边的数值为0,
             * 交换数据之后,更新一下当前空白块对应的x,y值
             */
            date[x][y] = date[x+1][y];
            date[x+1][y] = 0;
            x++;//空白方块相当于向下移动了,更新一下x的值
            //调用方法按照最新的数字加载图片
            initImage();

        } else if(code == 39){
            if(y == 0){
                //表示空白方块左面已经没有图片了,空白块就在最左面的一列
                return;
            }
            /**向右移动逻辑:[数字][空白]--->[空白][数字]
             * x,y 表示空白方块        x,y-1 表示空白块左边的数字
             * 把左边的数字赋值给空白方块,然后让左边的数值为0,
             * 交换数据之后,更新一下当前空白块对应的x,y值
             */
            date[x][y] = date[x][y-1];
            date[x][y-1] = 0;
            y--;//空白方块相当于向左移动了,更新一下y的值
            //调用方法按照最新的数字加载图片
            initImage();
        }
        else if(code == 40){
            if(x == 0){
                //表示空白方块上面已经没有图片了,空白块就在最上面一行
                return;
            }
            /**向下移动逻辑:[数字]    [空白]
             *             [空白]--->[数字]
             * x,y 表示空白方块        x-1,y 表示空白方块上方的数字
             * 把上边的数字赋值给空白方块,然后让上边的数值为0,
             * 交换数据之后,更新一下当前空白块对应的x,y值
             */
            date[x][y] = date[x-1][y];
            date[x-1][y] = 0;
            x--;//空白方块相当于向上移动了,更新一下x的值
            //调用方法按照最新的数字加载图片
            initImage();
        }
    }
}

上下左右移动业务小结:

1、给本类实现KeyListener接口,并重写所有抽象方法。

2、给整个界面添加键盘监听事件。(因为我们是在游戏界面中按上下左右键去移动的)

3、统计一下空白方块对应的数字0在二维数组中的位置,因为移动方块的本质就是让空白方块对应的数字0跟自己上下左右的数字进行数值交换。

4、在KeyReleased方法当中实现移动的逻辑。(如果用按下按键的方法去写,则如果按住键盘不送,则会一直移动,不符合游戏逻辑,所以用的抬起按键的方法)

5、Bug修复:

(1)、当空白块在最下方时,无法再次进行上移

(2)、当空白块在最上方时,无法再次进行下移。

(3)、当空白块在最左方时,无法再次进行右移。

(4)、当空白块在最右方时,无法再次进行左移。

你可能感兴趣的:(黑马程序员Java基础课程,学习,java,1024程序员节)