Java学习笔记——拼图小游戏

1、主界面分析

(1)任务一

由于我的分辨率和老师的不一样,因此我将以上像素都×2,生成的大小差不多。

解题思路:首先可以分别写三个类继承JFeame,分别对应游戏主界面、登录界面和注册界面,之后使用构造函数进行初始化,使用setsize和setvisible将显示框的宽高和是否显示进行设置。之后在src下创建App类,用于启动项目,需要那个对象就创建那个

代码如下:

App:

复制代码
import com.xzhao.test7.ui.GameGFrame;
import com.xzhao.test7.ui.LoginFrame;
import com.xzhao.test7.ui.RegisterFrame;

public class App {
    public static void main(String[] args) {
        //程序的启动窗口
        //如果要开启那个页面,就创建谁的对象即可
        new GameGFrame();//创建游戏主界面
        new LoginFrame();//创建登录界面
        new RegisterFrame();//创建注册界面
    }
}

RegisterFrame:

复制代码
package com.xzhao.test7.ui;

import javax.swing.*;

public class RegisterFrame extends JFrame {
    //注册界面
    public RegisterFrame() {
        this.setSize(976,1000);
        this.setVisible(true);
    }
}

LoginFrame:

复制代码
package com.xzhao.test7.ui;

import javax.swing.*;

public class LoginFrame extends JFrame {
    //登录界面
    public LoginFrame(){
        this.setSize(976,860);
        this.setVisible(true);
    }

}

GameFrame:

复制代码
package com.xzhao.test7.ui;

import javax.swing.*;

public class GameGFrame extends JFrame {
    //游戏的主界面,构造函数相当于初始化,设置了页面的宽高和显示页面
    public GameGFrame() {
        this.setSize(1206, 1360);//设置宽高
        this.setVisible(true);//显示界面,不然默认界面是关闭的
    }
}

结果展示:

(2)完善主界面的设置

以主界面代码为例:

复制代码
this.setTitle("拼图小游戏1.0");//设置界面的标题
this.setAlwaysOnTop(true);//将游戏界面永远置于顶层,可以盖住任意软件
this.setLocationRelativeTo(null);//设置界面居中
this.setDefaultCloseOperation(3);//当关闭这个界面程序就停止,关闭模式选择3

在上述情况下加入这几行代码,放在可视化界面代码前面即可,其中关于关闭模式主要有以下:

等于2表示只有关闭最后一个界面程序才停止,但要在所有界面中设置关闭模式为2;

等于3表示只要关闭一个界面程序(整个虚拟机)就停止;

由于在实际操作中,不会同时出现3个界面,因此设置为3是合理的。

结果展示:

2、菜单制作

直接调用方法即可

代码如下:

复制代码
package com.xzhao.test7.ui;

import javax.swing.*;

public class GameGFrame extends JFrame {
    //游戏的主界面,构造函数相当于初始化,设置了页面的宽高和显示页面
    public GameGFrame() {
        init();//界面设置
        menu();//菜单设置
        this.setVisible(true);//显示界面,不然默认界面是关闭的
    }

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

        JMenu functionmenu=new JMenu("功能");//创建选项
        JMenu aboutmenu=new JMenu("关于我们");

        JMenuItem refreshitem=new JMenuItem("重新游戏");//创建具体条目
        JMenuItem refreshlogin=new JMenuItem("重新登录");
        JMenuItem closegame=new JMenuItem("关闭游戏");

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

        functionmenu.add(refreshitem);//将具体的条目放到相应的选项中
        functionmenu.add(refreshlogin);
        functionmenu.add(closegame);

        aboutmenu.add(accountitem);

        jmenubar.add(functionmenu);//将选项加到菜单对象中
        jmenubar.add(aboutmenu);

        this.setJMenuBar(jmenubar);//将菜单对象添加到界面中
    }

    private void init() {
        this.setSize(1206, 1360);//设置宽高
        this.setTitle("拼图小游戏1.0");//设置界面的标题
        this.setAlwaysOnTop(true);//将游戏界面永远置于顶层,可以盖住任意软件
        this.setLocationRelativeTo(null);//设置界面居中
        this.setDefaultCloseOperation(3);//当关闭这个界面程序就停止,关闭模式选择3
    }
}

由于构造函数中的内容过多,因此ctrl+alt+m 将选中内容提取为一个方法,提取之后改名字+enter即可,便于程序的可读性。

结果如下:

3、添加图片

(1)不是将一张图片直接放进去,而是通过将图片分割成15个Imagelcon以及一个空白格,生成

解题思路:先创建Imagelcon对象,放到JLabel(可设置图片的宽高和位置)中,之后再将这个放到界面中

图片素材放在文章中了,有需要的可以使用!

代码如下:

复制代码
private void initimage() {//初始化图片
    ImageIcon imageicon=new ImageIcon("C:\\Users\\xzhao\\IdeaProjects\\test\\image\\animal\\animal3\\3.jpg");//创建图片对象
    JLabel label=new JLabel(imageicon);//创建标签对象,将图片添加到标签中
    this.add(label);//将标签添加到界面中,默认居中
}

结果如下:

