Java项目实战——数字华容道/石头迷阵游戏(包含源代码)

目录

1、准备环节

2、创建石头迷阵的界面

3、打乱顺序

4、控制上下左右移动

5、判断是否通关

6、统计移动步骤、重启游戏


本篇文章主要是基于Java去实现的一个数字华容道游戏,主要运用了GUI界面编程+二维数组+程序流程控制+面对对象编程

先看看界面实现效果吧~

1、准备环节

  • 创建一个模块用于开发石头迷阵游戏,模块名称取名为:stone-maze

  • 导入项目需要的资源包到src目录下:主要是一些图片文件,在image文件夹下。

  • 创建项目包:com.itheima

2、创建石头迷阵的界面

  • 定义主界面类,MainFrame继承JFrame. 自定义窗口。

  • 初始化窗口大小。

  • 初始化界面图片。

  • 初始化界面菜单:系统退出,重启游戏。

public class MainFrame extends JFrame {
    public static final String IMAGE_PATH = "stonemaze/image/";
    private int[][] data = new int[][]{
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };

    public MainFrame2() {
        // 1、初始化窗口大小等信息
        initFrame();
        // 3、添加操作按钮
        initMenu();
        // 2、初始化界面信息,展示图片,需要定义一个二维数组展示图片的位置,然后按照二位数组的图片名称依次在窗口上展示图片
        initImage();

        // 必须最后设置界面可见
        this.setVisible(true);
    }

    private void initMenu() {
        // 在主界面上方展示一个系统菜单,菜单下展示重启游戏和退出程序两个功能
        JMenuBar menuBar = new JMenuBar();
        JMenu menu = new JMenu("系统菜单");
        JMenuItem restartItem = new JMenuItem("重启游戏");
        restartItem.addActionListener(e -> {
            System.out.println("重启游戏!");
        });
        JMenuItem exitItem = new JMenuItem("退出程序");
        exitItem.addActionListener(e -> {
            // 退出程序,销毁窗口即可
            this.dispose();
        });
        menu.add(restartItem);
        menu.add(exitItem);
        menuBar.add(menu);
        this.setJMenuBar(menuBar);
    }

    // 在窗口上展示图片
    private void initImage() {
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data[i].length; j++) {
                // 获取图片名称
                String imageName = data[i][j] + ".png";
                // 创建图片对象
                ImageIcon imageIcon = new ImageIcon(IMAGE_PATH + imageName);
                // 创建图片标签
                JLabel jLabel = new JLabel(imageIcon);
                // 设置图片标签的位置
                jLabel.setBounds(20 + j * 100, 60 + i * 100, 100, 100);
                this.add(jLabel);
           }
       }

        // 设置窗口的背景图片
        JLabel background = new JLabel(new ImageIcon(IMAGE_PATH + "background.png"));
        background.setBounds(0, 0, 450, 484);
        this.add(background);
    }

    private void initFrame() {
        // 设置窗口的标题
        this.setTitle("石子迷阵 v1.0");
        // 设置窗口的大小
        this.setSize(465, 580);
        // 设置窗口的位置
        this.setLocationRelativeTo(null);
        // 设置窗口的关闭方式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 设置布局方式为绝对位置定位
        this.setLayout(null);

    }

    public static void main(String[] args) {
        new MainFrame();
    }
}

3、打乱顺序

  • 打乱界面的图片顺序,让游戏具备可玩性。

  • 打乱二维数组中的元素顺序:initRandomArray();

package com.itheima;

import javax.swing.*;

public class MainFrame extends JFrame {
    public static final String IMAGE_PATH = "stonemaze/image/";
    private int[][] data = new int[][]{
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };

    public MainFrame2() {
        // 1、初始化窗口大小等信息
        initFrame();
        // 3、添加操作按钮
        initMenu();

        // 4、打乱二维数组中的元素顺序。
        initRandomArray();

        // 2、初始化界面信息,展示图片,需要定义一个二维数组展示图片的位置,然后按照二位数组的图片名称依次在窗口上展示图片
        initImage();

        // 必须最后设置界面可见
        this.setVisible(true);
    }

