[LVGL] 中国象棋
0#. 基于 <<象棋巫师>> v2.1.2 有灵感,纯手工敲个中国象棋雏形
#1.用LVGL代码写了一个中国象棋的棋盘,代码量大概400行
#2.字体采用鸿蒙免费字库
#3.象棋规则还未添加. 测试屏幕窗口800x800
#4.chess_board.h
#ifndef CHESS_BOARD_H
#define CHESS_BOARD_H#include "lvgl/lvgl.h"// 棋盘尺寸
#define BOARD_ROWS 10
#define BOARD_COLS 9
// 棋盘底色
#define BOARD_GND_COLOR 0xE8C088// 红方底色
#define RED_CHESS_GND_COLOR 0xda8d54
// 红方字色
#define RED_CHESS_TXT_COLOR 0x9b1c03// 黑方底色
#define BLACK_CHESS_GND_COLOR 0xe4a543
// 黑方字色
#define BLACK_CHESS_TXT_COLOR 0x000000// 棋子未选中时的边框颜色
#define CHEESS_UNSELECTED_COLOR 0x84571a
// 棋子选中时的边框颜色
#define CHEESS_SELECTED_COLOR 0x22ff22// 是否允许棋盘坐标
#define ALLOW_BOARD_CORRDINATES 1// 棋子类型
typedef enum {PIECE_NONE = 0,PIECE_RED_GENERAL, // 帅PIECE_RED_ADVISOR, // 仕PIECE_RED_ELEPHANT, // 相PIECE_RED_HORSE, // 马PIECE_RED_CHARIOT, // 车PIECE_RED_CANNON, // 炮PIECE_RED_SOLDIER, // 兵PIECE_BLACK_GENERAL, // 将PIECE_BLACK_ADVISOR, // 士PIECE_BLACK_ELEPHANT, // 象PIECE_BLACK_HORSE, // 马PIECE_BLACK_CHARIOT, // 车PIECE_BLACK_CANNON, // 炮PIECE_BLACK_SOLDIER, // 卒
} piece_type_t;// 棋子所属方
typedef enum {SIDE_NONE = 0,SIDE_RED,SIDE_BLACK
} piece_side_t;// 棋子结构
typedef struct {piece_type_t type; // 棋子类型 (将,士,象,马,车,炮,兵)piece_side_t side; // 棋子所属方 (红方,黑方)lv_obj_t *obj; // LVGL对象
} chess_piece_t;// 棋盘初始化
void chess_board_init(lv_obj_t* parent);#endif // CHESS_BOARD_H
#5.chess_board.c
#include "chess_board.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>//使用鸿蒙字体 HarmonyOS_Sans_SC_Regular.ttf 生成字库数组
/*
* Bpp : 2
* Opts : --bpp 2 --size 18 --no - compress --stride 1 --align 1 --font HarmonyOS_Sans_SC_Regular.ttf --symbols 红方:帅、仕、相、马、车、炮、兵
黑方:将、士、象、马、车、炮、卒
中国象棋, 楚河汉界, 重新开始, 胜利, 走棋 - 将军
一二三四五六七八九 --range 32 - 127 --format lvgl - o lv_font_hongmeng_18.c
*/
extern const lv_font_t lv_font_hongmeng_18;
extern const lv_font_t lv_font_hongmeng_22;
extern const lv_font_t lv_font_hongmeng_24;// 棋盘尺寸常量
#define GRID_SIZE 60 // 格子大小 50px
#define PIECE_SIZE 50 // 棋子大小 40px
#define BOARD_PADDING 40 // 棋盘边距 30px//哨岗原形尺寸大小
#define SAO_GANGSIZE 10// 棋盘状态
chess_piece_t board[BOARD_ROWS][BOARD_COLS];
static lv_obj_t* chess_board_obj;
static lv_obj_t* status_label;
static lv_obj_t* selected_piece_obj = NULL;// 棋子的中文名称
static const char* piece_names[] = {"", // PIECE_NONE"帅", // PIECE_RED_GENERAL"仕", // PIECE_RED_ADVISOR"相", // PIECE_RED_ELEPHANT"马", // PIECE_RED_HORSE"车", // PIECE_RED_CHARIOT"炮", // PIECE_RED_CANNON"兵", // PIECE_RED_SOLDIER"将", // PIECE_BLACK_GENERAL"士", // PIECE_BLACK_ADVISOR"象", // PIECE_BLACK_ELEPHANT"马", // PIECE_BLACK_HORSE"车", // PIECE_BLACK_CHARIOT"炮", // PIECE_BLACK_CANNON"卒", // PIECE_BLACK_SOLDIER
};// 前向声明
static void create_pieces(void);
// 棋盘初始化//水平,垂直线段控件,可以使用空白的object控件修改属性来模拟线段
lv_obj_t* view_hor_line_create(lv_obj_t* parent, int x0, int y0, uint32_t line_length, uint32_t line_color, uint32_t line_width)
{lv_obj_t* line_obj = lv_obj_create(parent);lv_obj_clear_flag(line_obj, LV_OBJ_FLAG_SCROLLABLE);lv_obj_set_size(line_obj, line_length, line_width);lv_obj_set_style_bg_color(line_obj, lv_color_hex(line_color), 0);lv_obj_set_pos(line_obj, x0, y0);lv_obj_set_style_border_width(line_obj, 0, 0);return line_obj;
}void set_circle_position(lv_obj_t* circle_obj, uint8_t x, uint8_t y)
{lv_obj_set_pos(circle_obj, BOARD_PADDING + x * GRID_SIZE - SAO_GANGSIZE / 2, BOARD_PADDING + y * GRID_SIZE - SAO_GANGSIZE / 2);
}void board_event_cb(lv_event_t* ev)
{lv_event_code_t code = lv_event_get_code(ev);lv_obj_t* chess_btn = lv_event_get_target(ev);if (code == LV_EVENT_CLICKED) {lv_obj_t* chess_chose_btn = NULL;int row_id = 0;int col_id = 0;for (int row = 0; row < BOARD_ROWS; row++) {for (int col = 0; col < BOARD_COLS; col++) {if (board[row][col].obj != NULL) {lv_obj_set_style_border_color(board[row][col].obj, lv_color_hex(CHEESS_UNSELECTED_COLOR), 0);}if (board[row][col].obj == chess_btn){row_id = row;col_id = col;chess_chose_btn = board[row][col].obj;}}}if (chess_chose_btn != NULL){printf("[Click] Side:%d,Type:%d,x_y:{%d,%d}\n", board[row_id][col_id].side, board[row_id][col_id].type,row_id,col_id);lv_obj_set_style_border_color(chess_btn, lv_color_hex(CHEESS_SELECTED_COLOR), 0);}else{ // 点击棋盘空白处printf("[Click] None\n");}}
}
void chess_board_init(lv_obj_t* parent)
{// 创建棋盘容器chess_board_obj = lv_obj_create(parent);lv_obj_set_size(chess_board_obj, BOARD_COLS * GRID_SIZE + 2 * BOARD_PADDING,BOARD_ROWS * GRID_SIZE + 2 * BOARD_PADDING);lv_obj_align(chess_board_obj, LV_ALIGN_CENTER, 0, 0);lv_obj_set_style_bg_color(chess_board_obj, lv_color_hex(BOARD_GND_COLOR), 0); // 棋盘底色lv_obj_add_event_cb(chess_board_obj, board_event_cb, LV_EVENT_CLICKED, NULL);lv_obj_clear_flag(chess_board_obj, LV_OBJ_FLAG_SCROLLABLE);// 绘制棋盘网格线for (int i = 0; i < BOARD_ROWS; i++) {// x轴水平线int line_pixel = 1;if (i == 0 || i == BOARD_ROWS - 1) {line_pixel = 2;}view_hor_line_create(chess_board_obj, BOARD_PADDING, BOARD_PADDING + i * GRID_SIZE, (BOARD_COLS - 1) * GRID_SIZE, 0x222222, line_pixel);}for (int i = 0; i < BOARD_COLS; i++) {// y轴垂直线int line_pixel = 1;if (i == 0 || i == BOARD_COLS - 1) {line_pixel = 2;}view_hor_line_create(chess_board_obj, BOARD_PADDING + i * GRID_SIZE, BOARD_PADDING, line_pixel, 0x222222, (BOARD_ROWS - 1) * GRID_SIZE);}//画九宫格的对边线static lv_point_t jgg_line_points_a[4];static lv_point_t jgg_line_points_b[4];lv_obj_t* jgg_line_obj = lv_line_create(chess_board_obj);jgg_line_points_a[0].x = BOARD_PADDING + 3 * GRID_SIZE;jgg_line_points_a[0].y = BOARD_PADDING;jgg_line_points_a[1].x = BOARD_PADDING + 5 * GRID_SIZE;jgg_line_points_a[1].y = BOARD_PADDING + 2 * GRID_SIZE;jgg_line_points_a[2].x = BOARD_PADDING + 5 * GRID_SIZE;jgg_line_points_a[2].y = BOARD_PADDING;jgg_line_points_a[3].x = BOARD_PADDING + 3 * GRID_SIZE;jgg_line_points_a[3].y = BOARD_PADDING + 2 * GRID_SIZE;lv_line_set_points(jgg_line_obj, jgg_line_points_a, 4);lv_obj_set_style_line_width(jgg_line_obj, 1, 0);jgg_line_obj = lv_line_create(chess_board_obj);jgg_line_points_b[0].x = BOARD_PADDING + 3 * GRID_SIZE;jgg_line_points_b[0].y = BOARD_PADDING + 7 * GRID_SIZE;jgg_line_points_b[1].x = BOARD_PADDING + 5 * GRID_SIZE;jgg_line_points_b[1].y = BOARD_PADDING + 9 * GRID_SIZE;jgg_line_points_b[2].x = BOARD_PADDING + 5 * GRID_SIZE;jgg_line_points_b[2].y = BOARD_PADDING + 7 * GRID_SIZE;jgg_line_points_b[3].x = BOARD_PADDING + 3 * GRID_SIZE;jgg_line_points_b[3].y = BOARD_PADDING + 9 * GRID_SIZE;lv_line_set_points(jgg_line_obj, jgg_line_points_b, 4);lv_obj_set_style_line_width(jgg_line_obj, 1, 0);lv_obj_set_style_line_width(jgg_line_obj, 1, 0);// 绘制楚河-汉界lv_obj_t* river_mask = lv_obj_create(chess_board_obj);lv_obj_set_size(river_mask, (BOARD_COLS - 1) * GRID_SIZE-10 , GRID_SIZE-10);lv_obj_set_pos(river_mask, BOARD_PADDING+5, 4 * GRID_SIZE+ BOARD_PADDING+5);lv_obj_set_style_bg_color(river_mask, lv_color_hex(BOARD_GND_COLOR), 0);lv_obj_set_style_border_width(river_mask, 0, 0);//hide borderlv_obj_clear_flag(river_mask, LV_OBJ_FLAG_SCROLLABLE);lv_obj_t* river_label1 = lv_label_create(river_mask);lv_label_set_text(river_label1, "楚河");lv_obj_align(river_label1, LV_ALIGN_LEFT_MID, GRID_SIZE, 0);lv_obj_set_style_text_font(river_label1, &lv_font_hongmeng_24, 0);lv_obj_t* river_label2 = lv_label_create(river_mask);lv_label_set_text(river_label2, "汉界");lv_obj_align(river_label2, LV_ALIGN_RIGHT_MID, -GRID_SIZE, 0);lv_obj_set_style_text_font(river_label2, &lv_font_hongmeng_24, 0);//画哨岗const uint8_t sao_gang_pos[14][2] ={ {1, 2}, {7, 2}, {0, 3}, {2, 3},{4, 3},{6, 3},{8,3},{1, 7}, {7, 7}, {0, 6}, {2, 6},{4, 6},{6, 6},{8,6}};for (int i = 0; i < 14; i++){lv_obj_t* circle = lv_obj_create(chess_board_obj);lv_obj_clear_flag(circle, LV_OBJ_FLAG_SCROLLABLE);lv_obj_set_size(circle, SAO_GANGSIZE, SAO_GANGSIZE);lv_obj_set_style_radius(circle, LV_RADIUS_CIRCLE, LV_PART_MAIN);lv_obj_set_style_bg_opa(circle, LV_OPA_TRANSP, LV_PART_MAIN);lv_obj_set_style_border_color(circle, lv_color_hex(0x000000), LV_PART_MAIN);lv_obj_set_style_border_width(circle, 1, LV_PART_MAIN);set_circle_position(circle, sao_gang_pos[i][0], sao_gang_pos[i][1]); }//画坐标提示const char* coord_text[9] = { "九", "八", "七", "六", "五", "四", "三", "二", "一" };lv_obj_t* coord_label_a = NULL;lv_obj_t* coord_label_b = NULL;for (int i = 0; i < 9; i++){coord_label_a = lv_label_create(chess_board_obj);lv_obj_set_style_text_font(coord_label_a, &lv_font_hongmeng_18, 0);lv_obj_set_style_text_color(coord_label_a, lv_color_hex(0x000000), 0);lv_label_set_text_fmt(coord_label_a, "%d",i+1);lv_obj_align(coord_label_a, LV_ALIGN_TOP_LEFT, BOARD_PADDING-5 + i * GRID_SIZE, -BOARD_PADDING/2);coord_label_b = lv_label_create(chess_board_obj);lv_obj_set_style_text_font(coord_label_b, &lv_font_hongmeng_18, 0);lv_obj_set_style_text_color(coord_label_b, lv_color_hex(0x000000), 0);lv_label_set_text_fmt(coord_label_b, "%s", coord_text[i]);lv_obj_align(coord_label_b, LV_ALIGN_TOP_LEFT, BOARD_PADDING - 10 + i * GRID_SIZE, GRID_SIZE*10+ BOARD_PADDING/2);if (ALLOW_BOARD_CORRDINATES == 0){lv_obj_add_flag(coord_label_a, LV_OBJ_FLAG_HIDDEN);lv_obj_add_flag(coord_label_b, LV_OBJ_FLAG_HIDDEN);}}// 初始化棋盘状态并放置棋子memset(board, 0, sizeof(board));create_pieces();
}// 创建棋子
static void create_pieces(void)
{// 清除已有棋子for (int row = 0; row < BOARD_ROWS; row++) {for (int col = 0; col < BOARD_COLS; col++) {if (board[row][col].obj != NULL) {lv_obj_del(board[row][col].obj);board[row][col].obj = NULL;}board[row][col].type = PIECE_NONE;board[row][col].side = SIDE_NONE;}}// 初始化红方棋子// 车board[9][0].type = PIECE_RED_CHARIOT;board[9][8].type = PIECE_RED_CHARIOT;// 马board[9][1].type = PIECE_RED_HORSE;board[9][7].type = PIECE_RED_HORSE;// 相board[9][2].type = PIECE_RED_ELEPHANT;board[9][6].type = PIECE_RED_ELEPHANT;// 士board[9][3].type = PIECE_RED_ADVISOR;board[9][5].type = PIECE_RED_ADVISOR;// 帅board[9][4].type = PIECE_RED_GENERAL;// 炮board[7][1].type = PIECE_RED_CANNON;board[7][7].type = PIECE_RED_CANNON;// 兵board[6][0].type = PIECE_RED_SOLDIER;board[6][2].type = PIECE_RED_SOLDIER;board[6][4].type = PIECE_RED_SOLDIER;board[6][6].type = PIECE_RED_SOLDIER;board[6][8].type = PIECE_RED_SOLDIER;// 初始化黑方棋子// 车board[0][0].type = PIECE_BLACK_CHARIOT;board[0][8].type = PIECE_BLACK_CHARIOT;// 马board[0][1].type = PIECE_BLACK_HORSE;board[0][7].type = PIECE_BLACK_HORSE;// 象board[0][2].type = PIECE_BLACK_ELEPHANT;board[0][6].type = PIECE_BLACK_ELEPHANT;// 士board[0][3].type = PIECE_BLACK_ADVISOR;board[0][5].type = PIECE_BLACK_ADVISOR;// 将board[0][4].type = PIECE_BLACK_GENERAL;// 炮board[2][1].type = PIECE_BLACK_CANNON;board[2][7].type = PIECE_BLACK_CANNON;// 卒board[3][0].type = PIECE_BLACK_SOLDIER;board[3][2].type = PIECE_BLACK_SOLDIER;board[3][4].type = PIECE_BLACK_SOLDIER;board[3][6].type = PIECE_BLACK_SOLDIER;board[3][8].type = PIECE_BLACK_SOLDIER;// 设置棋子所属方for (int row = 0; row < BOARD_ROWS; row++) {for (int col = 0; col < BOARD_COLS; col++) {if (board[row][col].type >= PIECE_RED_GENERAL && board[row][col].type <= PIECE_RED_SOLDIER) {board[row][col].side = SIDE_RED;}else if (board[row][col].type >= PIECE_BLACK_GENERAL && board[row][col].type <= PIECE_BLACK_SOLDIER) {board[row][col].side = SIDE_BLACK;}// 如果有棋子,创建视觉对象if (board[row][col].type != PIECE_NONE) {board[row][col].obj = lv_obj_create(chess_board_obj);lv_obj_clear_flag(board[row][col].obj, LV_OBJ_FLAG_SCROLLABLE);lv_obj_set_size(board[row][col].obj, PIECE_SIZE, PIECE_SIZE);lv_obj_align(board[row][col].obj, LV_ALIGN_TOP_LEFT,BOARD_PADDING + col * GRID_SIZE - PIECE_SIZE / 2,BOARD_PADDING + row * GRID_SIZE - PIECE_SIZE / 2);// 设置棋子样式if (board[row][col].side == SIDE_RED) {lv_obj_set_style_bg_color(board[row][col].obj, lv_color_hex(RED_CHESS_GND_COLOR), 0);lv_obj_set_style_text_color(board[row][col].obj, lv_color_hex(RED_CHESS_TXT_COLOR), 0);}else {lv_obj_set_style_bg_color(board[row][col].obj, lv_color_hex(BLACK_CHESS_GND_COLOR), 0);lv_obj_set_style_text_color(board[row][col].obj, lv_color_hex(BLACK_CHESS_TXT_COLOR), 0);}//设置棋子边框颜色lv_obj_set_style_radius(board[row][col].obj, PIECE_SIZE / 2, 0);lv_obj_set_style_border_width(board[row][col].obj, 2, 0);lv_obj_set_style_border_color(board[row][col].obj, lv_color_hex(CHEESS_UNSELECTED_COLOR), 0);// 添加棋子文字lv_obj_t* piece_label = lv_label_create(board[row][col].obj);lv_label_set_text(piece_label, piece_names[board[row][col].type]);lv_obj_center(piece_label);lv_obj_set_style_text_font(piece_label, &lv_font_hongmeng_24, 0);// 设置棋子点击事件lv_obj_set_user_data(board[row][col].obj, &board[row][col]);lv_obj_add_event_cb(board[row][col].obj, board_event_cb,LV_EVENT_CLICKED, NULL);}}}
}