(2)指定图片到相应的位置,首先设置原点,以左上角为原点

之后插入的图片坐标以左上角为主

要将图片加到隐藏的容器中,即getContentPane中,此外为了取消图片默认居中,首先要在界面初始化时将其取消,在init()方法中添加一句:

复制代码
this.setLayout(null);//将界面中的图片默认居中取消,否则图片一直默认居中放置

之后将initinage方法改成以下内容:

复制代码
private void initimage() {//初始化图片
    ImageIcon imageicon=new ImageIcon("C:\\Users\\xzhao\\IdeaProjects\\test\\image\\animal\\animal3\\1.jpg");//创建图片对象
    JLabel label=new JLabel(imageicon);//创建标签对象,将图片添加到标签中
    label.setBounds(0, 0, imageicon.getIconWidth(), imageicon.getIconHeight());
    //this.add(label);//将标签添加到界面中,默认居中
    this.getContentPane().add(label);//将标签添加到界面中
}
结果如下:

(3)添加16张图片,使用循环嵌套的方式

复制代码
private void initimage() {//初始化图片
    //内循环:一行要添加4行图片
    //外循环:一共添加4行
    int number=1;//更改图片的计数器
    for(int j=0;j<4;j++){
        for(int i=0;i<4;i++){
            JLabel label=new JLabel(new ImageIcon("C:\\Users\\xzhao\\IdeaProjects\\test\\image\\animal\\animal3\\"+number+".jpg"));//创建标签对象,将图片添加到标签中
            label.setBounds(105*i, 105*j, 105, 105);
            this.getContentPane().add(label);//将标签添加到界面中
            number++;//当number=16时,由于找不到图片,因此自动添加了一个空白
        }
    }
}

首先要对图片进行循环嵌套展示,之后使用拼接的方法对使用那张图片进行修改

结果如下:

(4)打乱图片

将原来的顺序进行随机打乱,然后将4个数放一维数组,一共放4个,使用二维数组去放置。

先做一个练习:

复制代码
int[] tempArr={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
for(int i=tempArr.length;i>0;i--){
    Random rand = new Random();
    int randomIndex = rand.nextInt(i)+1;
    int temp = tempArr[i - 1];
    tempArr[i - 1] = tempArr[randomIndex];
    tempArr[randomIndex] = temp;
}
int[][] data=new int[4][4];//创建二维数组
for(int i=0;i<4;i++){
    for(int j=0;j<4;j++){
        data[i][j]=tempArr[i*4+j];
    }
}

实现以上功能,之后将以上内容添加到游戏界面中,将以上内容添加到初始化图片之前,这样在初始化图片时就可以直接输出

首先将二维数组在最开始建立,因为除了初始化数据要用,初始化图片也要用

之后修改代码:

复制代码
private void initdata() {//初始化图片数据
    int[] tempArr={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
    for(int i=tempArr.length;i>0;i--){
        Random rand = new Random();
        int randomIndex = rand.nextInt(i)+1;
        int temp = tempArr[i - 1];
        tempArr[i - 1] = tempArr[randomIndex];
        tempArr[randomIndex] = temp;
    }
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            data[i][j]=tempArr[i*4+j];
        }
    }
}

并将初始化图片中的number删去,可以直接用data[i][j]代替。

结果如下:

4、事件

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

a、使用匿名内部类

复制代码
JFrame frame = new TestDemo();
frame.setTitle("测试窗口");
frame.setSize(500,500);
frame.setLocationRelativeTo(null);//居中显示
frame.setDefaultCloseOperation(3);//设置关闭模式
frame.setLayout(null);

JButton btn = new JButton("测试按钮");//建立一个按钮
btn.setBounds(0,0,105,105);//设置按钮的位置

btn.addActionListener(new ActionListener() {//使用匿名内部类对按钮进行实现,空格或者鼠标点击时触发动作监听事件
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("按钮被点击了");
    }
});

frame.getContentPane().add(btn);//把按钮添加到界面中

frame.setVisible(true);//设置可见

这种方式使用鼠标点击按钮或者空格时,控制端显示 按钮被点击了

b、使用实现接口方式进行重写,对两个按钮进行

按钮实现类:

复制代码
package com.xzhao.test2;

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

public class Myjm extends JFrame implements ActionListener {
    JButton btn1 = new JButton("点我");//建立一个按钮
    JButton btn2 = new JButton("再点我");//建立一个按钮

    public Myjm() {
        JFrame frame = new TestDemo();
        frame.setTitle("测试框");
        frame.setSize(500, 500);
        frame.setLocationRelativeTo(null);//居中显示
        frame.setDefaultCloseOperation(3);//设置关闭模式
        frame.setLayout(null);

        btn1.setBounds(0, 0, 105, 105);//设置按钮的位置
        btn1.addActionListener(this);//将按钮添加到事件监听中

        btn2.setBounds(105, 0, 105, 105);//设置按钮的位置
        btn2.addActionListener(this);//将按钮添加到事件监听中

        frame.add(btn1);
        frame.add(btn2);

        frame.setVisible(true);//设置可见
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == btn1) {
            btn1.setSize( 200, 200);
        }
        if (e.getSource() == btn2) {
            Random r=new Random();
            btn2.setLocation(r.nextInt(420), r.nextInt(420));
        }
    }
}
主程序:
复制代码
Myjm myjm = new Myjm();//创建一个测试类的对象

