AI+Java开发项目——石头迷阵游戏
做一个有趣一点的项目,一方面提升编程思维,提升编程能力。同时开发一个实战的游戏,可以跟学java的人员拉开距离。
游戏项目开发的思路:
1.GUI界面编程 :使用Swing技术
2.二维数组
3.程序流程控制:因为在业务的推进过程中,肯定会涉及到一些循环的操作。再包括比如把拼图顺序打乱,肯定需要程序流程控制,循环。
4.面向对象编程:这个界面需要去继承一个Jframe成为一个窗口对象。他们是需要我们的一个面向对象编程的语法来理解的,包括这里面的事件监听器上下左右。给他绑定匿名内部类对象多需要一些匿名内部类的知识。
准备环节
- 创建一个模块用于开发石头迷阵游戏,模块名称取名为:stone-maze
- 导入项目需要的资源包到src目录下:主要是一些图片文件,在image文件夹下。
- 创建项目包:com.itheima.
一、创建石头迷阵界面
- 定义主界面类,MainFrame继承JFrame.
独立功能独立成方法,在MainFrame.java中不仅要初始化窗口,还要初始化界面,初始化菜单。分三块功能进行制作,所以不要在这里直接写这个代码,最好在无参构造器里面。 当别人调用无参构造器时在其里面进行初始化。这样的话程序比较逻辑清晰。代码复用性比较高,比较优雅。
- 初始化窗口大小
窗口大小要根据图片的大小和实际项目的需求来进行设定。注意:显示窗口一定要设置到最后,当日常AI生成的代码没有运行没有显示出时,有可能就是这个问题。
- 初始化界面图片
- 初始化界面菜单:系统退出,重启游戏。
二、打乱顺序
打乱界面的图片顺序,让游戏具备可玩性:使用方法如下
打乱二维数组中的元素顺序:initRandomArray();
完成代码如下:
package com.itheima;import javax.swing.*;
//自定义窗口类,创建对象,展示一个主窗口。
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}};public MainFrame() {//1.调用一个初始化方法:初始化窗口大小等信息initFrame();//4.打乱数组色块的顺序,再展示图片initRandomArray();//2.初始化界面,展示数字色块。initImage();//3.初始化系统菜单,点击弹出菜单信息是系统退出,重启游戏initMenu();//设置窗口的显示.注意显示窗口一定要设置到最后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 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() {//1.展示一个行列矩阵的图片色块依次铺满窗口(4*4)for (int i = 0; i < imageData.length; i++) {for (int j = 0; j < imageData[i].length; j++) {//2.拿到图片名称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代表当前窗口主对象this.add(label);}}//设置窗口的背景图片(背景图片太小了所以没展示)JLabel background = new JLabel(new ImageIcon(imagePath + "background.png"));background.setBounds(0, 0, 450, 484);this.add(background);}private void initFrame() {//设置窗口的标题this.setTitle("石子迷阵 V 1.0 xuan");//设置窗口的宽高this.setSize(465, 575);//设置窗口关闭方式this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗口居中this.setLocationRelativeTo(null);//设置布局方式为绝对位置定位,一定要设置绝对布局,用于展示图片在坐标上的位置this.setLayout(null);}
}
package com.itheima;public class App {public static void main(String[] args) {new MainFrame();}
}
三、控制上下左右移动
- 给窗口绑定上下左右按键事件
- 控制位置的交换
——定位当前空白色块的位置
——根据用户点击的方位确定交换哪个数据,到数组中交换
- 重新绘制主界面的内容
——让主界面按照二维数组的最新内容刷新界面
//5.给当前窗口绑定上下左右按键事件。initKeyPressEvent();private void initKeyPressEvent() {//给当前窗口绑定上下左右按键事件。this.addKeyListener(new KeyAdapter() {@Overridepublic 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;}}});}
package com.itheima;
//使用枚举来定义方向,使项目更加专业
public enum Direction {UP,DOWN,LEFT,RIGHT;
}
四、判断是否通关
- 用户每操作一步,需要立即判断是否已经通过,如果通过,需要显示胜利的标记。
//判断是否赢了。我们设置只要用户刷新界面就要判断一次用户是否胜利。//且将图层设置在所有界面的上面if (isWin()) {//展示胜利的图片JLabel label = new JLabel(new ImageIcon(imagePath + "win.png"));label.setBounds(124, 230, 266, 88);this.add(label);}//1.展示一个行列矩阵的图片色块依次铺满窗口(4*4)for (int i = 0; i < imageData.length; i++) {for (int j = 0; j < imageData[i].length; j++) {//2.拿到图片名称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代表当前窗口主对象this.add(label);}}//设置窗口的背景图片(背景图片太小了所以没展示)JLabel background = new JLabel(new ImageIcon(imagePath + "background.png"));background.setBounds(0, 20, 450, 484);this.add(background);//刷新新图层,重新绘制this.repaint();
private boolean isWin() {//判断游戏二维数组和赢了之后的二维数组的内容是否一样,只要有一个位置处的数据不一样,说明没有赢了for (int i = 0; i < imageData.length; i++) {for (int j = 0; j < imageData[i].length; j++) {if (imageData[i][j] != winData[i][j]) {return false;}}}//赢了return true;}
五、统计移动步骤、重启游戏
- 没成功移动一步,都需要累加一次步数
- 定义一个变量用于累加步数,并实时展示到界面上
private int count;//统计总共移动的步数。//先清空窗口上的全部图层this.getContentPane().removeAll();//刷新界面时,可以给界面显示步数。//给窗口添加一个展示文字的组件JLabel countTxt = new JLabel("当前移动" + count + "步");countTxt.setBounds(20, 20, 100, 20);//把文字展示成红色countTxt.setForeground(Color.RED);//加粗countTxt.setFont(new Font("楷体", Font.BOLD, 12));this.add(countTxt);
六、拓展问题
数字华容道的乱序操作,并不是可以随意打乱的,必须满足一定的算法去打乱顺序,这样才是有解的,才能让玩家恢复到有序。有没有简单的算法????