   private void initRandomArray() {
        // 打乱二维数组中的元素顺序
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                // 随机两个行列位置,让这两个位置交换。
                int i1 = (int) (Math.random() * imageData.length);
                int j1 = (int) (Math.random() * imageData.length);

                int i2 = (int) (Math.random() * imageData.length);
                int j2 = (int) (Math.random() * imageData.length);

                int temp = imageData[i1][j1];
                imageData[i1][j1] = imageData[i2][j2];
                imageData[i2][j2] = temp;
            }
        }
    }

    private void initMenu() {
        // 在主界面上方展示一个系统菜单,菜单下展示重启游戏和退出程序两个功能
        JMenuBar menuBar = new JMenuBar();
        JMenu menu = new JMenu("系统菜单");
        JMenuItem restartItem = new JMenuItem("重启游戏");
        restartItem.addActionListener(e -> {
            System.out.println("重启游戏!");
        });
        JMenuItem exitItem = new JMenuItem("退出程序");
        exitItem.addActionListener(e -> {
            // 退出程序,销毁窗口即可
            this.dispose();
        });
        menu.add(restartItem);
        menu.add(exitItem);
        menuBar.add(menu);
        this.setJMenuBar(menuBar);
    }

    // 在窗口上展示图片
    private void initImage() {
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data[i].length; j++) {
                // 获取图片名称
                String imageName = data[i][j] + ".png";
                // 创建图片对象
                ImageIcon imageIcon = new ImageIcon(IMAGE_PATH + imageName);
                // 创建图片标签
                JLabel jLabel = new JLabel(imageIcon);
                // 设置图片标签的位置
                jLabel.setBounds(20 + j * 100, 60 + i * 100, 100, 100);
                this.add(jLabel);
           }
       }

        // 设置窗口的背景图片
        JLabel background = new JLabel(new ImageIcon(IMAGE_PATH + "background.png"));
        background.setBounds(0, 0, 450, 484);
        this.add(background);
    }

    private void initFrame() {
        // 设置窗口的标题
        this.setTitle("石子迷阵 v1.0");
        // 设置窗口的大小
        this.setSize(465, 580);
        // 设置窗口的位置
        this.setLocationRelativeTo(null);
        // 设置窗口的关闭方式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 设置布局方式为绝对位置定位
        this.setLayout(null);

    }

    public static void main(String[] args) {
        new MainFrame();
    }
}

4、控制上下左右移动

  • 给窗口绑定上下左右按键事件。

  • 控制位置的交换。

    ​ --- 定位当前空白色块的位置。

    ​ --- 根据用户点击的方位确定交换哪个数据,到数组中交换。

  • 重新绘制主界面的内容。

    ​ --- 让主界面按照二维数组的最新内容刷新界面。

public enum Direction {
    UP, DOWN, LEFT, RIGHT;
}
package com.itheima;

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

// 自定义窗口类,创建对象,展示一个主窗口。
public class MainFrame extends JFrame {
    // 设置图片位置
    private static final String imagePath = "stone-maze/src/image/";
    // 准备一个数组,用户存储数字色块的行列位置: 4行4列
    private int[][] imageData = {
            {1,2,3,4},
            {5,6,7,8},
            {9,10,11,12},
            {13,14,15,0}
    };
    // 定义两个整数变量记录当前空白色块的位置。
    private int row; // 行索引
    private int col; // 列索引

    public MainFrame() {
        // 1、调用一个初始化方法:初始化窗口大小等信息。
        initFrame();
        // 4、打乱数组色块的顺序,再展示图片
        initRandomArray();
        // 2、初始化界面:展示数字色块。
        initImage();
        // 3、初始化系统菜单:点击弹出菜单信息是系统退出,重启游戏
        initMenu();
        // 5、给当前窗口绑定上下左右按键事件。
        initKeyPressEvent();
        // 设置窗口的显示
        this.setVisible(true);
    }