结果如下:

弊端:只能在按下按钮之后实现功能,对于其他操作不能实现!!!

鼠标监听:

新创建一个新的实现类:

复制代码
package com.xzhao.test2;

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

public class Myjm2 extends JFrame implements MouseListener {

    public Myjm2() {
        JFrame frame = new TestDemo();
        frame.setTitle("测试框");
        frame.setSize(500, 500);
        frame.setLocationRelativeTo(null);//居中显示
        frame.setDefaultCloseOperation(3);//设置关闭模式
        frame.setLayout(null);

        JButton btn1 = new JButton("点我");//建立一个按钮
        btn1.setBounds(0, 0, 105, 105);//设置按钮的位置
        btn1.addMouseListener(this);//将按钮添加到事件监听中

        frame.add(btn1);

        frame.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("鼠标退出了");
    }
}

主函数:
复制代码
Myjm2 myjm2 = new Myjm2();//创建一个测试类的对象

结果展示:

键盘监听:KeyListener

主要实现类:

复制代码
package com.xzhao.test2;

import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class Myjm3 extends JFrame implements KeyListener {
    public Myjm3() {
        JFrame frame = new TestDemo();
        frame.setTitle("测试框");
        frame.setSize(500, 500);
        frame.setLocationRelativeTo(null);//居中显示
        frame.setDefaultCloseOperation(3);//设置关闭模式
        frame.setLayout(null);

        JButton btn = new JButton("点我");//建立一个按钮
        btn.setBounds(0, 0, 105, 105);//设置按钮的位置
        btn.addKeyListener(this);//将按钮添加到事件监听中

        frame.add(btn);

        frame.setVisible(true);//设置可见
    }

    @Override
    public void keyTyped(KeyEvent e) {//这个一般不用,不用写

    }

    @Override//当按下不松时,会一直调用这个程序
    public void keyPressed(KeyEvent e) {
        System.out.println("键盘按下不松");
    }

    @Override
    public void keyReleased(KeyEvent e) {
        System.out.println("键盘松开了");
        if(e.getKeyCode()==65){//getkeycode可以获取键盘上的编号,本身与ascll码没关系,只是一个编号
            System.out.println("A键被按下");
        }
        if(e.getKeyCode()==68){
            System.out.println("D键被按下");
        }
    }
}

主程序:
复制代码
Myjm3 myjm3 = new Myjm3();//创建一个测试类的对象

结果显示:

5、美化界面

首先将加载的15个图片放到页面中下(此处修改setBounds),之后添加背景图(将其添加到15张图片引入之后),最后给图片添加边框(添加setborder)。

并将绝对路径更改为相对路径(非盘符开始)

复制代码
private void initimage() {//初始化图片
    //内循环:一行要添加4行图片
    //外循环:一共添加4行
    for(int j=0;j<4;j++){
        for(int i=0;i<4;i++){
            JLabel label=new JLabel(new ImageIcon("image\\animal\\animal3\\"+data[i][j]+".jpg"));//创建标签对象,将图片添加到标签中
            label.setBounds(105*i+83, 105*j+104, 105, 105);
            //给图片添加边框,0表示让图片凸起来,1表示让图片凹进去
            label.setBorder(new BevelBorder(0));
            this.getContentPane().add(label);//将标签添加到界面中
           // number++;//当number=16时,由于找不到图片,因此自动添加了一个空白
        }
    }
    JLabel labelb=new JLabel(new ImageIcon("image\\background.png"));
    labelb.setBounds(40, 10, 508, 560);
    this.getContentPane().add(labelb);//先加载的图片在上方,后加载的在下方
}

结果如下:

6、移动图片

首先在类中添加键盘监听的接口

之后定义两个值,用于后续记录空白图片的横纵坐标

之后更改初始化图片数据,记录空白图片坐标

复制代码
private void initdata() {//初始化图片数据
    int[] tempArr={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};
    for(int i=tempArr.length;i>0;i--){
        Random rand = new Random();
        int randomIndex = rand.nextInt(i)+1;
        int temp = tempArr[i - 1];
        tempArr[i - 1] = tempArr[randomIndex];
        tempArr[randomIndex] = temp;
    }
    for(int i=0;i<tempArr.length;i++){
        if(tempArr[i]==0){
            x=i/4;
            y=i%4;
        }
        else{
            data[i/4][i%4]=tempArr[i];
        }
    }
}

在初始化图片的开头和结尾分别添加清空和刷新界面

复制代码
private void initimage() {//初始化图片
    //清除已经出现的图片
    this.getContentPane().removeAll();//清除所有图片,重新生成
    //内循环:一行要添加4行图片
    //外循环:一共添加4行
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            JLabel label=new JLabel(new ImageIcon(path+data[i][j]+".jpg"));//创建标签对象,将图片添加到标签中
            label.setBounds(105*j+83, 105*i+104, 105, 105);
            //给图片添加边框,0表示让图片凸起来,1表示让图片凹进去
            label.setBorder(new BevelBorder(0));
            this.getContentPane().add(label);//将标签添加到界面中
           // number++;//当number=16时,由于找不到图片,因此自动添加了一个空白
        }
    }

