【JavaFx】使用JavaFx实现拼图游戏 实现键盘操作
2019-06-04 10:23
417 查看
- 参考博客:https://blog.csdn.net/qq_42370146/article/details/84842168#commentBox
- 在原博主的拼图游戏上添加了键盘控制系统,即通过方向键上下左右进行交换。
- 思路很简单,就是通过GridPane获取坐标的功能计算出上下左右的坐标,然后通过已经实现的交换方法进行交换,但是交换方法必须获得两个源,而:
- GridPane不支持通过坐标访问源,所以算出坐标后并不能直接访问来进行交换,所以如何通过坐标来联系上在那个位置的那张图片的源就是难点。
- 解决方法:
n[]
这个随机数组中值虽然是随机的,但是n[1],n[2]
却是依次排列的,比如imageview[n[5]]
就代表第5张图,这样就可以通过以n[5]的值作为ImageView的索引来实现访问第几张图了。然后就可以进行交换了。 - 但是交换过去后有一个很严重的问题,比如在向上交换后再向下交换,就是把第九张图和第六张图交换之后想再换回去时,现在空白方块在地址5,通过计算下面的方块地址为8,应该和n[8]交换,但是n[8]其实还是刚刚的第九张图,因此就会一直和自己交换,程序看上去就一动不动。
- 其实我们交换的是图片的源,和
n[]
并没有关系,所以n[]
还是原来的样子,并没有跟着一起交换,这样就导致算出来的新坐标对应的还是老的索引,因此我们应该把n[]
也跟着一起交换,达到一致。而n[]
中只有8个元素,我们要交换的话,需要九个元素都在,因此要创建一个新数组,前八个元素一样,把缺了的元素m补在最后。 - 实现:使用一个
int[] ncopy = new int[9]
来拓展随机数组n[8]
。通过遍历将前八个元素复制,并将m添加到ncopy最后。在源图片交换后,将数组里的元素也交换。
- 键盘操作部分代码:
switch (arg0.getCode()) { case DOWN: // 计算出需要交换的对象图片的位置nindex int nrow = mrow + 1; int ncol = mcol; int nindex = nrow * 3 + ncol; if (nindex >= 0 && nindex <= 8) { // 由于n[]中的索引是和位置一一对应的,再将其值作为imageViews的索引 // 只要算出nindex,就能对应上相应的图片。将其交换 // ncopy拓展了n,将m作为第九个元素加入数组,否则就访问不到最后一张图片。 myevent1.swapimg(imageViews[m], imageViews[ncopy[nindex]]); // 图片交换后,将数组值也交换,否则多次操作之后数据会混乱,导致胡乱交换。 int temp2 = ncopy[mindex]; ncopy[mindex] = ncopy[nindex]; ncopy[nindex] = temp2; } break;
- Game_1:
package application; import java.util.Random; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.geometry.Rectangle2D; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.control.Alert.AlertType; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; import javafx.scene.input.KeyEvent; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import java.util.Arrays; public class Game_1 extends Application { public int m; // m是不在随机数组的那个数字 ImageView[] imageViews = new ImageView[9]; Image image2 = new Image("application/test.jpg", 800, 800, false, false); public int partImagewidth = (int) image2.getHeight() / 3; public int[] ncopy; @Override public void start(Stage primaryStage) { init(primaryStage); } // 绘制拼图界面和按钮 public void init(Stage primaryStage) { // 父布局 VBox vBox = new VBox(); // 3*3九宫格布局,作为上方的选项嵌套入父布局 GridPane gridPane = new GridPane(); // 水平布局,作为下方的选项嵌套入父布局 HBox hbox = new HBox(); hbox.setPadding(new Insets(10)); // 填充 hbox.setAlignment(Pos.CENTER); // 居中 // 添加3个点击 Button home = new Button("返回首页"); home.setOnAction(e -> { Start start = new Start(); start.start(new Stage()); primaryStage.close(); }); Button again = new Button("重新游戏"); again.setOnAction(e -> { Game_1 game = new Game_1(); game.start(new Stage()); primaryStage.close(); }); Button look = new Button("查看原图"); look.setOnAction(e -> { OnImage_1 OnImage = new OnImage_1(); OnImage.start(new Stage()); }); // 设置按钮大小并居中 home.setPrefSize(200, 50); again.setPrefSize(200, 50); look.setPrefSize(200, 50); // 设置控件之间的距离 hbox.setSpacing(100); hbox.getChildren().addAll(home, again, look); // 游戏部分 /* * 自定义的函数,产生逆序数为偶数的不重复数组,依次作为图片数组的下标,例如: 2 0 1 3 4 5 6 7 ------>,此时m=8,变成如下即为正确: * 0 1 2 3 4 5 4 7 */ // 定义逆序数为偶数的随机数组 int[] n = random(); // 找出那个不在随机数组里面的数字 m = findnum(n); // 定义一个二维数组来存放随机数和地址 在键盘操作中用到 ncopy = new int[9]; for (int i = 0; i < 9; i++) { if (i == 8) { ncopy[8] = m; } else { ncopy[i] = n[i]; } } for (int i = 0; i < 9; i++) { imageViews[i] = new ImageView(image2); // 初始化数组 imageViews[i].setOnMouseClicked(new myevent()); // 设置点击事件 imageViews[i].setOnKeyPressed(new myKeyEvent());// 设置键盘事件 } // 切割图片分配给图片数组 for (int i = 0, k = 0; i <= 2; i++) { for (int j = 0; j <= 2; j++, k++) { imageViews[k].setViewport( new Rectangle2D(partImagewidth * j, partImagewidth * i, partImagewidth, partImagewidth)); } } // 按照产生的随机数将imageView数组加入面板 gridPane.add(imageViews[n[0]], 0, 0); gridPane.add(imageViews[n[1]], 1, 0); gridPane.add(imageViews[n[2]], 2, 0); gridPane.add(imageViews[n[3]], 0, 1); gridPane.add(imageViews[n[4]], 1, 1); gridPane.add(imageViews[n[5]], 2, 1); gridPane.add(imageViews[n[6]], 0, 2); gridPane.add(imageViews[n[7]], 1, 2); // 设定空白区域 Image image1 = new Image("file:C:\\Users\\CharlesChen\\Desktop\\empty.png"); // 3.png为一个透明图,放在空格子中 imageViews[m].setImage(image1); gridPane.add(imageViews[m], 2, 2); gridPane.setGridLinesVisible(true); gridPane.setPrefWidth(800); gridPane.setPrefHeight(800); vBox.getChildren().add(gridPane); // 布局嵌套,选项部分 vBox.getChildren().add(hbox); Scene scene = new Scene(vBox, 800, 850); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setTitle("智障拼图"); primaryStage.setScene(scene); primaryStage.setResizable(true); primaryStage.show(); imageViews[m].requestFocus(); } // 生成8个不重复的逆序数为偶数的数字,这样拼图才有解 public int[] random() { int[] ran = new int[8]; while (iso(ran) == false) { ran = random_num(); } return ran; } // 生成8个不重复数 public int[] random_num() { int r[] = new int[8]; Random random = new Random(); for (int i = 0; i < 8; ++i) { r[i] = random.nextInt(9); for (int j = 0; j < i; ++j) { while (r[i] == r[j]) { i--; break; } } } return r; } // 判断逆序数是否为偶数 public boolean iso(int[] num) { int sum = 0; for (int i = 0; i <= 6; ++i) { for (int j = i; j <= 7; j++) { if (num[i] > num[j]) { sum++; } } } if ((sum % 2) == 0 && sum != 0) { return true; } return false; } // 点击事件的实现 class myevent implements EventHandler<MouseEvent> { @Override public void handle(MouseEvent arg0) { // 取得鼠标点击位置处图片的位置 ImageView img = (ImageView) arg0.getSource(); double sx = img.getLayoutX(); double sy = img.getLayoutY(); // 计算与空白图片位置的差值 double dispx = sx - imageViews[m].getLayoutX(); double dispy = sy - imageViews[m].getLayoutY(); if ((dispx == -partImagewidth) && (dispy == 0)) { // 点击的空格左边的格子 swapimg(img, imageViews[m]); // 交换imageView if (issucc(imageViews)) { // 判断是否拼成功 Alert alert = new Alert(AlertType.WARNING, "恭喜你,拼图成功!"); alert.show(); } } else if ((dispx == 0) && (dispy == -partImagewidth)) { // 上面的格子 swapimg(img, imageViews[m]); if (issucc(imageViews)) { Alert alert = new Alert(AlertType.WARNING, "恭喜你,拼图成功!"); alert.show(); } } else if ((dispx == partImagewidth) && (dispy == 0)) { // 右边的格子 swapimg(img, imageViews[m]); if (issucc(imageViews)) { Alert alert = new Alert(AlertType.WARNING, "恭喜你,拼图成功!"); alert.show(); } } else if ((dispx == 0) && (dispy == partImagewidth)) { // 下面的格子 swapimg(img, imageViews[m]); if (issucc(imageViews)) { Alert alert = new Alert(AlertType.WARNING, "恭喜你,拼图成功!"); alert.show(); } } } // 交换两个imageView的实现 public void swapimg(ImageView i1, ImageView i2) { int row1 = GridPane.getRowIndex(i1); int colu1 = GridPane.getColumnIndex(i1); int row2 = GridPane.getRowIndex(i2); int colu2 = GridPane.getColumnIndex(i2); GridPane.setRowIndex(i1, row2); GridPane.setColumnIndex(i1, colu2); GridPane.setRowIndex(i2, row1); GridPane.setColumnIndex(i2, colu1); } } // 键盘操作的实现 class myKeyEvent implements EventHandler<KeyEvent> { @Override public void handle(KeyEvent arg0) { myevent myevent1 = new myevent(); int mrow = GridPane.getRowIndex(imageViews[m]); int mcol = GridPane.getColumnIndex(imageViews[m]); int mindex = mrow * 3 + mcol; // 针对不同的按键给出不同的反应 switch (arg0.getCode()) { case DOWN: // 计算出需要交换的对象图片的位置nindex int nrow = mrow + 1; int ncol = mcol; int nindex = nrow * 3 + ncol; if (nindex >= 0 && nindex <= 8) { // 由于n[]中的索引是和位置一一对应的,再将其值作为imageViews的索引 // 只要算出nindex,就能对应上相应的图片。将其交换 // ncopy拓展了n,将m作为第九个元素加入数组,否则就访问不到最后一张图片。 myevent1.swapimg(imageViews[m], imageViews[ncopy[nindex]]); // 图片交换后,将数组值也交换,否则多次操作之后数据会混乱,导致胡乱交换。 int temp2 = ncopy[mindex]; ncopy[mindex] = ncopy[nindex]; ncopy[nindex] = temp2; } break;case UP: nrow = mrow - 1; ncol = mcol; nindex = nrow * 3 + ncol; if (nindex >= 0 && nindex <= 8) { myevent1.swapimg(imageViews[m], imageViews[ncopy[nindex]]); // 图片交换后,将第一行中的值也交换,否则多次操作之后数据会混乱,导致胡乱交换。第二行不变(代表地址)。 int temp2 = ncopy[mindex]; ncopy[mindex] = ncopy[nindex]; ncopy[nindex] = temp2; } break; case LEFT: nrow = mrow; ncol = mcol - 1; nindex = nrow * 3 + ncol; if (nindex >= 0 && nindex <= 8 && nindex != 2 && nindex != 5) { myevent1.swapimg(imageViews[m], imageViews[ncopy[nindex]]); // 图片交换后,将第一行中的值也交换,否则多次操作之后数据会混乱,导致胡乱交换。第二行不变(代表地址)。 int temp2 = ncopy[mindex]; ncopy[mindex] = ncopy[nindex]; ncopy[nindex] = temp2; } break; case RIGHT: nrow = mrow; ncol = mcol + 1; nindex = nrow * 3 + ncol; if (nindex >= 0 && nindex <= 8 && nindex != 3 && nindex != 6) { myevent1.swapimg(imageViews[m], imageViews[ncopy[nindex]]); // 图片交换后,将第一行中的值也交换,否则多次操作之后数据会混乱,导致胡乱交换。第二行不变(代表地址)。 int temp2 = ncopy[mindex]; ncopy[mindex] = ncopy[nindex]; ncopy[nindex] = temp2; } break; default: break; } } } // 判断是否拼图成功 public boolean issucc(ImageView[] imageViews) { for (int i = 0; i <= 8; ++i) { if (i != 3 * GridPane.getRowIndex(imageViews[i]) + GridPane.getColumnIndex(imageViews[i])) { return false; } } return true; } // 找出m public int findnum(int[] n) { for (int j = 0; j <= 8; ++j) { if ((j == n[0]) || (j == n[1]) || (j == n[2]) || (j == n[3]) || (j == n[4]) || (j == n[5]) || (j == n[6]) || (j == n[7])) { } else { return j; } } return -1; } }
- Start:
package application; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.text.Font; /** * 开始菜单主界面 作者:Mr. Yu 2018年12月5日,下午5:40:51 */ public class Start extends Application { @Override public void start(Stage primaryStage) { try { // 父容器 VBox vBox = new VBox(); // 子容器,分别是按钮和输出文本 HBox hBox_1 = new HBox(); HBox hBox_2 = new HBox(); // 显示难度选择按钮 Button start_1 = new Button("简单"); start_1.setOnAction(e -> { Game_4 game_4 = new Game_4(); game_4.start(new Stage()); // Game_1 game_1 = new Game_1(); // game_1.start(new Stage()); // 关闭开始界面 primaryStage.close(); }); Button start_2 = new Button("困难"); start_2.setOnAction(e -> { // 打开另一个窗口,即游戏窗口 Game_2 game_2 = new Game_2(); game_2.start(new Stage()); // 关闭开始界面 primaryStage.close(); }); Button start_3 = new Button("地狱"); start_3.setOnAction(e -> { // 打开另一个窗口,即游戏窗口 Game_3 game_3 = new Game_3(); game_3.start(new Stage()); // 关闭开始界面 primaryStage.close(); }); Label label = new Label("@余氏出品,必属渣品"); // 设置文本样式大小和颜色 label.setFont(new Font("Arial", 30)); label.setTextFill(Color.web("black")); hBox_1.getChildren().addAll(start_1, start_2, start_3); hBox_2.getChildren().add(label); vBox.getChildren().addAll(hBox_1, hBox_2); // 设置按钮大小并调节位置 start_1.setPrefSize(100, 60); start_2.setPrefSize(100, 60); start_3.setPrefSize(100, 60); hBox_1.setPadding(new Insets(650, 100, 0, 175)); hBox_2.setPadding(new Insets(50, 50, 500, 260)); // 设置控件之间的距离 hBox_1.setSpacing(100); // 标题栏图标 // Image image = new Image("application/4.jpg"); Scene scene = new Scene(vBox, 800, 800); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); // primaryStage.getIcons().add(image); primaryStage.setTitle("智障拼图"); primaryStage.setScene(scene); primaryStage.setResizable(false); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } }
相关文章推荐
- 使用JavaFx实现拼图游戏
- 使用UI Automation实现自动化测试--7.2 (模拟键盘复杂操作在自动化测试中的应用)
- 使用jsonp跨域调用百度js实现搜索框智能提示,并实现鼠标和键盘对弹出框里候选词的操作【附源码和在线测试地址】
- 使用jsonp跨域调用百度js实现搜索框智能提示,并实现鼠标和键盘对弹出框里候选词的操作【附源码和在线测试地址】
- 使用UI Automation实现自动化测试--7.2 (模拟键盘复杂操作在自动化测试中的应用)
- c++使用sendinput函数实现模拟键盘按键操作
- 使用jsonp跨域调用百度js实现搜索框智能提示,并实现鼠标和键盘对弹出框里候选词的操作【附源码和在线测试地址】
- 使用jsonp跨域调用百度js实现搜索框智能提示,并实现鼠标和键盘对弹出框里候选词的操作【附源码】
- Python使用pyautogui模块实现自动化鼠标和键盘操作示例
- 使用XML封装数据库操作语句的实现(zz)
- Flex与.NET互操作(五):使用FileReference+HttpHandler实现文件上传/下载
- .NET1.1下,使用C#自动生成Word2003文档(通过操作COM组件实现)
- 使用JDBC的CachedRowSet实现将数据源中的数据读取到内存中进行离线操作
- 实现使用后台操作的窗体
- .NET1.1下,使用C#自动生成Word2003文档(通过操作COM组件实现)
- 如何使用c语言实现双向链表的插入删除操作
- 两种使用Spring JdbcTemplate实现update或insert操作
- 使用new实现realloc操作
- Flex与.NET互操作(十五):使用FluorineFx中的字节数组(ByteArray)实现图片上传
- Flex与.NET互操作(八):使用FluorineFx网关实现远程访问