자바 기초단계 프로젝트 - 직소퍼즐 게임(해설 및 소스코드 포함)

성명

본 프로젝트는 자바 기반의 스테이지 프로젝트로 기본적인 문법과 판단, 루프, 배열, 스트링, 콜렉션 등과 같은 문법 기초 등의 객체지향 지식과 캡슐화, 상속, 다형성 등의 객체지향 지식을 포함하는 프로젝트입니다. , 추상화 클래스, 인터페이스, 내부 클래스 등이 모두 관련됩니다. 이 프로젝트에는 많은 콘텐츠가 포함되어 있으며 초보자로서 이전 지식을 잘 결합할 수 있습니다. 이 프로젝트는 이전의 기본 지식을 연습하고 복습하기에 좋은 선택입니다.

게임 쇼

여기에 이미지 설명 삽입여기에 이미지 설명 삽입

재료 획득

참고: 이 자료의 각 사진은 15개 부분으로 나뉘고 각 사진은 정사각형이며 길이와 너비는 105픽셀입니다. 링크 :
https://pan.baidu.com/s/16maOd105xKqrWZSd6IScig?pwd=1234

게임 창

게임 창은 주로 Java에서 제공하는 JFrame 클래스를 사용하는데 이 클래스는 우리에게 창을 제공할 수 있으며 이 창의 크기, 위치, 표시 여부 등을 설정할 수 있습니다. 우리 뒤에 있는 모든 비즈니스
여기에 이미지 설명 삽입
논리는 이 창에서 구현되어야 하며 다음은 구현된 코드입니다.

JFrame jf = new JFrame();
jf.setSize(400, 400);
jf.setVisible(true);

JFrame은 Java에서 제공하는 클래스이므로 JFrame을 직접 사용하여 객체를 생성할 수 있으며 그 안에 있는 setSize() 메소드는 윈도우의 크기를 설정하는 것으로 단위는 픽셀이며 여기서 주의할 점은 크기를 설정한 후 실행할 수 없습니다. JFrame은 기본적으로 인터페이스 창을 숨깁니다. 표시하려면 setVisible() 메서드를 사용해야 합니다. true는 표시하고, 그렇지 않으면 false는 마침표를 숨깁니다.

게임에서 표시하려면 게임 인터페이스, 로그인 인터페이스 및 등록 인터페이스의 세 가지 인터페이스가 필요합니다.

따라서 우리는 3개의 JFrame 객체를 생성해야 하며 여기서 질문에 대해 생각해야 합니다: 새 클래스 파일을 생성한 다음 기본 메서드에서 3개의 JFrame 객체를 생성한 다음 기본 메서드에서 모든 비즈니스 로직을 작성합니까? 결과는 NO , Big NO, 특수 NO, 그때까지 하나의 주요 메서드에 천 줄 이상의 코드가 작성되고 그때까지 BUG가 있을 것이며 어디에 있는지 모르겠습니다. 그래서 게임인터페이스, 로그인인터페이스, 등록인터페이스 3가지 카테고리를 제안하고 새로운 클래스를 생성하고 메인메소드를 이용하여 게임을 실행시키고 메인메소드에는 설정과 같은 Call메소드를 위한 3개의 윈도우 클래스객체를 생성한다. 창 크기 등등. 이런 식으로 해당 창에서 비즈니스 로직을 수행해야 하는 경우 해당 클래스로 이동하여 수행할 수 있습니다. 일반적인 아이디어는 다음과 같습니다.
이것은 게임 프로그램 실행 항목입니다.

import ui.GameFrame;
import ui.LoginJFrame;
import ui.RegisterJFrame;

public class App {
    
    
    public static void main(String[] args) {
    
    
        //表示程序的启动入口
        new GameFrame();

        new LoginJFrame();

        new RegisterJFrame();
    }
}

다음은 GameFrame(게임 인터페이스) 클래스입니다.
여기서는 빈 매개변수 구조를 사용하여 창 크기와 디스플레이 인터페이스를 설정하므로 메인 메서드에서 GameFrame 객체를 생성할 때 GameFrame 빈 매개변수 구조를 호출하여 설정합니다. 메인 메소드에서 설정할 필요가 없도록 인터페이스

package ui;
import javax.swing.*;

public class GameFrame extends JFrame {
    
    

    public GameFrame(){
    
    
        this.setSize(603, 680);
        this.setVisible(true);
    }
}

다음은 GameFrame
(게임 인터페이스) 클래스와 동일한 LoginFrame(로그인 인터페이스) 클래스 입니다.

package ui;
import javax.swing.*;
public class LoginJFrame extends JFrame {
    
    

    public LoginJFrame(){
    
    
    
        this.setSize(488, 430);
        this.setVisible(true);
    }
}

다음은 RegisterFrame(등록 인터페이스) 클래스이며,
GameFrame(게임 인터페이스) 클래스와 아이디어는 동일합니다.

package ui;
import javax.swing.*;
public class RegisterJFrame extends JFrame {
    
    

    public RegisterJFrame(){
    
    
        this.setSize(488, 500);
        this.setVisible(true);
    }
}

일반 프레임워크가 구축된 후 해당 클래스에 해당 비즈니스 로직을 구축하기만 하면 되며, 우리 뒤에 있는 거의 모든 비즈니스 로직은 이 세 개의 창에서 구현됩니다.

창 설정

게임 창을 표시한 후 게임 창을 적절하게 설정하여 더 나은 게임 경험과 시각적 효과를 제공해야 합니다.다음으로 게임 인터페이스에 대해 다음 기능과 코드를 설정해야 합니다: 1. 게임
제목
게임 제목은 우리 게임의 이름이며 일반적으로 게임 인터페이스의 왼쪽 상단에 쓰여 있으며 효과는 아래 그림과 같습니다. 제목
여기에 이미지 설명 삽입
설정은 JFrame의 방법을 사용하며 코드는 다음과 같습니다.

//设置界面的标题
this.setTitle("拼图游戏单机版 V1.0");

2. 게임의 기본 인터페이스 상단에 고정하는 기능은
모든 사람이 잘 이해해야 합니다. 즉, 다른 소프트웨어를 열 때 상단에 고정하면 클릭할 때 여전히 상단 레이어에 표시될 수 있습니다. 다른 응용 프로그램 여기에서는 JFrame 메서드에서 하나를 사용합니다.

//设置界面置顶
//盖住其他所有软件
this.setAlwaysOnTop(true);

3. 게임 메인 인터페이스의 중심
게임 인터페이스의 중심이란 우리가 게임을 열 때 항상 컴퓨터 화면 중앙에 인터페이스가 나타나는 것을 의미합니다.방법은 다음과 같습니다.

//设置界面居中
this.setLocationRelativeTo(null);

이 메서드는 구성 요소 매개 변수를 전달해야 하므로 여기서는 null 4만 입력하면 됩니다.
게임 종료 모드 설정
게임 종료 모드에서 JFrame은 종료 메서드를 제공합니다.

//设置游戏的关闭模式
this.setDefaultCloseOperation(3);