之后对重写接口的内容进行添加,在这里数组是先纵坐标再横坐标,因此需要在代码中注意!

为了防止边界超出索引,因此添加并行事件,防止出bug

复制代码
@Override
public void keyTyped(KeyEvent e) {

}

@Override
public void keyPressed(KeyEvent e) {

}
复制代码
@Override
public void keyReleased(KeyEvent e) {
    int keyCode = e.getKeyCode();//获取用户按下的键值
    if(keyCode==KeyEvent.VK_LEFT&&y<3){//如果用户按下的是向左键
        data[x][y]=data[x][y+1];
        data[x][y+1]=0;
        y++;//将0的横坐标加1
        initimage();//重新初始化图片
        System.out.println("向左移动");
    }
    else if(keyCode==KeyEvent.VK_RIGHT&&y>0){
        data[x][y]=data[x][y-1];
        data[x][y-1]=0;
        y--;//将0的横坐标减1
        initimage();//重新初始化图片
        System.out.println("向右移动");
    }
    else if(keyCode==KeyEvent.VK_UP&&x<3){
        data[x][y]=data[x+1][y];
        data[x+1][y]=0;
        x++;//将0的纵坐标加1
        initimage();//重新初始化图片
        System.out.println("向上移动");
    }
    else if(keyCode==KeyEvent.VK_DOWN&&x>0){
        data[x][y]=data[x-1][y];
        data[x-1][y]=0;
        x--;//将0的纵坐标减1
        initimage();//重新初始化图片
        System.out.println("向下移动");
    }

最终结果可以实现上下左右移动图片。

7、查看完整图片

按下A键显示完整图片,使用键盘事件,首先在按下哪里编写程序

复制代码
@Override
public void keyPressed(KeyEvent e) {
    int key=e.getKeyCode();
    if(key==65){
        this.getContentPane().removeAll();//清除所有图片,重新生成
        JLabel labelb1=new JLabel(new ImageIcon(path+"all.jpg"));//加载完整图片
        labelb1.setBounds(38, 38, 508, 560);
        this.getContentPane().add(labelb1);

        JLabel labelb2=new JLabel(new ImageIcon("image\\background.png"));//加载背景图片
        labelb2.setBounds(40, 10, 508, 560);
        this.getContentPane().add(labelb2);

        this.getContentPane().repaint();//刷新界面,将新添加的图片显示出来
    }

}

之后在松开哪里添加一段代码

复制代码
else if(keyCode==65){
    initimage();
}

为了方便后续更换图片,在这里使用字符串将path进行定义

复制代码
String path="image\\animal\\animal7\\";

后续直接用就可以,如下所示,是按下A显示的图片

8、作弊码

一定要主要i对应行,j对应列,但是图片左上角坐标的x则是先按列走,y是行!要注意这里!

直接在松开哪里添加一个键盘按键的作用,直接给data赋新值,

复制代码
else if(keyCode==66){//如果是B,松开就一键胜利
    System.out.println("胜利");
    data=new int[][]{
            {1,2,3,4},
            {5,6,7,8},
            {9,10,11,12},
            {13,14,15,0}
    };//直接将排序好的赋给data就可以
    x=3;
    y=3;
    initimage();
}

9、判断胜利

首先建立一个数组,存放正确的图片顺序

复制代码
int[][] windata=new int[][]{//设置成功的二维数据
        {1,2,3,4},
        {5,6,7,8},
        {9,10,11,12},
        {13,14,15,0}
};

之后定义一个方法,返回布尔值,只有全部正确flag才是true

复制代码
public boolean win() {
    boolean flag=true;
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            if(data[i][j]!=windata[i][j]){
                flag=false;
            }
        }
    }
    return flag;
}

只有返回true才代表胜利

接下来在两个地方进行判断:第一个是在图片初始化中,清空图片之后添加以下代码,保证胜利的图片在最上面!

复制代码
if(win()){//如果胜利则显示胜利的图片
    JLabel label=new JLabel(new ImageIcon("image\\win.png"));
    label.setBounds(200, 260, 197, 73);
    this.getContentPane().add(label);
}

第二个是在重写的方法移动按钮上添加判断,此处return直接结束方法

复制代码
if(win()){//如果胜利则不允许移动
    return;
}

通过以上可以实现功能。

10、记步

定义一个成员变量,如果定义在方法内,容易导致每次调用方法都重新赋值为0

复制代码
int count=0;//记录步数

将以下代码加到初始化图片中,使用JLabel进行文字的显示

复制代码
JLabel step=new JLabel("步数:"+count);
step.setBounds(40, 15, 100, 30);
this.getContentPane().add(step);

之后在每个移动中写一个count++,写在初始化图片前面,不然会比实际步数少1!

结果如下:

11、重新开始

由于只需要一个单击的作用,选择动作监听更好。

