1.思路与分析
首先我们需要提供几个面板,一些菜单栏以及一些按钮,按照你所需要拼成的图片的一些小切片(可以4*4或者5*5,总之按照你的图片大小来定),定义一个控制图片移动的函数,还需要对你的函数方法及菜单按钮提供监听,然后我们就可以将这些想法付诸行动了。
2.程序代码及分析
1.拼图游戏app总代码
package op1; public class app { public static void main(string[] args) { // new registframe(); //注册 new loginframe(); //登录 // new gameframe(); //游戏 } }
我们由登录页面引出其他页面,先调用登录函数,运行代码时,先弹出登录页面
如果输入为空,则会弹出如下提示框
如果输入错误或者没有注册,则会弹出如下提示框
随后弹出注册页面
如果输入为空,则会弹出如下提示框
如果注册成功,则会弹出如下提示框
然后便可进入登录页面,进行登录
随后进入游戏页面
2.登录页面代码
package op1; import java.awt.flowlayout; import java.awt.event.actionevent; import java.awt.event.actionlistener; import java.io.bufferedreader; import java.io.bufferedwriter; import java.io.filenotfoundexception; import java.io.filereader; import java.io.filewriter; import java.io.ioexception; import javax.swing.*; public class loginframe extends jframe implements actionlistener { jframe dk = new jframe("登录"); // 添加按钮 jbutton login = new jbutton("登录"); jbutton exit = new jbutton("退出"); // 添加标签 jlabel name1 = new jlabel("用户名"); jlabel pwd1 = new jlabel("密码"); // 添加文本输入框 jtextfield name = new jtextfield(13); jtextfield password = new jtextfield(13); public loginframe() { initloginjframe();// 初始化界面 } private void initloginjframe() { dk.setsize(210, 200); dk.setalwaysontop(true); // dk.setlocationrelativeto(null); dk.setdefaultcloseoperation(2); dk.setlayout(new flowlayout()); dk.add(name1); dk.add(name); dk.add(pwd1); dk.add(password); dk.add(login); dk.add(exit); login.addactionlistener(this); exit.addactionlistener(this); dk.setvisible(true); } private void initview() { } @override public void actionperformed(actionevent e) { if (e.getsource() == login) { if (name.gettext().equals("") || password.gettext().equals("")) { 当用户名或密码文本框内的内容为空时 joptionpane.showmessagedialog(this, "用名或密码不能为空");// 出现对话框提醒 name.settext("");// 清空文本框 password.settext("");// 清空文本框 } else { try { bufferedwriter w = new bufferedwriter(new filewriter("d:\\用户信息2.0.txt", true));// true追加录入,录入用户信息 string sum = name.gettext() " " password.gettext();// 用户名与密码之间用空格连接 bufferedreader r = new bufferedreader(new filereader("d:\\用户信息2.0.txt"));// 读出用户信息 string text; boolean c = false; while ((text = r.readline()) != null) { if (sum.equals(text)) { // 循环排查,看录入的信息是否与读取的信息相同,如果相同 c = true; } // 则c为true,登陆成功,不同,c为false,则登录失败 } if (c == true) { joptionpane.showmessagedialog(this, "登录中!!!"); dk.setvisible(false);// 关闭当前窗口 new gameframe(); } else { joptionpane.showmessagedialog(this, "用名或密码错误或没有注册,请重新输入或进入注册!!!"); new registframe(); name.settext("");// 清空文本框 password.settext("");// 清空文本框 } } catch (ioexception ee) { } } } if (e.getsource() == exit) { dk.setvisible(false);// 关闭当前窗口 } } }
这是实现登录页面的函数,为了方便理解,我添加了注释。
3.注册页面代码
package op1; import javax.swing.*; import java.awt.flowlayout; import java.awt.event.actionevent; import java.awt.event.actionlistener; import java.io.bufferedreader; import java.io.bufferedwriter; import java.io.filereader; import java.io.filewriter; import java.io.ioexception; public class registframe extends jframe implements actionlistener { jframe dk = new jframe("注册"); // 添加按钮 jbutton regist = new jbutton("注册"); jbutton exit = new jbutton("退出"); // 添加标签 jlabel name1 = new jlabel("用户名"); jlabel pwd1 = new jlabel("密码"); // 添加文本输入框 jtextfield name = new jtextfield(13); jtextfield password = new jtextfield(13); public registframe() { initregistjframe();// 初始化界面 } private void initregistjframe() { dk.setsize(210, 200); dk.setalwaysontop(true); dk.setlocationrelativeto(null); dk.setlocation(200, 200); // 设置窗口位置 dk.setdefaultcloseoperation(2); dk.setlayout(new flowlayout()); dk.add(name1); dk.add(name); dk.add(pwd1); dk.add(password); dk.add(regist); dk.add(exit); regist.addactionlistener(this); exit.addactionlistener(this); dk.setvisible(true); } @override public void actionperformed(actionevent e) { if (e.getsource() == regist) { if (name.gettext().equals("") || password.gettext().equals("")) {// 当用户名或密码文本框内的内容为空时 joptionpane.showmessagedialog(this, "用户名或密码不能为空"); // 出现对话框提醒 name.settext("");// 清空文本框 password.settext("");// 清空文本框 } else { try { bufferedwriter w = new bufferedwriter(new filewriter("d:\\用户信息2.0.txt", true));// true追加录入,录入用户信息 string sum = name.gettext() " " password.gettext();// 用户名与密码之间用空格连接 bufferedreader r = new bufferedreader(new filereader("d:\\用户信息2.0.txt"));// 读出用户信息 boolean c = true; string text; while ((text = r.readline()) != null) { if (sum.equals(text)) { // 循环排查,看录入的信息是否与读取的信息相同,如果相同 c = false; // 则c为false,该用户已存在,不同,c为true,则注册成功 } } if (c == true) { w.write(sum); // 将信息写入文件 w.newline();// 生成换行符 w.close();// 关闭文件 r.close(); joptionpane.showmessagedialog(this, "注册成功!"); dk.setvisible(false);// 关闭当前窗口 new loginframe(); } else { joptionpane.showmessagedialog(this, "该用户已存在!"); name.settext("");// 清空文本框 password.settext("");// 清空文本框 } } catch (ioexception ee) { } } } if (e.getsource() == exit) { dk.setvisible(false);// 关闭当前窗口 } } }
与登录页面的代码差不多。
4.游戏页面代码
package op1; import java.awt.event.actionevent; import java.awt.event.actionlistener; import java.awt.event.keyevent; import java.awt.event.keylistener; import java.util.random; import javax.swing.*; import javax.swing.border.bevelborder; public class gameframe extends jframe implements keylistener, actionlistener {// 继承一个,实现两个接口 // jframe 界面,窗体 // gameframe即游戏主界面 // 与游戏相关的逻辑都比在此javabean类中 // 创建一个二维数组,加载图片 int[][] data = new int[4][4]; // 记录空白格在二维数组中的位置 int x = 0; int y = 0; // 定义二维数组,正确存储数据,即拼图胜利时,图片的正确顺序 int[][] win = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 0 } }; // 定义变量统计步数 int step = 0; // 创建菜单选项相关的对象(这几项之所以放在initjmenubar()之外,是因为后面方法重写时要调用) jmenuitem replayitem = new jmenuitem("重新游戏"); // 菜单项,创建选项下面的条目对象 jmenuitem closeitem = new jmenuitem("退出"); jmenuitem accountitem = new jmenuitem("关于kb88凯时官网登录"); jmenuitem reloginitem = new jmenuitem("重新登录"); public gameframe() { initjframe();// 初始化界面 initjmenubar();// 初始化菜单 initdata();// 初始化数据(打乱数据) initimage();// 初始化图片(根据打乱的结果加载图片) this.setvisible(true);// 让界面显示出来,可视化 } // 初始化数据(打乱数据) 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(); // 产生随机数r 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; } // 遍历一维数组arr中的每一个元素,并依次添加到二维数组中 for (int i = 0; i < arr.length; i ) { if (arr[i] == 0) { // 确定空白格的位置 x = i / 4; y = i % 4; } data[i / 4][i % 4] = arr[i]; // 一维数组转二维数组,并打乱顺序 } } // 按二维数组中管理的数据添加图片 private void initimage() { // 清空原本已经出现的所有图片 this.getcontentpane().removeall(); // 将方框里的内容全部清除 if (victory()) { // 显示胜利的图标 jlabel winjlabel = new jlabel(new imageicon("e:/java/op1/op1/win.png")); winjlabel.setbounds(203, 283, 197, 73); // 距屏幕左面203个像素,上方283个像素,窗口宽197,长73 this.getcontentpane().add(winjlabel); // 将胜利图标添加到内容方框 } jlabel stepcount = new jlabel("步数:" step); stepcount.setbounds(50, 30, 100, 20); this.getcontentpane().add(stepcount); // 将步数添加到内容方框 // 先加载的图片在上方,后加载的图片在下面 // 外循环:将内循环重复执行4次 for (int i = 0; i < 4; i ) { // 内循环:一行添加4张图片,例:第一行(0,0),(0,1),(0,2),(0,3) for (int j = 0; j < 4; j ) { // 获取要加载图片序号 int num = data[i][j]; // 创建jlabel的对象(管理容器) jlabel jlabel = new jlabel(new imageicon("e:/java/op1/op1/" num ".jpg"));// 菜单上的图标 // 指定图片位置 jlabel.setbounds(105 * j 90, 105 * i 130, 105, 105); // 图片添加边框,0:凸,1:凹 jlabel.setborder(new bevelborder(bevelborder.lowered)); // 把管理容器添加到界面 this.getcontentpane().add(jlabel); } } // 添加背景图片 jlabel background = new jlabel(new imageicon("e:/java/op1/op1/background.png")); background.setbounds(50, 40, 508, 560); // 把背景图片添加到界面 this.getcontentpane().add(background); // 刷新界面 this.getcontentpane().repaint(); } private void initjmenubar() { // 总菜单,菜单条 // 创建菜单对象 jmenubar jmenubar = new jmenubar(); // 创建菜单上面的两个选项的对象 jmenu functionjmenu = new jmenu("功能"); // 子菜单,创建两个菜单选项的对象 jmenu aboutjmenu = new jmenu("帮助"); // 将每一个选项下面的条目添加到选项当中 functionjmenu.add(replayitem); functionjmenu.add(reloginitem); functionjmenu.add(closeitem); aboutjmenu.add(accountitem); // 给条目绑定事件,以便实现动作监听 replayitem.addactionlistener(this); // 添加动作监听,则必有actionlistener接口,必有唯一的方法重写 closeitem.addactionlistener(this); accountitem.addactionlistener(this); reloginitem.addactionlistener(this); // 将两个选项添加到菜单 jmenubar.add(functionjmenu); jmenubar.add(aboutjmenu); //设置菜单 this.setjmenubar(jmenubar); } private void initjframe() { // 设置界面的宽高 this.setsize(700, 700); // 设置一个标题为拼图v2.0的窗口 this.settitle("拼图v2.0"); // 设置界面置顶 this.setalwaysontop(true); // 设置界面居中 this.setlocationrelativeto(null); // 设置关闭模式 this.setdefaultcloseoperation(2); // 根据参数的取值不同,做出不同的处理 // 取消默认居中放置,按照xy轴形式添加组件 this.setlayout(null); // 空布局,相当于自定义布局 // 添加键盘监听事件 this.addkeylistener(this); // 添加键盘监听,触发keyevent事件,调用三种方法,看情况选择 } @override public void keytyped(keyevent e) { // 调用keytyped()方法,并重写 } // 如果是65,就是按下不松调用的方法,如果是112,就是按下松开调用的方法 @override public void keypressed(keyevent e) { int code = e.getkeycode(); if (code == 112 || code == 65) { // 112代表f1,65代表a,查看原图 // 删除界面中所有图片 this.getcontentpane().removeall(); // 加载已经拼好原图 jlabel all = new jlabel(new imageicon("e:/java/op1/op1/all.jpg")); all.setbounds(30, 70, 420, 420); this.getcontentpane().add(all); // 加载背景图片 // jlabel background = new jlabel(new imageicon("background.png")); // background.setbounds(30, 30, 400, 500); // 把背景图片添加到界面 // this.getcontentpane().add(background); // 刷新界面 this.getcontentpane().repaint(); } } // 松开按键时调用的方法 @override public void keyreleased(keyevent e) { // 判断游戏是否胜利,if胜,直接结束,不再执行之后的移动代码 if (victory()) { // 结束方法 return; } // 判断左:37 上:38 右:39 下:40 讨论四种情况 int code = e.getkeycode(); // 采用键码值 system.out.println(code); if (code == 37) { system.out.println("向左移动"); if (y == 3) { return; } // 空白格右方的数字往左移动 data[x][y] = data[x][y 1]; // 交换所移动图片与空白格的位置 data[x][y 1] = 0; y ; // 空白格位置改动,所以y // 每移动一次,计数器就自增一次 step ; // 调用方法按照最新的数字加载图片 initimage(); } else if (code == 38) { system.out.println("向上移动"); if (x == 3) { // 空白格已经在最下方,无图片可移 return; } // 空白格下方的数字往上移动 // x,y 表示空白格;x 1,y 表示空白格下方数字 // 空白格下方的数字赋值给空白格 data[x][y] = data[x 1][y]; data[x 1][y] = 0; x ; // 每移动一次,计数器就自增一次 step ; // 调用方法按照最新的数字加载图片 initimage(); } else if (code == 39) { system.out.println("向右移动"); if (y == 0) { return; } // 空白格左方的数字往右移动 data[x][y] = data[x][y - 1]; data[x][y - 1] = 0; y--; // 每移动一次,计数器就自增一次 step ; // 调用方法按照最新的数字加载图片 initimage(); } else if (code == 40) { system.out.println("向下移动"); if (x == 0) { return; } // 空白格上方的数字往下移动 data[x][y] = data[x - 1][y]; data[x - 1][y] = 0; x--; // 每移动一次,计数器就自增一次 step ; // 调用方法按照最新的数字加载图片 initimage(); } else if (code == 65) { // 65代表a,刷新界面 initimage(); } else if (code == 87) { // 87代表w,作弊码,一键生成 data = new int[][] { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 0 } }; initimage(); } } // 判断data数组中的数据是否跟win数组中相同 // 如果全部相同,返回true。否则返回false public boolean victory() { for (int i = 0; i < data.length; i ) { // i : 依次表示二维数组data里面的索引 // data[i]:依次表示每一个一维数组 for (int j = 0; j < data[i].length; j ) { if (data[i][j] != win[i][j]) { // 只要有一个数据不一样,则返回false return false; } } } // 循环结束表示数组遍历比较完毕,全都一样返回true return true; } @override public void actionperformed(actionevent e) { // 方法重写,并实现actionevent类的getsource()方法 // 获取当前被点击的条目对象 object obj = e.getsource(); // 判断 if (obj == replayitem) { system.out.println("重新游戏"); // 计步器清零 step = 0; // 再次打乱二维数组中的数据 initdata(); // 重新加载图片 initimage(); } else if (obj == reloginitem) { system.out.println("重新登录"); // 关闭当前界面 this.setvisible(false); // 返回登录界面 new loginframe(); } else if (obj == closeitem) { system.out.println("关闭游戏"); // 直接关闭虚拟机 system.exit(0); } else if (obj == accountitem) { system.out.println("关于kb88凯时官网登录"); // 创建弹框对象 jdialog jdialog = new jdialog(); // 发现新大陆,实现弹窗 // 创建管理图片的容器对象jlabel // jlabel jlabel = new jlabel(new imageicon("about.png")); // 设置位置和宽高 // jlabel.setbounds(0,0,258,258); // 把图片添加到弹框 // jdialog.getcontentpane().add(jlabel); // 弹框大小 jdialog.setsize(344, 344); // 设置提示语 jlabel clue = new jlabel("按f1或长按a显示原图,按a刷新一下,按w一键拼好"); clue.setbounds(0, 0, 100, 20); jdialog.getcontentpane().add(clue); // 弹框置顶 jdialog.setalwaysontop(true); // 弹框居中 jdialog.setlocationrelativeto(null); // 弹框不关闭则无法操作下面的界面 jdialog.setmodal(true); // 弹框显示出来 jdialog.setvisible(true); } } }
这里的代码就显得复杂一些,它包括窗口组件的实现,事件监听的实现,以及拼图移动的实现,如果拼图成功,则会弹出拼图成功的提示图片。
3.总结
以上就是就是简单的拼游戏的代码的实现及分析,大家有兴趣可以自己去试试