이 메소드는 int 유형의 매개변수를 전달할 수 있습니다. 이 메소드에는 숫자 0, 1, 2 및 3에 해당하는 4개의 종료 모드가 있으며 이 메소드의 소스 코드를 입력하면 4개의 종료 모드를 볼 수 있습니다. 숫자 3은 종료 모드에 해당합니다: 창 중 하나를 닫으면 가상 머신 실행이 중지됩니다 게임 창 중 하나를 닫는 한 다른 로그인 창과 등록 창이 닫히는 것으로 이해할 수 있습니다 , 가상 머신이 실행을 중지합니다.

참고: 이것은 이것이 위치한 클래스를 나타냅니다. JFrame에서 세 개의 창 클래스를 상속했기 때문에 이를 직접 사용하여 부모 클래스 메서드를 호출할 수 있습니다.

메뉴 구성

게임은 필수 불가결한 메뉴입니다. 우리는 게임을 다시 시작하고 다시 로그인하는 등의 작업이 메뉴에 표시됩니다. 다음 그림은 게임 메뉴의 스타일입니다. 이러한 메뉴를 만들기 전에 먼저 Java를 이해해야 합니다
여기에 이미지 설명 삽입
. JMenuBar 클래스, JMenu 클래스 및 JMenuItem 클래스가 제공되며, 이 세 가지 유형 간의 관계를 이해하기 위해 아래 몇 가지 사진을 사용하겠습니다. 이 세 가지 유형을
여기에 이미지 설명 삽입
여기에 이미지 설명 삽입
이해한 후 이 세 가지 유형을 연결하여 GameFrame에 넣어야 합니다. 구현 단계:
1. 먼저 JMenuBar를 생성합니다
. 2. 그런 다음 JMenu를 생성합니다
. 3. 그런 다음 JMenuItem을 생성합니다
. 4. JMenuItem을 JMenu에 넣습니다
. 5. JMenu를 JMenuBar에 넣습니다.
6. 마지막으로 전체 JFrame 인터페이스에 JMenuBar를 추가합니다.

다음은 코드 구현입니다.

	//初始化菜单
    public 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 accountItem = new JMenuItem("公众号");

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

        aboutJMenu.add(accountItem);

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


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

여기서 초기화 메뉴를 메서드로 구성한 다음 GameFrame의 빈 매개변수 구조에서 직접 호출하여 다음과 같은 효과를 얻 습니다
여기에 이미지 설명 삽입
.
내 현재 GameFrame 클래스의 코드

public class GameFrame extends JFrame {
    
    