首先将条目信息需要挪到成员变量中

复制代码
JMenuItem refreshitem=new JMenuItem("重新游戏");//创建具体条目
JMenuItem refreshlogin=new JMenuItem("重新登录");
JMenuItem closegame=new JMenuItem("关闭游戏");

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

之后在menu中分别给以上内容绑定事件

复制代码
refreshitem.addActionListener(this);//将动作事件分别绑定
refreshlogin.addActionListener(this);
closegame.addActionListener(this);
accountitem.addActionListener(this);

最后在重写的动作事件中添加以下代码

复制代码
if(e.getSource()==refreshitem){
    count=0;
    initdata();
    initimage();
}

12、关闭游戏

复制代码
else if(e.getSource()==closegame){
    System.exit(0);
}

13、关于我们

可以找一个二维码放到这里,将图片在imagelcon中加载,然后在jlabel中放置,最后放在jdialog中

代码如下:

复制代码
else if(e.getSource()==accountitem){
    JDialog dialog=new JDialog(this,"公众号");//创建弹框对象
    dialog.setSize(350,350);
    dialog.setLocationRelativeTo(null);

    JLabel labelb=new JLabel(new ImageIcon("image\\about.png"));
    labelb.setBounds(50,50,258,258);
    dialog.getContentPane().add(labelb);
    dialog.setModal(true);//弹窗不关闭无法进行下面的操作
    dialog.setVisible(true);
}

14、添加更换图片的功能

首先在menu方法中添加,这里jmenu可以添加另一个jmenu对象,因此,如下所示,将更换图片这个添加到功能中即可,此外要记得给更换图片中的三个条目:动物、女孩和运动绑定动作事件,同样将其设置为成员变量:

复制代码
JMenuItem animals=new JMenuItem("动物");
JMenuItem girls=new JMenuItem("女孩");
JMenuItem sports=new JMenuItem("运动");
复制代码
private void menu() {
    //初始化菜单
    JMenuBar jmenubar=new JMenuBar();//创建整个菜单对象

    JMenu functionmenu=new JMenu("功能");//创建选项
    JMenu aboutmenu=new JMenu("关于我们");

    JMenu changepic=new JMenu("更换图片");

    functionmenu.add(refreshitem);//将具体的条目放到相应的选项中
    functionmenu.add(refreshlogin);
    functionmenu.add(closegame);
    functionmenu.add(changepic);//在功能中添加更换图片,jmenu和添加到另一个jmenu中

    aboutmenu.add(accountitem);

    changepic.add(animals);
    changepic.add(girls);
    changepic.add(sports);

    refreshitem.addActionListener(this);//将动作事件分别绑定
    refreshlogin.addActionListener(this);
    closegame.addActionListener(this);
    accountitem.addActionListener(this);
    animals.addActionListener(this);
    girls.addActionListener(this);
    sports.addActionListener(this);

    jmenubar.add(functionmenu);//将选项加到菜单对象中
    jmenubar.add(aboutmenu);

    this.setJMenuBar(jmenubar);//将菜单对象添加到界面中
}

最后在动作事件中,添加具体的实现方法,主要通过更改path路径即可,此处使用了random随机生成编号,然后导入,可实现该功能!!!

复制代码
else if(e.getSource()==animals){
    int animalsnum=random.nextInt(8)+1;
    path="image\\animal\\animal"+animalsnum+"\\";
    initdata();
    initimage();
}else if(e.getSource()==girls){
    int girlnum=random.nextInt(13)+1;
    path="image\\girl\\girl"+girlnum+"\\";
    initdata();
    initimage();
}else if(e.getSource()==sports){
    int sportnum=random.nextInt(10)+1;
    path="image\\sport\\sport"+sportnum+"\\";
    initdata();
    initimage();
}

15、登录界面

界面代码如下所示:

复制代码
public class LoginJFrame extends JFrame {
    //创建一个集合存储正确的用户名和密码
    static ArrayList<User> list = new ArrayList<>();
    static {
        list.add(new User("zhangsan","123"));
        list.add(new User("lisi","1234"));
    }
    
    
    public LoginJFrame() {
        //初始化界面
        initJFrame();

        //在这个界面中添加内容
        initView();

        //让当前界面显示出来
        this.setVisible(true);
    }

