使用 LittlevGL 實現 2048 小遊戲
使用 LittlevGL 實現 2048 小遊戲
2048 這款益智小遊戲,遊戲的規則十分簡單,簡單易上手的數字小遊戲,但又十分虐心。曾經也是風靡一時。
現在我們在 ESP32 上自己動手實現 2048 這款小遊戲吧。
1 使用 LittlevGL
esp32-lvgl-gui 倉庫已經適配了 LittlevGL V5.3,並適配了幾款屏幕(ILI9341、ST7789、SSD1306、NT35510) 和 觸摸驅動(FT5X06、XPT2046)。
使用時,在 menuconfig
中選擇你所使用的 屏幕 和 觸摸驅動。
編譯代碼:
- 添加頭文件:
/* lvgl includes */
#include "iot_lvgl.h"
/* game include */
#include "game.h"
- 初始化 LittlevGL:
extern "C" void app_main()
{
/* Initialize LittlevGL */
lv_init();
/* Tick interface, Initialize a Timer for 1 ms period and in its interrupt call*/
// esp_register_freertos_tick_hook(lv_tick_task_callback);
lvgl_tick_timer = xTimerCreate(
"lv_tickinc_task",
1 / portTICK_PERIOD_MS, //period time
pdTRUE, //auto load
(void *)NULL, //timer parameter
lv_tick_task_callback); //timer callback
xTimerStart(lvgl_tick_timer, 0);
/* Display interface */
lvgl_lcd_display_init(); /*Initialize your display*/
/* Input device interface */
input_device = lvgl_indev_init(); /*Initialize your indev*/
lvgl_timer = xTimerCreate(
"lv_task",
10 / portTICK_PERIOD_MS, //period time
pdTRUE, //auto load
(void *)NULL, //timer parameter
lvgl_task_time_callback); //timer callback
xTimerStart(lvgl_timer, 0);
// 2048 game init
game_init(480);
// game logic handle task
xTaskCreate(
user_task, //Task Function
"user_task", //Task Name
1024*4, //Stack Depth
NULL, //Parameters
1, //Priority
NULL); //Task Handler
}
2 2048 遊戲邏輯
2.1 界面初始化
將遊戲相關界面初始化分爲以下幾步:
- Step 1: 畫遊戲背景
- Step 2: 畫遊戲分數顯示框
- Step 3: 畫遊戲網格
- Step 4: 畫遊戲網格中每個單元格的內容
2.2 滑動處理
2.2.1 判斷滑動方向
通過調用 LittlevGL 的 API 讀取觸摸狀態(擡起/按下、座標點),計算水平/豎直方向滑動的差值,判斷爲哪個方向上的滑動(上/下/左/右),執行相應的操作。
static lv_indev_drv_t input_device;
static void user_task(void *pvParameter)
{
bool pressing = false;
uint8_t value = 0;
lv_indev_data_t touchpad_data;
lv_point_t last_data;
int16_t x_diff, y_diff;
while (1)
{
vTaskDelay(50 / portTICK_PERIOD_MS);
input_device.read(&touchpad_data); // 讀取觸摸驅動的值
if (touchpad_data.state == LV_INDEV_STATE_REL) { // 當前爲 `擡起` 狀態
pressing = false; // 計算座標偏移量
x_diff = touchpad_data.point.x - last_data.x;
y_diff = touchpad_data.point.y - last_data.y;
if(fabs(x_diff) > SENSITIVE || fabs(y_diff) > SENSITIVE) { // 判斷滑動距離是否超過判斷閾值
if (fabs(x_diff) > fabs(y_diff)) { // 判斷是否爲水平滑動
if (x_diff > 0) { // 判單是否爲向右滑動
move_right();
} else { // 向左滑動
move_left();
}
} else { // 豎直方向滑動
if (y_diff > 0) { // 判單是否爲向下滑動
move_down();
} else { // 向上滑動
move_up();
}
}
}
last_data.x = touchpad_data.point.x;
last_data.y = touchpad_data.point.y;
} else if (touchpad_data.state == LV_INDEV_STATE_PR) { // 當前爲 `按下` 狀態
if (!pressing) { // 按下狀態,記錄初次按下的座標點
last_data.x = touchpad_data.point.x;
last_data.y = touchpad_data.point.y;
pressing = true;
}
}
}
}
2.2.2 滑動邏輯處理
- 判斷同一行/列滑動方向上是否存在相等的數值
- 相同的單元格,數值相加爲相鄰單元格中的後一個(滑動方向上)的數值
- 畫出總的得分
- 判斷遊戲是否結束
- 刷新界面上的單元格
static uint16_t num_matrix[5][5] = {0}; // 保存所有的單元格中的數值
static uint32_t score_num = 0; // 總得分
void move_up()
{
int i, j, k, t, nx = -1, ny = 0, nn = 0;
for (i = 1; i <= 4; ++i)
tmp[i] = 0;
for (j = 1; j <= 4; ++j)
{
k = 0;
for (i = 1; i <= 4; ++i)
{
if (num_matrix[i][j])
{
tmp[++k] = num_matrix[i][j];
num_matrix[i][j] = 0;
}
}
t = 1;
while (t <= k - 1)
{
if (tmp[t] == tmp[t + 1])
{
tmp[t] *= 2;
tmp[t + 1] = 0;
score_num += tmp[t];
if (nx == -1)
{
nx = t;
ny = j;
nn = tmp[t];
}
t += 2;
}
else
t++;
}
t = 1;
for (i = 1; i <= k; ++i)
if (tmp[i])
num_matrix[t++][j] = tmp[i];
}
// 畫總分
draw_score_num(score_num);
// 刷新網格中單元格的內容
gen_num();
}
參考鏈接
GitHub 源碼:esp32-lvgl-gui
Twitter 視頻:Twitter