    public GameFrame(){
    
    

        //初始化界面
        initJFrame();

        //初始化菜单
        initJMenuBar();

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

    //初始化界面
    public void initJFrame(){
    
    
        //设置界面的宽高
        this.setSize(603, 680);

        //设置界面的标题
        this.setTitle("拼图游戏单机版 V1.0");

        //设置界面置顶
        //盖住其他所有软件
        this.setAlwaysOnTop(true);

        //设置界面居中
        this.setLocationRelativeTo(null);

        //设置游戏的关闭模式
        this.setDefaultCloseOperation(3);
    }


    //初始化菜单
    public 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 accountItem = new JMenuItem("公众号");

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

        aboutJMenu.add(accountItem);

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


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

사진 추가


창과 메뉴가 대략적 으로 구성되었으면 이제 메인 창에 그림을 추가해 보도록 하겠습니다. 게임 창 이해
여기에 이미지 설명 삽입
여기 게임 창은 실제로 세 부분으로 나뉩니다.
1. 제목 부분
2. 메뉴 부분
3. 숨겨진 컨테이너 부분
여기서 숨겨진 컨테이너 부분은 JFrame 객체를 생성할 때 이미 존재하므로 필요하지 않습니다. 개체를 재생성하려면 this.getContentPane() 메서드를 사용하여 호출한 다음 이를 사용하여 add() 메서드를 호출하여 사진을 추가할 수 있습니다. 중앙에서 다른 위치에 배치하려면 기본 스위치를 꺼야 합니다. 끄려면 다음 방법을 사용할 수 있습니다.

//取消默认的居中放置,只有取消了才会按照XY轴的形式添加组件
this.setLayout(null);

히든 컨테이너는 JFrame 객체가 생성될 때 생성되기 때문에 초기화 인터페이스인 initFrame()에서 이 setLayout 메소드를 사용할 수 있고, 나중에 이미지를 중간에 기본값을 배치하지 않고 XY축의 히든 컨테이너에 배치할 수 있습니다.

숨겨진 컨테이너의 기본 센터링 방법을 취소한 후 다음에 그림을 추가할 수 있습니다.

JFrame에 그림을 추가하는 것은 단순히 그림 주소를 JFrame에 직접 넣는 것이 아니라 ImageIcon 및 JLabel 클래스를 사용하는 것
입니다
. 전달되는 매개변수는 이미지 주소가 될 수 있습니다
2. 우리의 그림인 ImageIcon 개체를 저장할 JLabel 클래스를 만듭니다 이 JLabel은 그림과 텍스트 등을 저장할 수 있는 관리 컨테이너 컨테이너입니다 3.
JLabel에서 만든 개체를 사용합니다 그림을 지정하는 클래스 위치, 즉 XY 축, 여기서는 JLabel의 setBounds 메서드를 사용합니다. 여기서 전달하는 매개 변수는 (x, y, 너비, 높이), 즉 XY 축과 너비 및 이미지의 높이가 설정되고 여기에서 XY 축은 직각이 아닙니다. 좌표계의 경우 원점은 JFrame 창의 왼쪽 상단에 있습니다. 다음 그림은 더 나은 이해를 제공할 수 있습니다. 4. 마지막
여기에 이미지 설명 삽입
으로 JLabel에 의해 JFrame의 숨겨진 컨테이너로 생성된 객체

아래에서 위의 기능을 달성하기 위해 코드를 사용합니다.

    //初始化图片
    public void initImage(){
    
    

        //创建一个图片ImageIcon对象
        ImageIcon icon = new ImageIcon("D:\\Java Code\\puzzlegame\\image\\animal\\animal3\\1.jpg");
        //创建一个JLabel的对象(管理容器)
        JLabel jLabel = new JLabel(icon);
        //指定图片位置
        jLabel.setBounds(0, 0 , 105, 105);
        //把管理容器添加到界面中
        this.getContentPane().add(jLabel);
    }

여기에서는 GameFrame 클래스에 메서드를 만든 다음 GameFrame 클래스의 빈 매개 변수 구성에서 호출했습니다.

이렇게 작성하고 나면 효과는 이렇습니다.
여기에 이미지 설명 삽입
우리가 설정한 setBounds메소드의 매개변수는 x=0, y=0, 즉 원점에 위치하기 때문에 여기에서 좌표에 주의를 기울여야 합니다 . 그림의 왼쪽 위 모서리에 해당하는 좌표, 즉 그림의 좌표는 그림의 왼쪽 위 모서리에 해당하는 좌표이므로 당연히 결과는 정확합니다.

JFrame에 사진을 추가하는 방법을 알았으면 다른 사진을 추가해 보겠습니다.

무슨 생각하는지 알겠어 그만!!! 위의 코드를 열두번 써서 모든 사진을 추가하는건 정말 싫지 않나? 정답은 NO!!! 정말 해보고
싶다면
, 불가능하지 않은데 언제 사진 3장을 추가할 때 코드를 볼까요 규칙이 있나요? 다들 보기 쉽게 하기 위해 사진 3장을 추가한 후의 코드입니다

        //创建一个JLabel的对象(管理容器)放入ImageIcon对象
        JLabel jLabel1 = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\animal\\animal3\\1.jpg"));
        //指定图片位置
        jLabel1.setBounds(0, 0, 105, 105);
        //把管理容器添加到界面中
        this.getContentPane().add(jLabel1);

        //创建一个JLabel的对象(管理容器)放入ImageIcon对象
        JLabel jLabel2 = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\animal\\animal3\\2.jpg"));
        //指定图片位置
        jLabel2.setBounds(105, 0, 105, 105);
        //把管理容器添加到界面中
        this.getContentPane().add(jLabel2);

        //创建一个JLabel的对象(管理容器)放入ImageIcon对象
        JLabel jLabel3 = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\animal\\animal3\\3.jpg"));
        //指定图片位置
        jLabel3.setBounds(210, 0, 105, 105);
        //把管理容器添加到界面中
        this.getContentPane().add(jLabel3);

여기에서는 편의상 생성된 ImageIcon 객체를 JLabel 객체 생성 매개변수에 직접 넣었습니다.

여기서 우리는 이 세 가지 코드를 주의 깊게 관찰하고 규칙을 찾았는지 알려줄 필요가 있습니다.
그림이 추가될 때마다 그림의 위치와 그림의 주소만 변경된 것을 알 수 있습니다. 그림의 위치가 바뀌어도 여전히 규칙을 충족합니다. 즉, 그림의 각 행의 x축이 하나씩 105씩 추가됩니다. 즉, 첫 번째 행의 첫 번째 x는 0이고 두 번째 행은 105입니다. , 세 번째는 210... 등등, 첫 번째 행의 n 번째 Zhang의 x는 105 * (n - 1)이고, y는 항상 0이고, 두 번째 행은 첫 번째 행과 마찬가지로 y가 105가 되고, 세 번째 줄의 y는 210...등으로 첫 번째 줄은 n개 행의 y는 105 * (n - 1)입니다.

위의 아이디어로 이제 루프를 생각해야 합니다. 4개의 행과 4개의 열이 있기 때문에 행으로 4번 반복할 수 있고 열로 4번 반복할 수 있으므로 루프 중첩, 외부 루프 내부 루프를 사용할 수 있습니다. 그림을 추가하기 위한 코드 데모는 다음과 같습니다.

    public void initImage(){
    
    

        int number = 1;
        //外循环 --- 四行
        for (int i = 0; i < 4; i++) {
    
    
            //内循环 --- 一行的四张图片
            for (int j = 0; j < 4; j++) {
    
    
                //创建一个图片ImageIcon对象
                //创建一个JLabel的对象(管理容器)放入ImageIcon对象
                JLabel jLabel = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\animal\\animal3\\" + number + ".jpg"));
                //指定图片位置
                jLabel.setBounds(105 * j, 105 * i, 105, 105);
                //把管理容器添加到界面中
                this.getContentPane().add(jLabel);

                //number自增表示图片变化
                number++;
            }
        }
    }

GameFrame 클래스의 initImage() 메서드에 이 함수를 작성한 다음 빈 매개변수 구성에서 호출했습니다.

위의 코드에서 볼 수 있듯이 외부 루프는 행 수를 나타내는 데 사용되고 내부 루프는 열 수를 나타내는 데 사용됩니다.이 중첩 루프는 대략 내가 루프를 시작할 때 i = 0이 나타내는 것을 의미합니다. 첫 번째 행, 그리고 j는 열의 수를 나타내며, j가 점점 증가한다는 것은 열의 수가 점차 증가한다는 것을 의미합니다. 사진 배치 규칙을 충족합니다.사진의 첫 번째 줄이 추가된 후 자동으로 증가는 두 번째 행을 의미하고 내부 루프를 실행하고 두 번째 행에 4개의 사진을 추가한 다음 모든 사진이 나올 때까지 반복을 계속합니다. 사진에 추가됩니다

어떤 사람들은 숫자가 무엇을 의미하는지 물을 것입니다. 여기에서 다시 상기시켜 드리고 싶은데, 사진 이름을 추가하는 편의를 위해 다음 형식으로 설정하려고 합니다. 참고
: 여기에서는 사진을 15개 부분으로 나누고 JFrame, 여기에 전체 그림을 넣을 수 없습니다
여기에 이미지 설명 삽입
.ImageIcon 개체를 만들고 주소를 전달할 때 숫자를 사용하여 점차적으로 증가하여 모든 그림을 나타내고 컨테이너에 모든 그림을 추가하여 다음을 수행할 수 있습니다.
주소 값으로 사용되는 숫자의 의미를 이해하고 컨테이너에 해당 그림을 추가하고 주소에 숫자를 쓸 때 "+숫자+"를 쓰는 것을 기억하고 숫자를 직접 쓰지 마십시오. 다음과 같은 결과를 얻을 수 있다. I 그림을 15개로 나누기 때문에 마지막 그림이 누락됨
여기에 이미지 설명 삽입
i = 3, j = 3까지 루프가 실행되면 해당 그림이 없고 빈 그림이 표시된다. 그림을 이동하는 빈 위치

사진 순서 섞기

이제 JFrame에 사진을 추가하는 방법을 알아냈으니 다음으로 해야 할 일은 사진의 순서를 뒤섞는 것입니다. 사진의 순서를 뒤죽박죽으로 만들려면
int형 숫자 변수를 사용하여 계속해서 증가시키면서 사진을 하나씩 넣어줍니다. 1부터 16까지 순서대로 값이 더해지기 때문에 번호 순서를 어기면 그림의 순서를 어길 수 있을까?

1-16의 순서를 끊는 가장 쉬운 방법은 길이가 16인 1차원 배열을 설정한 다음 인덱스를 교환하여 순서를 끊는 것입니다.다음은 구현을 위한 코드입니다.

		//创建一个一维数组
        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;
        }

        //遍历一维数组
        for (int i = 0; i < Arr.length; i++) {
    
    
            System.out.print(Arr[i] + " ");
        }

여기서 주목해야 할 것은 1차원 배열을 위해 1-16을 누르지 않고 0-15를 누르는 이유는 내 그림 이름이 1-15뿐이고 여분의 0, 즉 0이 인식될 때 , 자료에서 찾을 수 없으면 자동으로 비어 있습니다. 즉, 빈 그림, 그림을 이동할 때 흰색 프레임입니다.

최종 출력 결과:
여기에 이미지 설명 삽입
분명히 순서가 완전히 깨졌습니다.순서가 깨진 후 초기화 이미지 메소드에 직접 넣을 수 있습니까?아니, 아니, 아니, 이미지 인덱스를 더 우아하게 만들어야 합니다. 4 × 4로 표시되는 경우 인덱스도 4 × 4로 표시되지 않는 이유는 무엇입니까?

4 × 4 인덱스 배열 가장 먼저 떠오르는 것은 2차원 배열이어야 하므로 이제 1차원 배열을 2차원 배열로 변환하기만 하면 루프 중첩을 사용하여 표현할 수 있으며 점진적으로 변환할 수 있습니다. 1차원 배열 내부에 요소를 추가합니다. 다음은 구현된 코드입니다.

		//将一维数组添加到二维数组中
        int[][] newArr = new int[4][4];