    public void initView() {
        //1. 添加用户名文字
        JLabel usernameText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\用户名.png"));
        usernameText.setBounds(116, 135, 47, 17);
        this.getContentPane().add(usernameText);

        //2.添加用户名输入框
        JTextField username = new JTextField();
        username.setBounds(195, 134, 200, 30);
        this.getContentPane().add(username);

        //3.添加密码文字
        JLabel passwordText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\密码.png"));
        passwordText.setBounds(130, 195, 32, 16);
        this.getContentPane().add(passwordText);

        //4.密码输入框
        JTextField password = new JTextField();
        password.setBounds(195, 195, 200, 30);
        this.getContentPane().add(password);

        //验证码提示
        JLabel codeText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\验证码.png"));
        codeText.setBounds(133, 256, 50, 30);
        this.getContentPane().add(codeText);

        //验证码的输入框
        JTextField code = new JTextField();
        code.setBounds(195, 256, 100, 30);
        this.getContentPane().add(code);

        String codeStr = CodeUtil.getCode();
        JLabel rightCode = new JLabel();
        //设置内容
        rightCode.setText(codeStr);
        //位置和宽高
        rightCode.setBounds(300, 256, 50, 30);
        //添加到界面
        this.getContentPane().add(rightCode);

        //5.添加登录按钮
        JButton login = new JButton();
        login.setBounds(123, 310, 128, 47);
        login.setIcon(new ImageIcon("puzzlegame\\image\\login\\登录按钮.png"));
        //去除按钮的默认边框
        login.setBorderPainted(false);
        //去除按钮的默认背景
        login.setContentAreaFilled(false);
        this.getContentPane().add(login);

        //6.添加注册按钮
        JButton register = new JButton();
        register.setBounds(256, 310, 128, 47);
        register.setIcon(new ImageIcon("puzzlegame\\image\\login\\注册按钮.png"));
        //去除按钮的默认边框
        register.setBorderPainted(false);
        //去除按钮的默认背景
        register.setContentAreaFilled(false);
        this.getContentPane().add(register);

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


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

    
    //要展示用户名或密码错误
    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);
    }
}

登录界面,代码如下:

复制代码
package com.xzhao.test7.ui;

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

public class LoginFrame extends JFrame implements MouseListener{
    //登录界面
        //创建一个集合存储正确的用户名和密码
        static ArrayList<User> list = new ArrayList<>();
        static {
            list.add(new User("zhangsan","123"));
            list.add(new User("lisi","1234"));
        }

        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 LoginFrame() {
            //初始化界面
            initJFrame();
            //在这个界面中添加内容
            initView();
            //让当前界面显示出来
            this.setVisible(true);
        }

        public void initView() {
            //1. 添加用户名文字
            JLabel usernameText = new JLabel(new ImageIcon("image\\login\\用户名.png"));
            usernameText.setBounds(116, 135, 47, 17);
            this.getContentPane().add(usernameText);

            //2.添加用户名输入框
            username.setBounds(195, 134, 200, 30);
            this.getContentPane().add(username);

            //3.添加密码文字
            JLabel passwordText = new JLabel(new ImageIcon("image\\login\\密码.png"));
            passwordText.setBounds(130, 195, 32, 16);
            this.getContentPane().add(passwordText);

            //4.密码输入框
            password.setBounds(195, 195, 200, 30);
            this.getContentPane().add(password);


            //验证码提示
            JLabel codeText = new JLabel(new ImageIcon("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);

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

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


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


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


        //要展示用户名或密码错误
        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 mouseClicked(MouseEvent e) {
        if (e.getSource() == login) {
            System.out.println("登录开始");
            String usernameStr = username.getText();//获取用户输入的用户名
            String passwordStr = password.getText();
            String codeStr = code.getText();

            User user = new User(usernameStr, passwordStr);
            System.out.println("输入的用户名为:" + usernameStr);
            System.out.println("输入的密码为:" + passwordStr);

            if (codeStr == null) {
                showJDialog("验证码不能为空!");
            } else if ((usernameStr.length() == 0) || (passwordStr.length() == 0)) {
                showJDialog("用户名或密码不能为空!");
            } else if (!codeStr.equalsIgnoreCase(rightCode.getText())) {
                showJDialog("验证码错误!");
            } else if (checkUser(user)) {
                showJDialog("登录成功!");
                this.setVisible(false);//关闭登录界面
                new GameGFrame();//打开游戏的主界面
            } else {
                System.out.println("用户名或密码错误");
                showJDialog("用户名或密码错误");
            }
        } else if (e.getSource() == register) {
            System.out.println("注册按钮");
        } else if (e.getSource() == rightCode) {
            System.out.println("更换验证码");
            String codeStr = CodeUtil.getCode();
            rightCode.setText(codeStr);
        }
    }

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

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

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }

    public boolean checkUser(User userinput){
        for(int i=0;i<list.size();i++){
            User u=list.get(i);
            if(u.getName().equals(userinput.getName())&&u.getPassword().equals(userinput.getPassword())){
                return true;
            }
        }
        return false;
    }

}


以下是游戏界面全部代码:
复制代码
package com.xzhao.test7.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 GameGFrame extends JFrame implements KeyListener, ActionListener {
    //游戏的主界面,构造函数相当于初始化,设置了页面的宽高和显示页面
    int[][] data=new int[4][4];//创建二维数组,用于管理图片的位置,可以控制上下左右移动

    int x=0;//记录图片0对应的横坐标
    int y=0;//记录图片0对应的纵坐标

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

    int[][] windata=new int[][]{//设置成功的二维数据
            {1,2,3,4},
            {5,6,7,8},
            {9,10,11,12},
            {13,14,15,0}
    };

    int count=0;//记录步数

    JMenuItem refreshitem=new JMenuItem("重新游戏");//创建具体条目
    JMenuItem refreshlogin=new JMenuItem("重新登录");
    JMenuItem closegame=new JMenuItem("关闭游戏");


    JMenuItem animals=new JMenuItem("动物");
    JMenuItem girls=new JMenuItem("女孩");
    JMenuItem sports=new JMenuItem("运动");

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

    public GameGFrame() {
        init();//初始化界面
        menu();//初始化菜单
        initdata();//初始化数据(将图片数据进行打乱)
        initimage();//初始化图片
        this.setVisible(true);//显示界面,不然默认界面是关闭的
    }

    public boolean win() {
        boolean flag=true;
        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                if(data[i][j]!=windata[i][j]){
                    flag=false;
                }
            }
        }
        return flag;
    }

