玩游戏学编程(1)-猜数字UI

游戏需要图形界面,虽然互联网时代Java FX 使用频率很小, 但是作为开发者,技多不压身,况且UI源码中有很多经典设计,是各类设计模式聚集地,可以让开发者加深对OOP、设计模式的理解。 如果把UI界面运用到游戏中去,既学到知识又增加趣味,岂不乐哉。

数字游戏界面

一、问题
  1. 怎样使用JavaFX创建一个UI界面?
  2. 怎样创建一个按钮并添加到UI界面中?
  3. 怎样监听一个按钮的事件?
  4. 怎样使用线程开启一个异步任务?
  5. 在异步任务中绘图并添加到UI窗口
二、解决问题
  1. 要编写一个JavaFX界面,需要创建一个类并继承javafx.application.Application类(Java8中可以直接使用,更高版本的Java引入了模块化,需要手动引入), 并重写start方法:
  2. 代码中的Stage参数可以认为是一个UI窗口,英文翻译成“舞台”, 调用它的show方法显示这个窗口
1
2
3
4
5
6
7
8
9
10
package top.coolcode;
import javafx.application.Application;
public class GuessApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
//显示“舞台”窗口
primaryStage.show();
}
}


  1. 窗口创建好之后需要创建一些内容放到这个窗口中,这个内容我们称为Scene , 英文翻译成场景
  2. 可以独立创建一个函数,这个函数用来创建窗口界面中的内容
  3. 界面元素包含一个按钮Button, 并为这个按钮添加了一个事件处理函数,代码中使用了箭头函数
  4. 设置对象的属性通常会使用set开头的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package top.coolcode;
import javafx.application.Application;
public class GuessApp extends Application {
//垂直盒子布局容器
private VBox root;
//游戏中的地砖面板
private Pane tilePane;
//使用Executors创建线程
private ScheduledExecutorService servicePool = Executors.newSingleThreadScheduledExecutor();
//存储所有数字框 的集合
private List<TileView> tileSequence = new ArrayList<>();

@Override
public void start(Stage primaryStage) throws Exception {
//createContent()函数将会独立出来用于创建内容
Scene scene = new Scene(this.createContent());
//显示“舞台”窗口
primaryStage.show();
}

private Parent createContent() {
//使用垂直盒子进行布局,界面元素将会垂直放置在这个盒子容器中
// root是定义在类上的属性
this.root = new VBox();
//设置布局容器大小(宽,高)
root.setPrefSize(1024, 500);
//创建按钮对象
Button button = new Button("开始");
//为按钮添加一个事件处理函数startGame(),也就是说当按下按钮后,这个函数就会被调用
button.setOnAction(e->startGame());
//创建一个面板
Pane pane = new Pane();
//获取这个垂直盒子容器并将面板和按钮添加到容器中
root.getChildren().addAll(pane, button);
//返回这个垂直盒子容器,返回的内容将会被添加到舞台Stage
return root;
}
}


  1. 编写按钮事件处理函数
  2. 在函数中会调用随机产生数字方格的函数populateGrid(),并将此函数返回的内容添加到VBox根容器
  3. 此函数中有一个计划任务,实际上使用的是Java中的线程池,同时还使用到了Java8中的Stream编程
1
2
3
4
5
6
7
8
9
private void startGame() {
this.tilePane = this.populateGrid();
this.root.getChildren().set(0, tilePane);
//6秒后执行线程,计划任务
this.servicePool.schedule(()->{
tilePane.getChildren().stream().map(n->(TileView)n)
.forEach(TileView::hide);//此处是遍历tilePane面板上的每一个元素,并调用它的hide方法
}, 6, TimeUnit.SECONDS);
}

  1. populateGrid函数实现, 用于随机绘制数字框,并添加到UI界面中
  2. 此函数的逻辑稍显复杂,主要使用到了Point2D类,产生9个数字框,并需要判断每个数字框不重合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
private Pane populateGrid() {
Pane pane = new Pane();
pane.setPrefSize(1024, 500);
Random random = new Random();
List<Point2D> usedPoints = new ArrayList<>();
for (int i = 0; i <= 9; i++) {

int randomX = random.nextInt(1024/80);
int randomY = random.nextInt(500/80);
Point2D p = new Point2D(randomX, randomY);
//判断集合usedPoints中是否包含了p,如果包含了则重新创建,并添加到usedPoints集合中
while(usedPoints.contains(p)) {
randomX = random.nextInt(1024/80);
randomY = random.nextInt(500/80);
p = new Point2D(randomX,randomY);
}
usedPoints.add(p);
//此类是自建类,用于创建一个数字框
TileView tile = new TileView(Integer.toString(i));

tile.setTranslateX(randomX * 80);
tile.setTranslateY(randomY * 80);
tile.setOnMouseClicked(e->{
if(tileSequence.isEmpty()) {
System.out.println("游戏已结束");
return;
}
TileView correctTile = tileSequence.remove(0);
if(tile == correctTile) {
tile.show();
}
else {
tileSequence.clear();
System.out.println("失败:游戏结束");
}
});

pane.getChildren().add(tile);
tileSequence.add(tile);

}

return pane;
}
private static class TileView extends StackPane {
private Text text;
TileView(String content) {
Rectangle border = new Rectangle(80,80,null);
border.setStroke(Color.BLUEVIOLET);
border.setStrokeWidth(4);
border.setStrokeType(StrokeType.INSIDE);

text = new Text(content);
text.setFont(Font.font(64));
getChildren().addAll(border,text);
setPickOnBounds(true);
}
void hide() {
text.setVisible(false);
}
void show() {
text.setVisible(true);
}
}
  1. 最后运行程序
1
2
3
public static void main(String[] args) {
launch(args);
}

此程序融合了线程、集合、FX UI界面、事件、Stream, 亦有很多英文单词