        int index = 0;
        for (int i = 0; i < 4; i++) {
    
    
            for (int j = 0; j < 4; j++) {
    
    
                newArr[i][j] = Arr[index];
                index++;
            }
        }

        //遍历二维数组
        for (int i = 0; i < newArr.length; i++) {
    
    
            for (int j = 0; j < newArr[i].length; j++) {
    
    
                System.out.print(newArr[i][j] + " ");
            }
            System.out.println();
        }

최종 출력 결과(1차원 배열 및 2차원 배열)
여기에 이미지 설명 삽입
마지막으로 이 2차원 배열의 데이터를 이미지 초기화 방법으로 대체하기만 하면 됩니다.
참고: 2차원 배열 생성이 가장 잘 작성됩니다. GameFrame 클래스의 멤버 위치에 여기에 표시의 편의를 위해 초기화 데이터 메서드에 씁니다. 초기화 이미지 메서드에서 사용해야 하는 2차원 배열이기 때문에 멤버 위치에 쓰면 사용할 수 있습니다. 직접, 그래서 이 초기화 데이터 방법은 2차원 배열 할당을 제공하는 것과 동일하며, 그림을 초기화하는 방법은 2차원 배열의 데이터를 직접 사용할 수 있습니다.

다음은 초기화 이미지 메서드에서 2차원 배열을 사용하여 이미지 순서를 섞는 방법입니다.

		//外循环 --- 四行
        for (int i = 0; i < 4; i++) {
    
    
            //内循环 --- 一行的四张图片
            for (int j = 0; j < 4; j++) {
    
    
                //获取加载当前图片的序号
                int num = newArr[i][j];

                //创建一个图片ImageIcon对象
                //创建一个JLabel的对象(管理容器)放入ImageIcon对象
                JLabel jLabel = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\animal\\animal3\\" + num + ".jpg"));
                //指定图片位置
                jLabel.setBounds(105 * j, 105 * i, 105, 105);
                //把管理容器添加到界面中
                this.getContentPane().add(jLabel);

            }
        }

이전에는 이미지 주소를 호출할 때 number를 사용했는데, 지금은 2차원 배열에서 교란된 값으로 호출합니다. 그래서 1차원 배열을 미리 2차원 배열로 변환해야 했습니다.
최종 결과는 이렇습니다.
여기에 이미지 설명 삽입
결과는 참으로 우리의 예상과 일치하며 이전 그림과 비교하여 완전히 중단됩니다. 허스키.

인터페이스를 아름답게

사진 순서를 엉망으로 만든 후 인터페이스 미화를 시작할 수 있습니다 게임 인터페이스의 주요 미화는 게임 배경 이미지를 추가하고 이미지에 사이드 스팬을 추가하고 이미지와 배경 이미지를 중간 및 하단 위치 (여러 개 먼저 시도해보십시오. 중간 및 하단 위치가 가장 좋습니다)
1. 사진의 위치를 ​​이동하십시오.

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

위는 그림을 추가할 때 중첩 루프에 있는 코드, 즉 그림의 위치를 ​​조정하는 코드입니다. 는 x+83이고 전체 134픽셀 아래로 이동합니다. 즉, y+134의 위치가 가장 좋습니다.

2. 게임 배경 이미지 추가
게임 배경 이미지 추가는 여전히 그림을 추가하는 방법이지만 한 가지 매우 중요한 점이 있습니다.
먼저 추가된 그림이 맨 위에 있고 나중에 추가된 그림이 맨 아래에 있으며 이는 반대일 수 있습니다. 다른 언어의 경우, 여기에 주의하시기 바랍니다. 따라서 배경 이미지를 추가하는 코드는 이미지 추가 주기 후에 작성해야 하며 initImage() 메서드 내에서 코드 구현은 다음과 같습니다.

//添加背景图片
//创建ImageIcon对象
ImageIcon bg = new ImageIcon("image\\background.png");
//创建JLabel容器对象
JLabel backgound = new JLabel(bg);
backgound.setBounds(40,40,508,560);
//把背景图片添加到界面当中
this.getContentPane().add(backgound);

배경 이미지는 시도해본 결과 x=40, y=40에 배치했을 때 가장 잘 보입니다.

3. 그림 테두리 추가
그림 테두리 추가는 테두리를 찾아서 추가할 필요가 없습니다.
의 중첩된 루프 내에 그림을 추가하여 작성

JLabel jLabel = new JLabel(new ImageIcon("image\\animal\\animal3\\" + num + ".jpg"));
//指定图片位置
jLabel.setBounds(105 * j + 83, 105 * i + 134, 105, 105);
//给图片添加边框
//0:让图片凸起来
//1:让图片凹下去
jLabel.setBorder(new BevelBorder(1));
//把管理容器添加到界面中
this.getContentPane().add(jLabel);

setBorder() 메서드는 BevelBorder 개체를 전달할 수 있습니다.내부 매개 변수에는 두 가지 옵션이 있습니다. 하나는 0, 다른 하나는 1, 0은 그림을 볼록하게 만드는 테두리를 추가하는 것이고 1은 볼록하게 만드는 테두리를 추가하는 것입니다. 사진이 오목합니다. 직접 해보실 수 있습니다. 개인적으로 오목한 것이 더 좋아 보인다고 생각합니다.

위의 미화 코드를 완성한 결과 아래 사진이
여기에 이미지 설명 삽입
이전보다 훨씬 좋아 보입니다.

이벤트 리스너

이벤트 모니터링에 대한 간단한 이해는 자바가 응답하기 위해 마우스와 키보드에서 다양한 작업을 수행한 것을 인식한다는 것입니다. 마치 이 게임에서 ↑ 키를 누르면 이렇게 공백 아래 그림이 위로 이동하고, Java는 ↑ 키를 눌렀다는 것을 인식하고 아래의 빈 그림을 위로 이동하는 것을 이벤트 모니터링이라고 합니다 이벤트 모니터링과 관련하여
Java는 직접 사용할 수 있는 세 가지 인터페이스를 제공합니다. 모니터링 ), KeyListener(키보드 모니터링)에서 동작 모니터링은 다른 두 가지의 단순화된 버전으로 마우스 왼쪽 버튼 클릭과 스페이스 키보드만 인식할 수 있으며, 마우스 모니터링은 클릭, 길게 누르기, 놓기, 그리기를 인식할 수 있으며, 그리고 끌기, 누르기, 누르기, 타이핑, 놓기 등을 키보드 모니터에서 인식할 수 있습니다.

이제 연습을 시작하겠습니다.
이벤트 리스너는 인터페이스이므로 GameFrame 클래스에서 인터페이스를 직접 호출합니다.

public class GameFrame extends JFrame implements KeyListener

인터페이스를 호출한 후 인터페이스의 모든 메소드를 다시 작성해야 하며 총 3가지 메소드가 있습니다.

    @Override
    public void keyTyped(KeyEvent e) {
    
    

    }

    @Override
    public void keyPressed(KeyEvent e) {
    
    
        
    }

    @Override
    public void keyReleased(KeyEvent e) {
    
    
        
    }

