stm32f103ZET6实现2048小游戏(LVGL)保姆级教程(二)(核心思路)
2048是一款数字益智游戏,玩家需要通过滑动方块来合并相同数字的方块,最终得到数字2048的方块。下面是2048小游戏的核心思路:
概要:
-
游戏面板:2048游戏面板是一个4x4的方格网格,初始状态有两个随机生成的数字方块,每个方块上有数字2或4。
-
移动方块:玩家可以通过滑动方向键或触摸屏来控制方块的移动方向。每一次移动操作,所有的方块都会在所选择的方向上移动尽可能远的距离,直到遇到其他方块或边界。
-
合并方块:当两个相同数字的方块在移动过程中碰撞时,它们会合并为一个新的方块,数字是原来两个方块数字的和。
-
生成新方块:每次移动之后,游戏会生成一个新的数字方块,随机出现在空白格子中。新生成的数字方块只可能是2或4。
-
游戏结束判断:当所有的格子都被填满,且相邻的方块不能合并时,游戏结束。
-
得分计算:每次合并方块时,玩家会获得对应合并数字的分数。最终得分是所有合并操作得分的总和。
-
游戏目标:玩家的目标是合并数字方块,尽可能得到一个2048数字方块。
核心思路
1.游戏面板
2048游戏面板是一个4x4的方格网格,初始状态有两个随机生成的数字方块,每个方块上有数字2或4。
这里可以使用一个二维数组存储1~12的数字,用switch语句可代表不同的方块,用for循环双重嵌套输出
void show_mb(lv_obj_t* bg) {
uint8_t margin_x = 0, margin_y = 0;
for (uint8_t i = 0; i < WIDTH; i++) {
margin_x = 0;
for (uint8_t j = 0; j < WIDTH; j++) {
//printf("%d ", sz[i][j]);
show_color_block(sz[i][j], margin_x + j * BLOCKH, margin_y + i * BLOCKH, bg, i, j);
margin_x += 5;
}
margin_y += 5;
//printf("\n");
}
}
switch (n) {
case 0:
lv_obj_set_style_bg_color(block, lv_color_hex(0xc6b8ab), LV_STATE_DEFAULT); break;
case 1:
lv_obj_set_style_bg_color(block, lv_color_hex(0xece2d8), LV_STATE_DEFAULT); break;
case 2:
lv_obj_set_style_bg_color(block, lv_color_hex(0xeadec6), LV_STATE_DEFAULT); break;
case 3:
lv_obj_set_style_bg_color(block, lv_color_hex(0xf0af76), LV_STATE_DEFAULT); break;
case 4:
lv_obj_set_style_bg_color(block, lv_color_hex(0xec8d54), LV_STATE_DEFAULT); break;
case 5:
lv_obj_set_style_bg_color(block, lv_color_hex(0xf67c5f), LV_STATE_DEFAULT); break;
case 6:
lv_obj_set_style_bg_color(block, lv_color_hex(0xea5937), LV_STATE_DEFAULT); break;
case 7:
lv_obj_set_style_bg_color(block, lv_color_hex(0xf2d76b), LV_STATE_DEFAULT); break;
case 8:
lv_obj_set_style_bg_color(block, lv_color_hex(0xf1d04b), LV_STATE_DEFAULT); break;
case 9:
lv_obj_set_style_bg_color(block, lv_color_hex(0xe4c02a), LV_STATE_DEFAULT); break;
case 10:
lv_obj_set_style_bg_color(block, lv_color_hex(0xdcb82a), LV_STATE_DEFAULT); break;
case 11:
lv_obj_set_style_bg_color(block, lv_color_hex(0xd4b02a), LV_STATE_DEFAULT); break;
}
部分代码实例。
2.移动方块与方块合并
采用lvgl提供的手势函数判定滑动
lv_obj_add_event_cb(lv_scr_act(), event_cb, LV_EVENT_GESTURE, bg);
添加事件
if (code == LV_EVENT_GESTURE) {
lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_get_act()); //手势判定
if (dir == LV_DIR_TOP) {
bg_hd(UP, bg); //向上滑
}
else if (dir == LV_DIR_BOTTOM) {
bg_hd(DOWN, bg); //向下滑
}
else if (dir == LV_DIR_LEFT) {
bg_hd(LEFT, bg); //向左滑
}
else if (dir == LV_DIR_RIGHT) {
bg_hd(RIGHT, bg); //向右滑
}
}
在事件函数中判定手势,再调用不同方向的方块移动函数
方块移动的方式:
不同方向我们采用类似的扫描方法,这里我们以向上移动为例。
我们从第二行第一列开始扫描,将其元素往第一行移动,如果第一行有元素,则判断是否相同,相同的话就将第一行元素×2,并将第二行元素赋值为0,不相同则位置不动;如果没元素则将第二行赋值给第一行,第二行赋值为0。 (空格默认为0)
嵌套循环一遍遍向下循环,下面为部分代码。
uint8_t flag, flag_t = 0; //不连续合并
if (toward == UP) {
for (short j = 0; j < WIDTH; j++) {
flag = 1;
for (short i = 1; i < WIDTH; i++) {
for (short z = i; z > 0; z--) {
if (sz[z - 1][j] == sz[z][j] && sz[z][j] != 0 && flag) {
sz[z - 1][j] += 1;
cnt--;
score += (uint16_t)pow(2, sz[z - 1][j]);
sz[z][j] = 0;
flag = 0;
flag_t = 1;
if (flag_t != 1)flag_t = 1;
}
else if (sz[z - 1][j] == 0 && sz[z][j] != 0) {
sz[z - 1][j] = sz[z][j];
sz[z][j] = 0;
if (flag_t != 1)flag_t = 1;
}
}
}
}
}
3.产生新方块
这里要用到随机数函数,stm32产生随机数方法有ADC采集法(即ADC采集随机电压), 定时器计数生成法(即利用定时器进行计数,调用函数采集计数)等。这里我们采用定时器计数法。
TIM5溢出中断中添加ms计数,让其在1~2^32之间震荡。
随机生成方块。
4.判断游戏结束
当所有的格子都被填满,且相邻的方块不能合并时,游戏结束。
首先第一层判定是格子全满,格子全满会调用相邻方块调用函数。这里我们用cnt计数,方块增加时:cnt+1,方块减少是cnt-=1。最后当cnt==WIDTH*WIDTH时,则格子全满。
然后这里使用相邻方块是否相同判断函数:
bool detect_end() {
const short a[4][2] = { 0,1,0,-1,1,0,-1,0 };
for (short i = 0; i < WIDTH; i++) {
for (short j = 0; j < WIDTH; j++) {
for (short z = 0; z < 4; z++) {
if (i + a[z][0] < WIDTH && j + a[z][1] < WIDTH && i + a[z][0] >= 0 && j + a[z][1] >= 0) {
if (sz[i][j] == sz[i + a[z][0]][j + a[z][1]]) {
return 0;
}
}
}
}
}
return 1;
}
若都满足则调用结算函数(部分)
void end_ami(lv_obj_t* bg) {
lv_obj_add_flag(lv_layer_top(), LV_OBJ_FLAG_CLICKABLE);
end_main = lv_obj_create(lv_layer_top());
lv_obj_set_style_bg_opa(lv_layer_top(), LV_OPA_50, 0); // 设置对象透明度
lv_obj_set_style_bg_color(lv_layer_top(), lv_palette_main(LV_PALETTE_GREY), 0); //灰色背景
lv_obj_set_style_bg_color(end_main, lv_color_hex(0xb2a296), LV_STATE_DEFAULT);
lv_obj_set_size(end_main, 140, 150);
lv_obj_clear_flag(end_main, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_align(end_main, LV_ALIGN_CENTER);
lv_obj_t* score_l = lv_label_create(end_main);
lv_label_set_recolor(score_l, true);
lv_label_set_text(score_l, "#000000 SCORE#");
lv_obj_align(score_l, LV_ALIGN_TOP_MID, 0, 10);
lv_obj_t* score_s = lv_label_create(end_main);
static lv_style_t font_style1;
lv_style_init(&font_style1);
lv_label_set_recolor(score_s, true);
lv_label_set_text_fmt(score_s, "#000000 %d#",score);
lv_obj_add_style(score_s, &font_style1, LV_STATE_DEFAULT);
lv_obj_align(score_s, LV_ALIGN_BOTTOM_MID, 0, -70);
lv_obj_t* regame = lv_obj_create(end_main);
lv_obj_set_size(regame, 100, 40);
lv_obj_set_style_bg_color(regame,lv_color_hex(0x1E90FF), LV_STATE_DEFAULT);
lv_obj_align(regame,LV_ALIGN_BOTTOM_MID, 0, -10);
lv_obj_t* retry = lv_label_create(regame);
lv_label_set_recolor(retry, true);
lv_label_set_text(retry, "#000000 RETRY#");
lv_obj_clear_flag(regame, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_align(retry, LV_ALIGN_CENTER,0,0);
lv_obj_add_event_cb(regame, event_cb, LV_EVENT_CLICKED, bg);
}
5.得分计算
这里使用全局变量score记录得分,当方块合并时score+=方块数字。实时得分会在左上角显示,部分代码:
lv_obj_t* score_block = lv_obj_create(lv_scr_act());
lv_obj_set_size(score_block, 50, 50);
lv_obj_align(score_block,LV_ALIGN_TOP_LEFT,90,10);
lv_obj_set_style_bg_color(score_block,lv_color_hex(0xb8afa0), LV_STATE_DEFAULT);
lv_obj_t* score_l = lv_label_create(score_block);
static lv_style_t font_style;
lv_style_init(&font_style);
lv_style_set_text_font(&font_style, &lv_font_montserrat_12);
lv_obj_add_style(score_l, &font_style, LV_STATE_DEFAULT);
p_score = score_block;
lv_label_set_recolor(score_l, true);
lv_label_set_text(score_l, "#f0e4d8 SCORE#");
lv_obj_clear_flag(score_block, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_align(score_l, LV_ALIGN_TOP_MID, 0, -10);
lv_obj_t* score_s = lv_label_create(score_block);
static lv_style_t font_style1;
lv_style_init(&font_style1);
lv_style_set_text_font(&font_style1, &lv_font_montserrat_16);
lv_label_set_recolor(score_s, true);
lv_label_set_text_fmt(score_s, "#ffffff %d#",score);
lv_obj_add_style(score_s, &font_style1, LV_STATE_DEFAULT);
lv_obj_align(score_s, LV_ALIGN_BOTTOM_MID, 0, 5);
总结:
本章为核心思路以及部分代码实现,后续全部代码会发出,下篇文章会给出一些细节注意,以及避坑指南。
更多推荐
所有评论(0)