    private void initdata() {//初始化图片数据
        int[] tempArr={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};//将0放到最后可以在每次运行时将空白放到新的位置
        Random rand = new Random();
        for(int i=tempArr.length;i>0;i--){
            int randomIndex = rand.nextInt(i);//注意此处的索引值最大是15!!!
            int temp = tempArr[i - 1];
            tempArr[i - 1] = tempArr[randomIndex];
            tempArr[randomIndex] = temp;
        }

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

    private void initimage() {//初始化图片
        //清除已经出现的图片
        this.getContentPane().removeAll();//清除所有图片,重新生成

        if(win()){//如果胜利则显示胜利的图片
            JLabel label=new JLabel(new ImageIcon("image\\win.png"));
            label.setBounds(200, 260, 197, 73);
            this.getContentPane().add(label);
        }

        //内循环:一行要添加4行图片
        //外循环:一共添加4行
        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                JLabel label=new JLabel(new ImageIcon(path+data[i][j]+".jpg"));//创建标签对象,将图片添加到标签中
                label.setBounds(105*j+83, 105*i+104, 105, 105);
                //给图片添加边框,0表示让图片凸起来,1表示让图片凹进去
                label.setBorder(new BevelBorder(0));
                this.getContentPane().add(label);//将标签添加到界面中
               // number++;//当number=16时,由于找不到图片,因此自动添加了一个空白
            }
        }

        JLabel step=new JLabel("步数:"+count);
        step.setBounds(40, 15, 100, 30);
        this.getContentPane().add(step);

        JLabel labelb=new JLabel(new ImageIcon("image\\background.png"));
        labelb.setBounds(40, 10, 508, 560);
        this.getContentPane().add(labelb);//先加载的图片在上方,后加载的在下方