여기서는 keyReleased 방식을 주로 사용하는데, 즉 release, 즉 내가 눌렀다 뗀 후에야 인식이 되고, 계속 누르고 있으면 인식이 안 되는 것
입니다
. 인덱스에 따라 그림을 추가한 다음 인덱스에 따라 그림을 이동합니다. 이동 후 데이터를 초기화 그림 방식으로 주면 구현이 가능합니다. 그림을 이동하므로 대략적으로 구현하는 논리는 다음과 같습니다.
1. 빈 그림의 위치 구하기
다음은 초기화 데이터 방식에서 2차원 배열에 1차원 배열을 추가하는 코드이다. 사진을 찍고 그 인덱스를 구합니다.빈 사진 위치를 기록하기 위해 멤버 위치에 x와 y를 설정했습니다(키보드 모니터 재정의 방법도 x와 y를 사용하므로). 여기서 x와 y y는 실제로 앞에서 설정한 2차원 배열의 i와 j를 4×4 테이블로 나열하면 실제로는 행과 열

        //将一维数组添加到二维数组
        int index = 0;
        for (int i = 0; i < 4; i++) {
    
    
            for (int j = 0; j < 4; j++) {
    
    
                if(Arr[index] == 0){
    
    
                    x = i;
                    y = j;
                }else{
    
    
                    newArr[i][j] = Arr[index];
                }
                index++;
            }
        }

2. keyReleased 방식으로 이동 로직을 구현 이미지 이동의 원리는 사실 공백 이미지를 주변 이미지와 교환하는 것입니다. 이전에는 공백 이미지의 위치를 ​​알고 있었습니다. 이제 여기에서
교환을 주로 구현합니다. int code = e
.getKeyCode(); 인터페이스는 키를 식별하는 방법을 제공합니다. 키보드의 거의 모든 키에 이름을 지정합니다. 이름은 int 유형의 숫자이므로 나중에 코드를 직접 판단한 다음 해당 교환을 수행할 수 있습니다. exchange는 인덱스 교환입니다. , 교환할 사진은 아래로 이동, 즉 교환할 사진의 x 변경 없음, y는 +1이어야 하며 교체된 사진의 위치는 빈 사진과 교체되므로 해당 인덱스를 0. 마지막으로 빈 그림이 이동된 후 그 위치도 변경된다는 점을 잊지 마십시오. 빈 그림

    @Override
    public void keyReleased(KeyEvent e) {
    
    
        //对上,下,左,右进行判断
        //左:37, 右:39, 上:38, 下:40
        int code = e.getKeyCode();
        
        if(code == 37){
    
    
            System.out.println("向左移动");
            if(y == 3){
    
    
                return;
            }

            newArr[x][y] = newArr[x][y + 1];
            newArr[x][y + 1] = 0;
            y++;
            //调用方法按最新的方法加载图片
            initImage();


        }else if(code == 38){
    
    
            System.out.println("向上移动");
            if(x == 3){
    
    
                //表示方块已经已经在最下方了,他的下面没有图片再能移动了
                return;
            }

            //把空白方块下方的数字赋值给空白方块
            newArr[x][y] = newArr[x + 1][y];
            newArr[x + 1][y] = 0;
            x++;
            //调用方法按最新的方法加载图片
            initImage();


        }else if(code == 39){
    
    
            System.out.println("向右移动");

            if(y == 0){
    
    
                return;
            }

            newArr[x][y] = newArr[x][y - 1];
            newArr[x][y - 1] = 0;
            y--;
            //调用方法按最新的方法加载图片
            initImage();


        }else if(code == 40){
    
    
            System.out.println("向下移动");
            if(x == 0){
    
    
                return;
            }

            newArr[x][y] = newArr[x - 1][y];
            newArr[x - 1][y] = 0;
            x--;
            //调用方法按最新的方法加载图片
            initImage();
        }
    }

3. 이동 후 효과의 표시 및 새로 고침
위의 코드를 완료하면 실행 후 이동하지 않는 것을 알 수 있습니다. 인덱스를 수정한 후 이때 이동 후의 효과는 메인 인터페이스에 다시 배치되지만 첫 번째 초기화 그림으로 가려집니다. 즉, 먼저 추가된 그림이 맨 위에 있고 나중에 추가된 그림이 있습니다 . 가 맨 아래에 있습니다 . 잊지 마세요, 그래서 우리는 이동할 때마다 이동 후 콘텐츠가 표시되도록 initImage 메소드 앞에 이전 메인 인터페이스의 콘텐츠 지우기를 구현해야 합니다.

//清空原本已经出现的所有图片
//清空以后才会出现移动后的图片,不然被覆盖在下面了
this.getContentPane().removeAll();

위의 코드는 initImage()에 작성해야 하고, 맨 앞에 작성해야
하고, 새로고침은 initImage() 메소드 맨 아래에 작성하면 됩니다.

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

4. BUG 수정
위의 모든 코드를 작성한 후 실제로 게임을 실행할 수 있지만 게임 중에 빈 그림이 맨 위에 있을 때 ↓ 키를 누르면 프로그램에서 예외가 발생하는 것을 발견했습니다. , 정상적으로
재생이 되긴 하지만.. 그래도 좀 불편하네요 뭔일이죠? 경계 밖으로 움직이게 하면 확실히 움직일 수 없게 됩니다. x와 y가 경계에 있는지 판단하고 만약 그렇다면 모바일 코드를 실행하지 않고 종료만 하면 구현 코드는 2에 있다. keyReleased 메소드에서 이동 로직 구현

전체 이미지 보기

우리는 게임을 할 때 가끔 게임의 전체 그림이 무엇인지 모르고 퍼즐을 어떻게 이동해야 하는지 모르기 때문에 단축키를 눌러 전체 그림을 표시하는 기능을 깨달아야 합니다. 예를 들어 A 키를 누르면 A 버튼을 누르고 누르고 있으면 현재 퍼즐의 전체 그림이 게임의 메인 인터페이스에 나타나고 우리가 플레이하고 있는 인터페이스를 보내면 다시 표시됩니다. 떨어져 있는. 1. A 키를 누르면 키보드 모니터링이 나오므로 키보드 모니터링을 이용해야 합니다
.
다시 쓰기 방법 사용
2.
A 버튼을 누르고 있으면 전체 그림이 표시되고 손을 떼면 게임 인터페이스로 돌아갑니다.여기서 keyPressed() 및 keyReleased() 메서드에 코드를 작성해야 합니다. 그것, keyPressed() 메서드의 내용이 실행되고, 우리가 그것을 놓으면 keyReleased() 메서드의 내용이 실행됩니다
3. 인터페이스를 지우고 사진을 표시합니다
.누르면 모든 사진이 우리의 게임은 깨끗하고 완전한 그림이 메인 인터페이스에 표시될 것입니다. 우리가 그것을 출시할 때 우리는 이전 게임 콘텐츠를 표시하기 위해 initImage() 메서드를 직접 호출할 수 있습니다.