    private void initKeyPressEvent() {
        // 给当前窗口绑定上下左右按键事件。
        this.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                // 获取当前按钮的编号
                int keyCode = e.getKeyCode();
                // 判断这个编号是否是上下左右的按键
                switch (keyCode) {
                    case KeyEvent.VK_UP:
                        // 用户按了上键,让图片向上移动。
                        switchAndMove(Direction.UP);
                        break;
                    case KeyEvent.VK_DOWN:
                        // 用户按了下键,让图片向下移动。
                        switchAndMove(Direction.DOWN);
                        break;
                    case KeyEvent.VK_LEFT:
                        // 用户按了左键,让图片向左移动。
                        switchAndMove(Direction.LEFT);
                        break;
                    case KeyEvent.VK_RIGHT:
                        // 用户按了右键,让图片向右移动。
                        switchAndMove(Direction.RIGHT);
                        break;
                }
            }
        });
    }

    // 控制数据交换,和图片移动。
    private void switchAndMove(Direction r){
        // 判断图片的方向,再控制图片移动
        switch (r) {
            case UP:
                // 上交换的条件是行必须 < 3,然后才开始交换.
                if(row < imageData.length - 1){
                    // 当前空白色块位置:rol  col
                    // 需要被交换的位置:row + 1  col
                    int temp = imageData[row][col];
                    imageData[row][col] = imageData[row + 1][col];
                    imageData[row + 1][col] = temp;
                    // 更新当前空白色块的位置了。
                    row++;
                }
                break;
            case DOWN:
                if(row > 0){
                    // 当前空白色块位置:rol  col
                    // 需要被交换的位置:row - 1  col
                    int temp = imageData[row][col];
                    imageData[row][col] = imageData[row - 1][col];
                    imageData[row - 1][col] = temp;
                    // 更新当前空白色块的位置了。
                    row--;
                }
                break;
            case LEFT:
                // 左交换的条件是空白色块的列必须小于3
                if(col < imageData.length - 1){
                    // 当前空白色块位置:rol  col
                    // 需要被交换的位置:row   col + 1
                    int temp = imageData[row][col];
                    imageData[row][col] = imageData[row][col + 1];
                    imageData[row][col + 1] = temp;
                    // 更新当前空白色块的位置了。
                    col++;
                }
                break;
            case RIGHT:
                if(col > 0){
                    // 当前空白色块位置:rol  col
                    // 需要被交换的位置:row   col - 1
                    int temp = imageData[row][col];
                    imageData[row][col] = imageData[row][col - 1];
                    imageData[row][col - 1] = temp;
                    // 更新当前空白色块的位置了。
                    col--;
                }
                break;
        }
        // 重新刷新界面!!!
        initImage();
    }

    private void initRandomArray() {
        // 打乱二维数组中的元素顺序
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                // 随机两个行列位置,让这两个位置交换。
                int i1 = (int) (Math.random() * imageData.length);
                int j1 = (int) (Math.random() * imageData.length);

                int i2 = (int) (Math.random() * imageData.length);
                int j2 = (int) (Math.random() * imageData.length);

                int temp = imageData[i1][j1];
                imageData[i1][j1] = imageData[i2][j2];
                imageData[i2][j2] = temp;
            }
        }

        // 定位空白色块的位置。
        // 去二维数组中遍历每个数据,只要发现这个数据等于0,这个位置就是当前空白色块的位置。
        OUT:
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                if (imageData[i][j] == 0) {
                    // 定位到空白色块的位置。
                    row = i;
                    col = j;
                    break OUT; // 跳出循环。
                }
            }
        }
    }

    private void initMenu() {
        JMenuBar menuBar = new JMenuBar(); // 创建一个菜单条
        JMenu menu = new JMenu("系统"); // 创建一个菜单
        JMenuItem exitJi = new JMenuItem("退出");
        menu.add(exitJi); // 添加一个菜单项
        exitJi.addActionListener(e -> {
            dispose(); // 销毁!
        });
        // 添加一个菜单,重启
        JMenuItem restartJi = new JMenuItem("重启");
        menu.add(restartJi);
        restartJi.addActionListener(e -> {
             // 重启游戏。
        });
        menuBar.add(menu); // 添加到菜单条中
        this.setJMenuBar(menuBar);
    }

    private void initImage() {
        // 先清空窗口上的全部图层
        this.getContentPane().removeAll();


        // 1、展示一个行列矩阵的图片色块依次铺满窗口(4 * 4)
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                // 拿到图片的名称
                String imageName = imageData[i][j] + ".png";
                // 2、创建一个JLabel对象,设置图片给他展示。
                JLabel label = new JLabel();
                // 3、设置图片到label对象中去。
                label.setIcon(new ImageIcon( imagePath + imageName));
                // 4、设置图片位置展示出来
                label.setBounds(20 + j * 100,60 + i * 100, 100, 100);
                // 5、把这个图片展示到窗口上去
                this.add(label);
            }
        }

        // 设置窗口的背景图片
        JLabel background = new JLabel(new ImageIcon(imagePath + "background.png"));
        background.setBounds(0, 0, 450, 484);
        this.add(background);

        // 刷新新图层,重新绘制
        this.repaint();
    }

    private void initFrame() {
        // 设置窗口的标题
        this.setTitle("石子迷宫 V 1.0 dlei");
        // 设置窗口的宽高
        this.setSize(465,575);
        // 设置窗口的关闭方式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 设置窗口的居中显示
        this.setLocationRelativeTo(null);
        // 设置布局方式为绝对位置定位
        this.setLayout(null);
    }
}

