简介
计算机是门实践性很强的学科,光看没用,一定要动手。比如你看了javaFX的教程,感觉啥都会了,然后一跑代码全是错,一做项目发现这个没学到,那个没人教。所以书和文档只是给你个方向指引,真正掌握一门技术,就是动手,干就完了。
2048是一个很简单的小游戏,整体上分成两部分,一部分是人机交互,另一部分是游戏逻辑控制。这两部分整合到一起就是个完整的游戏。人机交互部分就是画个图面(ui),接受用户输入,比如鼠标/键盘,用户输入后触发后台数据的变化,通过游戏逻辑返回相应的结果,再通过屏幕反馈给玩家。2048的游戏逻辑就是横纵方向的数字以2指数进行合并,这里边需要进行数字比较,相同的才能合并,不同的不能合并。
以下是2048的DEMO截图,这DEMO并不是完整的游戏。


前端实现
先是实现4*4的圆角矩形阵列,方框用Rectagle控件,数字用Label控件有数字显示,没数字不显示:
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
double x = j * tile_width + (j + 1) * BLANK_COL_WIDTH;
double y = i * tile_height + (i + 1) * BLANK_ROW_HEIGHT;
double radius = (canvas_height > tile_width) ? (canvas_height / 20.0) : (tile_width / 20.0);
Rectangle rect = new Rectangle(x, y, tile_width, tile_height);
rect.setArcHeight(radius);
rect.setArcWidth(radius);
rect.setFill(Color.WHITE);
backgroudPane.getChildren().add(rect);
Label number = new Label();
number.setFont(Font.font("Sans", FontWeight.BOLD, tile_height / 2));
number.setAlignment(Pos.CENTER);
number.setBackground(Background.EMPTY);
number.setTextFill(Color.WHITE);
number.setPrefWidth(tile_width);
number.setPrefHeight(tile_height);
number.setLayoutX(x);
number.setLayoutY(y);
backgroudPane.getChildren().add(number);
tileHolder[i][j] = new TileView();
tileHolder[i][j].number = number;
tileHolder[i][j].backgroud = rect;
tileHolder[i][j].val = 0;
}
}
用户响应
用户输入只有上下左右,这里用键盘的wasd四个键控制方向。
backgroudPane.getParent().setOnKeyPressed((event) -> {
System.out.println(event.getCode());
switch (event.getCode()) {
case W:
case UP:
move(MoveRequest.UP);
break;
case A:
case LEFT:
move(MoveRequest.LEFT);
break;
case S:
case DOWN:
move(MoveRequest.DOWN);
break;
case D:
case RIGHT:
move(MoveRequest.RIGHT);
break;
default:
break;
}
refresh();
});
逻辑控制
比如向上移动,向上的纵向的变化,同列合并:
private int move_down(int cols, int rows, int max_changed) {
int m = 0;
var grid = cellValues;
for (short i = 0; i < cols; i++) {
GridPosition free = new GridPosition();// { rows, i };
free.row = rows;
free.col = i;
for (int j = 0; j < rows; j++) {
//从上往下
int row = rows - j - 1;
GridPosition cur = new GridPosition();// { row, i };
cur.col = i;
cur.row = row;
int val = grid[cur.row][cur.col];
//0表示没有数字,跳过
if (val == 0) {
if (free.row == rows)
free.row = row;
continue;
}
// 查找前一个相同的数字
GridPosition match = new GridPosition();// { 0, 0 };
boolean has_match = false;
for (int k = row - 1; k >= 0; k--) {
int k_val = grid[k][cur.col];
if (k_val != 0) {
if (k_val == val) {
has_match = true;
// match = { k, cur.col };
match.row = k;
match.col = cur.col;
}
break;
}
}
//找到后进行合并
if (has_match) {
if (free.row == rows)
free.row = row; // temporarily
m++;
move_to_match(cur, free, match, val, max_changed);
free.row--;
} else if (free.row != rows) {
//没找到移到最上边。
move_to_end(cur, free, val);
m++;
free.row--;
}
}
}
return m;
}
界面刷新
后台数据变化要体验在前端界面上:
private void refresh() {
int rows = ROWS;
int cols = COLS;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
tileHolder[i][j].val = data.getValue(i, j);
if (tileHolder[i][j].val == 0) {
tileHolder[i][j].number.setText("");
} else {
tileHolder[i][j].number.setText("" + (int) Math.pow(2, tileHolder[i][j].val));
}
tileHolder[i][j].backgroud.setFill(colors.get(tileHolder[i][j].val));
}
}
}
总结
此版本实现比较简单,玩家每次按一下键盘,然后就会得到一个结果,在合适的机会把数据刷出来,这里第次按键之后,进行一次刷新。因为游戏变化是跟按键一一对应的,所以这么实现没啥问题,只是效果差一点,如次想效果好一点,可以加上动画,让用户感受到方块的移动,在动画结束的时候再刷新界面,其结果是一样的,但有可能鼠标按太快没反应,因为动画还没结束。

1826

被折叠的 条评论
为什么被折叠?