다음은 코드 구현입니다.

    @Override
    public void keyPressed(KeyEvent e) {
    
    
        int code = e.getKeyCode();
        if(code == 65){
    
    
            //把界面中的所有图片全部清除
            this.getContentPane().removeAll();
            //加载第一张完整的图片
            JLabel all = new JLabel(new ImageIcon("image\\animal\\animal3\\all.jpg"));
            all.setBounds(83, 134, 420, 420);
            this.getContentPane().add(all);

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

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

위 단락은 A 키를 누르고 그림의 모든 코드를 지우고 새로 고침할 때 인식하는 것입니다.

else if(code == 65){
    
    
      initImage();

위 문단은 keyReleased() 메서드의 else if()인데, 버튼이 그림을 움직인다고 판단했을 때 판단이 더해졌기 때문에 지금은 바로 뒤에 else if()를 쓰고 직접 호출하면 된다. initImage() 메서드, 이 메서드는 이전 콘텐츠를 모두 지우고 게임 이미지와 배경 이미지를 다시 표시합니다.

이미지 경로 최적화

우리는 우리가 사용하는 그림이 고정되어 있고 경로가 고정되어 있으며 변경되지 않을 것임을 알 수 있습니다.그림을 변경하려면 클래스의 모든 경로를 다시 작성해야 하는데 이는 매우 번거로운 일입니다.더 편리하도록 지금 최적화합시다. 나중에 변경할 때 편리하게 제가 원하는 것은 클래스의 멤버 위치에 문자열 형태의 경로 변수를 추가한 후 나중에 경로를 사용해야 하는 메소드에서 직접 경로를 호출하는 것입니다. 경로를 직접 수정하면 됩니다. 다음은
수행한 코드 입니다.

String path = "image\\animal\\animal3\\";

그런 다음 경로가 필요한 경로로 변경하십시오.

JLabel jLabel = new JLabel(new ImageIcon(path + num + ".jpg"));
JLabel all = new JLabel(new ImageIcon(path + "all.jpg"));

이런 식으로 나중에 그림을 수정하고 싶다면 경로를 직접 수정할 수 있습니다.

치트 코드

모두 여기 치트 코드를 알고 있으므로 자세히 설명하지 않겠습니다. 여기에 코드가 직접 있습니다.

else if(code == 87){
    
    
            //作弊码
            //重写给二维数组赋值,初始化二维数组
            newArr = new int[][]{
    
    
                    {
    
    1,2,3,4},
                    {
    
    5,6,7,8},
                    {
    
    9,10,11,12},
                    {
    
    13,14,15,0},
            };
            //调用上面的二维数组进行初始化图片,直接通关
            initImage();
        }

여전히 keyReleased() 메서드에서 else if()를 사용합니다. 여기서는 W 키(87에 해당)를 설정하고 2차원 배열을 직접 초기화합니다. 즉, 통관 순서에 따라 2차원 배열을 설정합니다. , 그리고 initImage()를 직접 호출 이 메소드는 아래 그림과 같이 통관 후 모습을 표시할 수 있습니다.
여기에 이미지 설명 삽입

승리를 심판하다

승리를 판단하는 것은 실제로 치트 코드와 매우 유사하며 현재 2차원 배열 데이터가 전체 그림의 데이터와 동일한지, 즉 치트 코드의 2차원 배열과 동일한지 식별하는 것입니다. 동일하면 승리 아이콘이 표시됩니다. 동일하지 않으면 1로 진행합니다.
이 데이터를 여러 방법으로 사용해야 하므로 올바른 2차원 배열 win을 정의하고 멤버 위치에 설정합니다.

//定义一个二维数组,存储正确的数据
    int[][] win = {
    
    
            {
    
    1,2,3,4},
            {
    
    5,6,7,8},
            {
    
    9,10,11,12},
            {
    
    13,14,15,0}
    };

2. initImage() 메서드에 사진을 불러오는 기능과 게임 인터페이스를 표시하는 기능이 모두 있기 때문에 사진을 불러오기 전에 먼저 2차원 배열의 숫자가 win 배열의 숫자와 같은지 판단합니다. 게임을 계속하려면 게임 인터페이스에 계속 표시해야 합니다. 먼저 승리 여부를 확인해야 합니다.

if(victory()){
    
    
            //显示胜利的图标
            JLabel winJLabel = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\win.png"));
            winJLabel.setBounds(203, 283, 197, 73);
            this.getContentPane().add(winJLabel);
        }

3. 이기면 올바른 아이콘이 바로 표시되고 그렇지 않으면 잘못된 아이콘이 표시됩니다.승리가 없으면 initImage() 메서드의 코드가 계속 실행되어 게임 퍼즐이 표시됩니다
. . 우리가 모든 것을 작성했을 때 우리는 우리가 이겼을 때 올바른 아이콘이 표시되었지만 그림은 여전히 ​​움직일 수 있다는 것을 발견했습니다. 제 생각에 성공하면 게임을 종료하거나 다음 라운드를 플레이하지 않는 한 게임이 움직일 수 없습니다.
이것은 우리가 keyReleased() 메서드에서 판단해야 합니다. 이기면 메서드를 직접 종료합니다. 종료 후에는 키를 판단하는 데 사용한 코드를 실행할 수 없음을 의미합니다. 즉, 그림이 다시 움직이지 않습니다. .

//判断游戏是否胜利,如果胜利,此方法需要直接结束,不能再执行下面的移动代码了
        if(victory()){
    
    
            //结束方法
            return;
        }

위의 코드는 keyReleased() 메서드 맨 위에 작성해야 하는데, 이 메서드 아래에는 상하좌우 키를 판단하는 코드가 있기 때문입니다. 메서드를 직접 종료하고, 다음 판정 버튼을 실행하지 않고, 승패가 없을 경우 승소할 때까지 다음 판정을 계속 실행합니다.

통계 단계

게임의 재미를 높이기 위해 게임 옆에 카운터를 추가하여 걸음 수를 표시하고 움직일 때마다 하나씩 추가하여 누가 걸음 수가 가장 적은지 비교할 수 있습니다. 매우 흥미로운 단계 카운팅 로직
구현은 비교적 간단합니다.멤버 위치에 카운터 변수를 정의한 다음 키보드 모니터링의 keyReleased() 메소드에서 이동 키를 판단할 때 카운터 변수를 자체 증가에 추가할 수 있으며, 마지막으로 initImage() 메서드에 카운터 내용을 표시합니다. 다음은 코드 구현입니다.

//定义变量用来统计步数
int step = 0;

멤버 위치에서 계산기 변수 정의

//添加计数器到主界面中
JLabel stepCount = new JLabel("步数:" + step);
stepCount.setBounds(50, 30, 100, 20);
this.getContentPane().add(stepCount);

initImage() 메소드에서 카운터를 표시하는 기능을 구현하고
마지막으로 위, 아래, 왼쪽, 오른쪽 키를 판단하는 코드 하단에 step++를 추가하여 각 단계를 구현합니다. 인터페이스는 아래 그림과 같이 이동 단계 수를 표시합니다.
여기에 이미지 설명 삽입

메뉴 바

다시 게임

게임을 다시 시작하려면 메뉴바에서 Replay를 클릭해야 하는데 다시 사진 순서가 어긋나고 다시 시작하려면 메뉴바의 데이터를 클릭해야 하므로
데이터에 모니터링 이벤트를 추가해야 합니다. ActionListener를 사용하면 마우스 클릭과 키보드 스페이스만 있기 때문에 initJMenuBar() 메소드에서 이들 항목에 이벤트 리스너를 바인딩해야 합니다.ActionListener를 호출하기 전에 ActionListene을 호출해야 합니다
. 상호 작용

public class GameFrame extends JFrame implements KeyListener, ActionListener

그런 다음 항목에 이벤트를 추가하십시오.

//给条目绑定事件
replayItem.addActionListener(this);
reLoginItem.addActionListener(this);
closeItem.addActionListener(this);
accountItem.addActionListener(this);

참고로 ActionListener 인터페이스 메서드를 다시 작성하기 전에 ActionListener 인터페이스 rewrite 메서드에서 항목 객체를 호출해야 하므로 이전에 정의한 항목 객체 즉 JMenuItem을 멤버 위치로 이동해야 합니다. ActionListener interface rewrite
method
here 리게임 진입객체의 replayItem 클릭시 실행되는 코드 판단

    @Override
    public void actionPerformed(ActionEvent e) {
    
    
        //获取当前被点击的条目对象
        Object obj = e.getSource();
        //判断
        if(obj == replayItem){
    
    
            System.out.println("重新游戏");

            //计数器清零
            step = 0;
            //再次打乱二维数组中的数据
            initData();
            //重新加载图片
            initImage();

여기서 카운터 리셋은 초기화 데이터와 초기화 그림 위에 위치해야 한다는 점에 유의해야 합니다. 카운터 단계가 초기화 그림에서 사용되기 때문입니다. 위에서 지우지 않으면 카운터는 다음의 카운터 데이터를 계속 사용합니다. 이전 게임

재등록

재로그인은 메뉴바의 Re-login 을 클릭하면 되고, 재로그인 이벤트 모니터링을 추가하였으므로 ActionListener 인터페이스로 재작성된 메소드에서 판단하면 됩니다.다음은 코드 구현입니다.

else if(obj == reLoginItem){
    
    
            System.out.println("重新登录");
            //关闭当前的游戏界面
            this.setVisible(false);
            //打开登录界面
            new LoginJFrame();

게임을 종료

게임종료는 매우 간단합니다 재작성 방법에서 게임종료를 클릭할지 여부를 판단하고 클릭하면 가상머신을 종료하면 됩니다.

else if(obj == closeItem){
    
    
            System.out.println("关闭游戏");
            System.exit(0);

회사 소개

회사 소개, 우리는 QR 코드를 표시할 수 있습니다. top, 표시 여부 등은 본질적으로 JFrame과 유사합니다. 다음은 코드 구현입니다.

else if(obj == accountItem){
    
    
            System.out.println("公众号");

            //创建一个弹框对象
            JDialog jDialog = new JDialog();
            //创建一个管理图片的容器对象JLabel
            JLabel jLabel = new JLabel(new ImageIcon("image\\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);

        }

다음은 렌더링입니다
여기에 이미지 설명 삽입

게임 최적화

게임의 전반적인 내용 작성을 마쳤습니다. 게임의 재미를 높이기 위해 사진을 몇 장 더 추가하고 사진을 분류할 수 있습니다. 플레이어는 미인, 동물, 스포츠 사진과 같은 사진을 선택할 수 있습니다. 제가 준 자료에 좋은 자료가 여러개 있는데, 이 효과는 이전 초기화 그림을 깨달은 후 비교적 간단합니다. 다시 게임 메뉴에 뷰티, 동물, 스포츠 등과 같은 몇 가지 옵션을 추가할 수 있습니다. 해당 옵션을 클릭하여 해당 사진으로 게임을 무작위로 선택하십시오. 단계 수는 0으로 재설정되고 사진은 무작위입니다.

일반적인 구현 프로세스:
1. re-game이라는 JMenu를 재정의한 다음 그림 유형의 JMenuItem을 JMenu에 추가(re-game)하고 마지막으로 JMenu를 JMenu(기능)에 추가하여 다단계 메뉴를 구현합니다. 2. 추가
제공 액션 모니터링 이벤트를 사진 종류에 따라 클릭한 버튼을 판단하고 해당 버튼에 해당 작업을 구현합니다. 이때 우리는 미리 정의한 경로 변수를 사용하며, 판단에서 경로의 값, 즉 이미지 주소를 수정하면 이미지 종류를 선택할 수 있다. 데이터를
초기화하고 이미지를 초기화하면 단계를 0으로 정의하여 단계 수가 0으로 지워집니다.

요약하다

여기에 로그인 인터페이스와 등록 인터페이스를 작성하지 않았습니다. 관심이 있으시면 직접 공부할 수 있습니다. 더 많은 코드가 있습니다. 직접 작성해 볼 수 있습니다. 재미있는 게임 플레이를 추가하여 게임을 더 다양하게 만들 수도 있습니다.

코드

package ui;

import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.xml.crypto.Data;
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 GameFrame extends JFrame implements KeyListener, ActionListener {
    
    
    //JFrame 界面,窗体
    //子类也可以表示界面和窗体
    //那么以后GameFrame就表示游戏的主界面
    //以后跟游戏相关的所有逻辑都写在这个类中

    Random r = new Random();

    //创建选项下面的条目对象
    JMenuItem girlItem = new JMenuItem("美女");
    JMenuItem animalItem = new JMenuItem("动物");
    JMenuItem sportItem = new JMenuItem("运动");
    JMenuItem reLoginItem = new JMenuItem("重新登录");
    JMenuItem closeItem = new JMenuItem("关闭游戏");

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


    //定义变量用来统计步数
    int step = 0;

    //定义一个二维数组,存储正确的数据
    int[][] win = {
    
    
            {
    
    1,2,3,4},
            {
    
    5,6,7,8},
            {
    
    9,10,11,12},
            {
    
    13,14,15,0}
    };


    String path = "image\\animal\\animal3\\";

    //记录空白方块在数组中的位置
    int x = 0;
    int y = 0;

    //创建一个二维数组供初始化数据方法添加数据,和供初始化图片方法使用
    int[][] newArr = new int[4][4];


    public GameFrame(){
    
    

        //初始化界面
        initJFrame();

        //初始化菜单
        initJMenuBar();

        //初始化数据(打乱图片顺序的数据)
        initData();

        //初始化图片
        initImage();

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

    //初始化界面
    private void initJFrame(){
    
    
        //设置界面的宽高
        this.setSize(603, 680);

        //设置界面的标题
        this.setTitle("拼图游戏单机版 V1.0");

        //设置界面置顶
        //盖住其他所有软件
        this.setAlwaysOnTop(true);

        //设置界面居中
        this.setLocationRelativeTo(null);

        //设置游戏的关闭模式
        this.setDefaultCloseOperation(3);

        //取消默认的居中放置,只有取消了才会按照XY轴的形式添加组件
        this.setLayout(null);

        //给整个界面添加键盘监听事件
        this.addKeyListener(this);
    }


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

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


        //将图片类型添加到重新游戏菜单中
        replayJMenu.add(girlItem);
        replayJMenu.add(animalItem);
        replayJMenu.add(sportItem);

        //将每一个选项下的条目添加到选项当中
        functionJMenu.add(replayJMenu);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);

        aboutJMenu.add(accountItem);

        //给条目绑定事件
        reLoginItem.addActionListener(this);
        closeItem.addActionListener(this);
        accountItem.addActionListener(this);
        girlItem.addActionListener(this);
        animalItem.addActionListener(this);
        sportItem.addActionListener(this);

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


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


    //初始化图片
    //添加图片的时候,需要按照二维数组中管理的数据添加图片
    private void initImage(){
    
    

        //清空原本已经出现的所有图片
        //清空以后才会出现移动后的图片,不然被覆盖在下面了
        this.getContentPane().removeAll();


        if(victory()){
    
    
            //显示胜利的图标
            JLabel winJLabel = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\win.png"));
            winJLabel.setBounds(203, 283, 197, 73);
            this.getContentPane().add(winJLabel);
        }

        //添加计数器到主界面中
        JLabel stepCount = new JLabel("步数:" + step);
        stepCount.setBounds(50, 30, 100, 20);
        this.getContentPane().add(stepCount);


        //添加背景图片
        //先加载的图片在上方,后加载的图片在下方

        //外循环 --- 四行
        for (int i = 0; i < 4; i++) {
    
    
            //内循环 --- 一行的四张图片
            for (int j = 0; j < 4; j++) {
    
    
                //获取加载当前图片的序号
                int num = newArr[i][j];

                //创建一个图片ImageIcon对象
                //创建一个JLabel的对象(管理容器)放入ImageIcon对象
                JLabel jLabel = new JLabel(new ImageIcon(path + num + ".jpg"));
                //指定图片位置
                jLabel.setBounds(105 * j + 83, 105 * i + 134, 105, 105);
                //给图片添加边框
                //0:让图片凸起来
                //1:让图片凹下去
                jLabel.setBorder(new BevelBorder(1));
                //把管理容器添加到界面中
                this.getContentPane().add(jLabel);

            }
        }

        //添加背景图片
        //创建ImageIcon对象
        ImageIcon bg = new ImageIcon("image\\background.png");
        //创建JLabel容器对象
        JLabel backgound = new JLabel(bg);
        backgound.setBounds(40,40,508,560);
        //把背景图片添加到界面当中
        this.getContentPane().add(backgound);


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



    //初始化数据
    private void initData(){
    
    
        //打乱一个数组里数字的顺序,并构成二维数组

        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 index = 0;
        for (int i = 0; i < 4; i++) {
    
    
            for (int j = 0; j < 4; j++) {
    
    
                if(Arr[index] == 0){
    
    
                    x = i;
                    y = j;
                }

                newArr[i][j] = Arr[index];

                index++;
            }
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {
    
    

    }

    @Override
    public void keyPressed(KeyEvent e) {
    
    
        int code = e.getKeyCode();
        if(code == 65){
    
    
            //把界面中的所有图片全部清除
            this.getContentPane().removeAll();
            //加载第一张完整的图片
            JLabel all = new JLabel(new ImageIcon(path + "all.jpg"));
            all.setBounds(83, 134, 420, 420);
            this.getContentPane().add(all);

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

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

    @Override
    public void keyReleased(KeyEvent e) {
    
    
        //判断游戏是否胜利,如果胜利,此方法需要直接结束,不能再执行下面的移动代码了
        if(victory()){
    
    
            //结束方法
            return;
        }


        //对上,下,左,右进行判断
        //左:37, 右:39, 上:38, 下:40
        int code = e.getKeyCode();
        if(code == 37){
    
    
            System.out.println("向左移动");
            if(y == 3){
    
    
                return;
            }

            newArr[x][y] = newArr[x][y + 1];
            newArr[x][y + 1] = 0;
            y++;
            //每移动一次,计数器自增一次
            step++;
            //调用方法按最新的方法加载图片
            initImage();


        }else if(code == 38){
    
    
            System.out.println("向上移动");
            if(x == 3){
    
    
                //表示方块已经已经在最下方了,他的下面没有图片再能移动了
                return;
            }

            //逻辑:
            //把空白方块下方的数字往上移动
            //x , y 表示空白方块
            //x+1 , y表示空白下方的方块

            //把空白方块下方的数字赋值给空白方块
            newArr[x][y] = newArr[x + 1][y];
            newArr[x + 1][y] = 0;
            x++;
            //每移动一次,计数器自增一次
            step++;
            //调用方法按最新的方法加载图片
            initImage();

        }else if(code == 39){
    
    
            System.out.println("向右移动");

            if(y == 0){
    
    
                return;
            }

            newArr[x][y] = newArr[x][y - 1];
            newArr[x][y - 1] = 0;
            y--;
            //每移动一次,计数器自增一次
            step++;
            //调用方法按最新的方法加载图片
            initImage();

        }else if(code == 40){
    
    
            System.out.println("向下移动");
            if(x == 0){
    
    
                return;
            }

            //逻辑
            //把空白方块往下移
            newArr[x][y] = newArr[x - 1][y];
            newArr[x - 1][y] = 0;
            x--;
            //每移动一次,计数器自增一次
            step++;
            //调用方法按最新的方法加载图片
            initImage();
        }else if(code == 65){
    
    
            initImage();
        }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;
            initImage();
        }
    }


    //判断data数组中的数据是否跟win数组中相同
    //如果全部相同,返回true,否则返回false
    public boolean victory(){
    
    
        for (int i = 0; i < newArr.length; i++) {
    
    
            for (int j = 0; j < newArr[i].length; j++) {
    
    
                if(newArr[i][j] != win[i][j]){
    
    
                    //只要有一个数据不一样则返回false
                    return false;
                }
            }
        }
        //循环结束表示数组遍历比较完毕,完全一样则返回true
        return true;
    };

    @Override
    public void actionPerformed(ActionEvent e) {
    
    
        //获取当前被点击的条目对象
        Object obj = e.getSource();
        //判断
        if(obj == reLoginItem){
    
    
            System.out.println("重新登录");
            //关闭当前的游戏界面
            this.setVisible(false);
            //打开登录界面
            new LoginJFrame();
        }else if(obj == closeItem){
    
    
            System.out.println("关闭游戏");
            System.exit(0);
        }else if(obj == accountItem){
    
    
            System.out.println("公众号");

            //创建一个弹框对象
            JDialog jDialog = new JDialog();
            //创建一个管理图片的容器对象JLabel
            JLabel jLabel = new JLabel(new ImageIcon("image\\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(obj == girlItem){
    
    
            System.out.println("美女图片");
            //随机获取图片
            int num = r.nextInt(1, 14);
            path =  "image\\girl\\girl"+ num + "\\";

            //初始化步数
            step = 0;
            //初始化数据
            initData();
            //初始化图片
            initImage();
            //刷新
            this.repaint();

        }else if(obj == animalItem){
    
    
            System.out.println("动物图片");
            int num = r.nextInt(1, 9);
            path =  "image\\animal\\animal"+ num + "\\";
            //初始化步数
            step = 0;
            //初始化数据
            initData();
            //初始化图片
            initImage();
            //刷新
            this.repaint();

        }else if(obj == sportItem){
    
    
            System.out.println("运动图片");
            int num = r.nextInt(1, 11);
            path =  "image\\sport\\sport"+ num + "\\";
            //初始化步数
            step = 0;
            //初始化数据
            initData();
            //初始化图片
            initImage();
            //刷新
            this.repaint();
        }
    }
}

다음은 메인 프로그램

import ui.GameFrame;
import ui.LoginJFrame;
import ui.RegisterJFrame;

public class App {
    
    
    public static void main(String[] args) {
    
    

        //表示程序的启动入口

        //如果我们想要开启一个界面,就创建谁的对象就可以了
        new GameFrame();

        //new LoginJFrame();

        //new RegisterJFrame();

        //
    }
}

추천

출처blog.csdn.net/m0_64041302/article/details/128689399