        this.getContentPane().repaint();//刷新界面,将新添加的图片显示出来
    }

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

        JMenu functionmenu=new JMenu("功能");//创建选项
        JMenu aboutmenu=new JMenu("关于我们");

        JMenu changepic=new JMenu("更换图片");

        functionmenu.add(refreshitem);//将具体的条目放到相应的选项中
        functionmenu.add(refreshlogin);
        functionmenu.add(closegame);
        functionmenu.add(changepic);//在功能中添加更换图片,jmenu和添加到另一个jmenu中

        aboutmenu.add(accountitem);

        changepic.add(animals);
        changepic.add(girls);
        changepic.add(sports);

        refreshitem.addActionListener(this);//将动作事件分别绑定
        refreshlogin.addActionListener(this);
        closegame.addActionListener(this);
        accountitem.addActionListener(this);
        animals.addActionListener(this);
        girls.addActionListener(this);
        sports.addActionListener(this);

        jmenubar.add(functionmenu);//将选项加到菜单对象中
        jmenubar.add(aboutmenu);

        this.setJMenuBar(jmenubar);//将菜单对象添加到界面中
    }

    private void init() {
        this.setSize(603, 680);//设置宽高
        this.setTitle("拼图小游戏1.0");//设置界面的标题
        this.setAlwaysOnTop(true);//将游戏界面永远置于顶层,可以盖住任意软件
        this.setLocationRelativeTo(null);//设置界面居中
        this.setDefaultCloseOperation(3);//当关闭这个界面程序就停止,关闭模式选择3
        this.setLayout(null);//将界面中的图片默认居中取消,否则图片一直默认居中放置
        this.addKeyListener(this);//将键盘事件添加到界面中
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {
        int key=e.getKeyCode();
        if(key==65){//如果是A,按下显示完整图片
            this.getContentPane().removeAll();//清除所有图片,重新生成
            JLabel labelb1=new JLabel(new ImageIcon(path+"all.jpg"));//加载完整图片
            labelb1.setBounds(38, 38, 508, 560);
            this.getContentPane().add(labelb1);

            JLabel labelb2=new JLabel(new ImageIcon("image\\background.png"));//加载背景图片
            labelb2.setBounds(40, 10, 508, 560);
            this.getContentPane().add(labelb2);

            this.getContentPane().repaint();//刷新界面,将新添加的图片显示出来
        }

    }

    @Override
    public void keyReleased(KeyEvent e) {
        if(win()){//如果胜利则不允许移动
            return;
        }

        int keyCode = e.getKeyCode();//获取用户按下的键值
        if(keyCode==KeyEvent.VK_LEFT&&y<3){//如果用户按下的是向左键
            data[x][y]=data[x][y+1];
            data[x][y+1]=0;
            y++;//将0的横坐标加1
            count++;
            initimage();//重新初始化图片
            System.out.println("向左移动");
        }
        else if(keyCode==KeyEvent.VK_RIGHT&&y>0){
            data[x][y]=data[x][y-1];
            data[x][y-1]=0;
            y--;//将0的横坐标减1
            count++;
            initimage();//重新初始化图片
            System.out.println("向右移动");
        }
        else if(keyCode==KeyEvent.VK_UP&&x<3){
            data[x][y]=data[x+1][y];
            data[x+1][y]=0;
            x++;//将0的纵坐标加1
            count++;
            initimage();//重新初始化图片
            System.out.println("向上移动");
        }
        else if(keyCode==KeyEvent.VK_DOWN&&x>0){
            data[x][y]=data[x-1][y];
            data[x-1][y]=0;
            x--;//将0的纵坐标减1
            count++;
            initimage();//重新初始化图片
            System.out.println("向下移动");
        }
        else if(keyCode==65){//如果是A,松开显示原来的图片
            initimage();
        }
        else if(keyCode==66){//如果是B,松开就一键胜利
            System.out.println("胜利");
            data=new int[][]{
                    {1,2,3,4},
                    {5,6,7,8},
                    {9,10,11,12},
                    {13,14,15,0}
            };//直接将排序好的赋给data就可以
            x=3;
            y=3;
            initimage();
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        Random random=new Random();//随机给定任意一个图片的编号
        if(e.getSource()==refreshitem){
            count=0;//实现计数器清0
            initdata();
            initimage();
        }else if(e.getSource()==refreshlogin){
            this.setVisible(false);//关闭游戏界面
            new LoginFrame();//重新打开登录界面
        }else if(e.getSource()==closegame){
            System.exit(0);
        }else if(e.getSource()==accountitem){
            JDialog dialog=new JDialog(this,"公众号");//创建弹框对象
            dialog.setSize(350,350);
            dialog.setLocationRelativeTo(null);

            JLabel labelb=new JLabel(new ImageIcon("image\\about.png"));
            labelb.setBounds(50,50,258,258);
            dialog.getContentPane().add(labelb);
            dialog.setModal(true);//弹窗不关闭无法进行下面的操作
            dialog.setVisible(true);
        }else if(e.getSource()==animals){
            int animalsnum=random.nextInt(8)+1;
            path="image\\animal\\animal"+animalsnum+"\\";
            initdata();
            initimage();
        }else if(e.getSource()==girls){
            int girlnum=random.nextInt(13)+1;
            path="image\\girl\\girl"+girlnum+"\\";
            initdata();
            initimage();
        }else if(e.getSource()==sports){
            int sportnum=random.nextInt(10)+1;
            path="image\\sport\\sport"+sportnum+"\\";
            initdata();
            initimage();
        }
    }
}


注册界面因为需要用到后边的知识,所以这里没有写太多
复制代码
package com.xzhao.test7.ui;

import javax.swing.*;

public class RegisterFrame extends JFrame {
    //注册界面
    public RegisterFrame() {
        this.setSize(488,500);
        this.setTitle("注册界面");//设置界面的标题
        this.setAlwaysOnTop(true);//将游戏界面永远置于顶层,可以盖住任意软件
        this.setLocationRelativeTo(null);//设置界面居中
        this.setDefaultCloseOperation(3);//当关闭这个界面程序就停止,关闭模式选择3
        this.setVisible(true);
    }
}

主界面:
复制代码
import com.xzhao.test7.ui.LoginFrame;


public class App {
    public static void main(String[] args) {
        //程序的启动窗口
        //如果要开启那个页面,就创建谁的对象即可
        //new GameGFrame();//创建游戏主界面
        new LoginFrame();//创建登录界面
//        new RegisterFrame();//创建注册界面
    }
}

结果展示:

效果展示

运行这个代码之后会先跳出登录界面,因为注册还没有写,因此这个目前只能登录已经写好的两个对象,一个是"zhangsan",123;另一个是"lisi",1234。通过以上代码也是对游戏的一些开发有了一定的了解,第一次感受到项目。

相关推荐
qq_2130594336 分钟前
CKA认证笔记(1.28版本)
笔记·docker·kubernetes
点云SLAM37 分钟前
Embedding 英文单词学习
人工智能·学习·嵌入式·embedding·安装·英文单词学习·雅思备考
昊昊该干饭了40 分钟前
Spring Boot 从接口设计到业务编排
java·spring boot·后端
freexyn1 小时前
Matlab自学笔记六十九:多项式求值、求根、积分和求导
开发语言·笔记·matlab
yihuiComeOn1 小时前
【大数据高并发核心场景实战】 - 设计秒杀架构必知必会的那些事
java·后端·微服务·架构
iナナ1 小时前
Java自定义协议的发布订阅式消息队列(一)
java·开发语言·spring·消息队列·生成消费者模型
一直都在5721 小时前
手写tomcat(2):Servlet原理和自定义tomcat
java·servlet·tomcat
Seven971 小时前
数据结构——树
java
摇滚侠1 小时前
2025最新 SpringCloud 教程,Gateway-路由-工作原理,笔记54
笔记·spring cloud·gateway