5、判断是否通关

  • 用户每操作一步,需要立即判断是否已经通关,如果通过,需要显示胜利的标记。

 

private int[][] winData = new int[][]{
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12},
        {13, 14, 15, 0}
};
private boolean isWin() {
    for (int i = 0; i < data.length; i++) {
        for (int j = 0; j < data[i].length; j++) {
            if(data[i][j] != winData[i][j]){
                return false;
            }
        }
    }
    return true;
}
if(isWin()){
    JLabel winLabel = new JLabel(new ImageIcon(IMAGE_PATH + "win.png"));
    winLabel.setBounds(124, 230, 266, 88);
    this.add(winLabel);
}

6、统计移动步骤、重启游戏

  • 每成功移动一步,都需要累加一次步数。

  • 定义一个变量用于累加步数,并实时展示到界面上。 


以上就是本次的学习分享,希望对大家有所帮助,关注我!日更分享有用干货!!!

如果有疑问的可以评论提出来~ 

import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.JButton; import javax.swing.JFrame; // ,, public class Hua_Rong_Road extends JFrame implements ActionListener, MouseListener, KeyListener { /** * */ private static final long serialVersionUID = 1L; Person person[] = new Person[10]; JButton left, right, above, below; JButton restart = new JButton("重新开始"); JButton nextpass = new JButton("下一关"); public Hua_Rong_Road() { init(); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setBounds(100, 100, 320, 500); setVisible(true); validate(); } public void init() { setLayout(null); add(restart); restart.setBounds(100, 360, 120, 35); restart.addActionListener(this); add(nextpass); nextpass.setBounds(100, 320, 120, 35); nextpass.addActionListener(this); String name[] = { "曹操", "关羽", "张", "刘", "周", "黄", "兵", "兵", "兵", "兵" }; for (int k = 0; k < name.length; k++) { person[k] = new Person(k, name[k]); person[k].addMouseListener(this); person[k].addKeyListener(this); add(person[k]); } person[0].setBounds(104, 54, 100, 100); person[1].setBounds(104, 154, 100, 50); person[2].setBounds(54, 154, 50, 100); person[3].setBounds(204, 154, 50, 100); person[4].setBounds(54, 54, 50, 100); person[5].setBounds(204, 54, 50, 100); person[6].setBounds(54, 254, 50, 50); person[7].setBounds(204, 254, 50, 50); person[8].setBounds(104, 204, 50, 50); person[9].setBounds(154, 204, 50, 50); person[9].requestFocus(); left = new JButton();// 四个边界 right = new JButton(); above = new JButton(); below = new JButton(); add(left); add(right); add(above); add(below); left.setBounds(49, 49, 5, 260); right.setBounds(254, 49, 5, 260); above.setBounds(49, 49, 210, 5); below.setBounds(49, 304, 210, 5); validate(); } @Override public void keyPressed(KeyEvent e) { Person man = (Person) e.getSource(); if (e.getKeyCode() == KeyEvent.VK_DOWN) go(man, below); if (e.getKeyCode() == KeyEvent.VK_UP) go(man, above); if (e.getKeyCode() == KeyEvent.VK_LEFT) go(man, left); if (e.getKeyCode() == KeyEvent.VK_RIGHT) go(man, right); } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void go(Person man, JButton direction) { boolean move = true; Rectangle manRect = man.getBounds(); int x = man.getBounds().x; int y = man.getBounds().y; if (direction == below) y = y + 50; else if (direction == above) y = y - 50; else if (direction == left) x = x - 50; else if (direction == right) x = x + 50; manRect.setLocation(x, y); Rectangle dirctionRect = direction.getBounds(); for (int k = 0; k < 10; k++) { Rectangle personRect = person[k].getBounds(); if ((manRect.intersects(personRect)) && (man.number != k)) move = false; } if (manRect.intersects(dirctionRect)) move = false; if (move == true) man.setLocation(x, y); } public void actionPerformed(ActionEvent e) { dispose(); new Hua_Rong_Road(); } } public class MainClass { public static void main(String[] args) { new Hua_Rong_Road(); } } import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.Color; import javax.swing.JButton; public class Person extends JButton implements FocusListener { /** * */ private static final long serialVersionUID = 1L; int number; Color c=new Color(255, 245, 170); public Person(int number,String s) { super(s); setBackground(c); this.number=number; c=getBackground(); addFocusListener(this); } @Override public void focusGained(FocusEvent e) { setBackground(Color.red); } @Override public void focusLost(FocusEvent e) { setBackground(c); } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值